Updated on 2026-08-14
This commit is contained in:
parent
6f64d550cb
commit
a692abaffb
11 changed files with 475 additions and 2 deletions
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.data.swap
|
||||
|
||||
import com.tangem.data.express.converter.ExpressErrorConverter
|
||||
import com.tangem.datasource.api.common.response.ApiResponseError
|
||||
import com.tangem.domain.express.models.ExpressError
|
||||
import com.tangem.domain.swap.SwapErrorResolver
|
||||
|
||||
internal class DefaultSwapErrorResolver(
|
||||
private val expressErrorConverter: ExpressErrorConverter,
|
||||
) : SwapErrorResolver {
|
||||
override fun resolve(throwable: Throwable): ExpressError {
|
||||
return when (throwable) {
|
||||
is ApiResponseError.HttpException -> {
|
||||
expressErrorConverter.convert(throwable.errorBody.orEmpty())
|
||||
}
|
||||
else -> ExpressError.UnknownError
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
package com.tangem.data.swap
|
||||
|
||||
import com.tangem.data.common.api.safeApiCall
|
||||
import com.tangem.data.swap.converter.TokenInfoConverter
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.express.TangemExpressApi
|
||||
import com.tangem.datasource.api.express.models.request.PairsRequestBody
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressUtils
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.domain.express.ExpressRepository
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.SwapRepositoryV2
|
||||
import com.tangem.domain.swap.models.SwapPairModel
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultSwapRepositoryV2 @Inject constructor(
|
||||
private val tangemExpressApi: TangemExpressApi,
|
||||
private val expressRepository: ExpressRepository,
|
||||
private val coroutineDispatcher: CoroutineDispatcherProvider,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val currencyStatusOperations: BaseCurrencyStatusOperations,
|
||||
) : SwapRepositoryV2 {
|
||||
|
||||
private val tokenInfoConverter = TokenInfoConverter()
|
||||
override suspend fun getPairs(
|
||||
userWallet: UserWallet,
|
||||
initialCurrency: CryptoCurrency,
|
||||
cryptoCurrencyStatusList: List<CryptoCurrencyStatus>,
|
||||
): List<SwapPairModel> = withContext(coroutineDispatcher.io) {
|
||||
val cryptoCurrencyList = cryptoCurrencyStatusList.map { it.currency }
|
||||
|
||||
val allPairs = getPairsInternal(
|
||||
userWallet = userWallet,
|
||||
initialCurrency = initialCurrency,
|
||||
cryptoCurrencyList = cryptoCurrencyList,
|
||||
)
|
||||
|
||||
val providers = expressRepository.getProviders(userWallet = userWallet)
|
||||
val mappedProviders = providers.associateBy(ExpressProvider::providerId)
|
||||
|
||||
allPairs.map { pair ->
|
||||
async {
|
||||
val statusFrom = cryptoCurrencyStatusList
|
||||
.firstOrNull {
|
||||
it.currency.getContractAddress() == pair.from.contractAddress &&
|
||||
it.currency.network.backendId == pair.from.network
|
||||
}
|
||||
val statusTo = cryptoCurrencyStatusList
|
||||
.firstOrNull {
|
||||
it.currency.getContractAddress() == pair.to.contractAddress &&
|
||||
it.currency.network.backendId == pair.to.network
|
||||
}
|
||||
|
||||
if (statusFrom != null && statusTo != null) {
|
||||
SwapPairModel(
|
||||
from = statusFrom,
|
||||
to = statusTo,
|
||||
providers = pair.providers.mapNotNull {
|
||||
mappedProviders[it.providerId]
|
||||
},
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}.awaitAll().filterNotNull()
|
||||
}
|
||||
|
||||
override suspend fun getPairsOnly(
|
||||
userWallet: UserWallet,
|
||||
initialCurrency: CryptoCurrency,
|
||||
cryptoCurrencyList: List<CryptoCurrency>,
|
||||
): List<SwapPairModel> = withContext(coroutineDispatcher.io) {
|
||||
val allPairs = getPairsInternal(
|
||||
userWallet = userWallet,
|
||||
initialCurrency = initialCurrency,
|
||||
cryptoCurrencyList = cryptoCurrencyList,
|
||||
)
|
||||
|
||||
allPairs.map { pair ->
|
||||
async {
|
||||
val statusFromDeferred = async {
|
||||
cryptoCurrencyList
|
||||
.firstOrNull {
|
||||
it.getContractAddress() == pair.from.contractAddress &&
|
||||
it.network.backendId == pair.from.network
|
||||
}
|
||||
}
|
||||
val statusToDeferred = async {
|
||||
cryptoCurrencyList
|
||||
.firstOrNull {
|
||||
it.getContractAddress() == pair.to.contractAddress &&
|
||||
it.network.backendId == pair.to.network
|
||||
}
|
||||
}
|
||||
|
||||
createPairModelOnly(
|
||||
currencyFrom = statusFromDeferred.await(),
|
||||
currencyTo = statusToDeferred.await(),
|
||||
userWalletId = userWallet.walletId,
|
||||
)
|
||||
}
|
||||
}.awaitAll().filterNotNull()
|
||||
}
|
||||
|
||||
private suspend fun CoroutineScope.getPairsInternal(
|
||||
userWallet: UserWallet,
|
||||
initialCurrency: CryptoCurrency,
|
||||
cryptoCurrencyList: List<CryptoCurrency>,
|
||||
) = awaitAll(
|
||||
// original pairs
|
||||
async {
|
||||
invokePairRequest(
|
||||
userWallet = userWallet,
|
||||
from = arrayListOf(initialCurrency),
|
||||
to = cryptoCurrencyList,
|
||||
)
|
||||
},
|
||||
// reversed pairs
|
||||
async {
|
||||
invokePairRequest(
|
||||
userWallet = userWallet,
|
||||
from = cryptoCurrencyList,
|
||||
to = arrayListOf(initialCurrency),
|
||||
)
|
||||
},
|
||||
).flatten()
|
||||
|
||||
private suspend fun invokePairRequest(
|
||||
userWallet: UserWallet,
|
||||
from: List<CryptoCurrency>,
|
||||
to: List<CryptoCurrency>,
|
||||
) = safeApiCall(
|
||||
call = {
|
||||
tangemExpressApi.getPairs(
|
||||
userWalletId = userWallet.walletId.stringValue,
|
||||
refCode = ExpressUtils.getRefCode(
|
||||
userWallet = userWallet,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
),
|
||||
body = PairsRequestBody(
|
||||
from = tokenInfoConverter.convertList(from),
|
||||
to = tokenInfoConverter.convertList(to),
|
||||
),
|
||||
).getOrThrow()
|
||||
},
|
||||
onError = {
|
||||
Timber.w(it, "Unable to get pairs")
|
||||
throw it
|
||||
},
|
||||
)
|
||||
|
||||
private suspend fun CoroutineScope.createPairModelOnly(
|
||||
currencyFrom: CryptoCurrency?,
|
||||
currencyTo: CryptoCurrency?,
|
||||
userWalletId: UserWalletId,
|
||||
): SwapPairModel? {
|
||||
return if (currencyFrom != null && currencyTo != null) {
|
||||
val statusFrom = currencyStatusOperations.getCurrencyStatusSync(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = currencyFrom.id,
|
||||
).getOrNull()
|
||||
val statusTo = currencyStatusOperations.getCurrencyStatusSync(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = currencyTo.id,
|
||||
).getOrNull()
|
||||
|
||||
if (statusFrom != null && statusTo != null) {
|
||||
SwapPairModel(
|
||||
from = statusFrom,
|
||||
to = statusTo,
|
||||
providers = emptyList(),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun CryptoCurrency.getContractAddress(): String {
|
||||
return when (this) {
|
||||
is CryptoCurrency.Token -> this.contractAddress
|
||||
is CryptoCurrency.Coin -> "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.data.swap.converter
|
||||
|
||||
import com.tangem.datasource.api.express.models.request.LeastTokenInfo
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.models.TokenInfo
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
class TokenInfoConverter : Converter<CryptoCurrency, LeastTokenInfo> {
|
||||
|
||||
override fun convert(value: CryptoCurrency): LeastTokenInfo {
|
||||
return LeastTokenInfo(
|
||||
contractAddress = (value as? CryptoCurrency.Token)?.contractAddress ?: "0",
|
||||
network = value.network.backendId,
|
||||
)
|
||||
}
|
||||
|
||||
fun convert(value: TokenInfo): LeastTokenInfo {
|
||||
return LeastTokenInfo(
|
||||
contractAddress = value.contractAddress,
|
||||
network = value.network,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.tangem.data.swap.di
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.data.express.converter.ExpressErrorConverter
|
||||
import com.tangem.data.swap.DefaultSwapErrorResolver
|
||||
import com.tangem.data.swap.DefaultSwapRepositoryV2
|
||||
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.ExpressRepository
|
||||
import com.tangem.domain.swap.SwapErrorResolver
|
||||
import com.tangem.domain.swap.SwapRepositoryV2
|
||||
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@Module
|
||||
internal object SwapDataModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSwapErrorResolver(@NetworkMoshi moshi: Moshi): SwapErrorResolver {
|
||||
val jsonAdapter = moshi.adapter(ExpressErrorResponse::class.java)
|
||||
return DefaultSwapErrorResolver(
|
||||
ExpressErrorConverter(jsonAdapter),
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSwapRepository(
|
||||
tangemExpressApi: TangemExpressApi,
|
||||
expressRepository: ExpressRepository,
|
||||
coroutineDispatcher: CoroutineDispatcherProvider,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
currencyStatusOperations: BaseCurrencyStatusOperations,
|
||||
): SwapRepositoryV2 {
|
||||
return DefaultSwapRepositoryV2(
|
||||
tangemExpressApi = tangemExpressApi,
|
||||
expressRepository = expressRepository,
|
||||
coroutineDispatcher = coroutineDispatcher,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
currencyStatusOperations = currencyStatusOperations,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue