Updated on 2026-08-14
This commit is contained in:
commit
62e4838d2f
22 changed files with 1171 additions and 0 deletions
|
|
@ -2,7 +2,11 @@ package com.tangem.tap.di.domain
|
|||
|
||||
import com.tangem.domain.swap.SwapErrorResolver
|
||||
import com.tangem.domain.swap.SwapRepositoryV2
|
||||
import com.tangem.domain.swap.SwapTransactionRepository
|
||||
import com.tangem.domain.swap.usecase.GetSwapPairsUseCase
|
||||
import com.tangem.domain.swap.usecase.GetSwapQuoteUseCase
|
||||
import com.tangem.domain.swap.usecase.GetSwapSupportedPairsUseCase
|
||||
import com.tangem.domain.swap.usecase.SelectInitialPairUseCase
|
||||
import com.tangem.feature.swap.domain.GetAvailablePairsUseCase
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -35,4 +39,38 @@ internal object SwapDomainModule {
|
|||
swapErrorResolver = swapErrorResolver,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetSwapPairsUseCase(
|
||||
swapRepositoryV2: SwapRepositoryV2,
|
||||
swapErrorResolver: SwapErrorResolver,
|
||||
): GetSwapPairsUseCase {
|
||||
return GetSwapPairsUseCase(
|
||||
swapRepositoryV2 = swapRepositoryV2,
|
||||
swapErrorResolver = swapErrorResolver,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSelectInitialPairUseCase(
|
||||
swapTransactionRepository: SwapTransactionRepository,
|
||||
): SelectInitialPairUseCase {
|
||||
return SelectInitialPairUseCase(
|
||||
swapTransactionRepository = swapTransactionRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetSwapQuoteUseCase(
|
||||
swapRepositoryV2: SwapRepositoryV2,
|
||||
swapErrorResolver: SwapErrorResolver,
|
||||
): GetSwapQuoteUseCase {
|
||||
return GetSwapQuoteUseCase(
|
||||
swapRepositoryV2 = swapRepositoryV2,
|
||||
swapErrorResolver = swapErrorResolver,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.datasource.api.express.models.response.ExpressErrorResponse
|
|||
import com.tangem.domain.onramp.model.error.OnrampError
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
@Deprecated("Use ExpressErrorConverter")
|
||||
internal class OnrampErrorConverter(
|
||||
private val jsonAdapter: JsonAdapter<ExpressErrorResponse>,
|
||||
) : Converter<String, OnrampError> {
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ 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.express.models.ExpressRateType
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.SwapRepositoryV2
|
||||
import com.tangem.domain.swap.models.SwapPairModel
|
||||
import com.tangem.domain.swap.models.SwapQuoteModel
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
|
@ -22,6 +24,7 @@ import kotlinx.coroutines.async
|
|||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -34,6 +37,7 @@ internal class DefaultSwapRepositoryV2 @Inject constructor(
|
|||
) : SwapRepositoryV2 {
|
||||
|
||||
private val tokenInfoConverter = TokenInfoConverter()
|
||||
|
||||
override suspend fun getPairs(
|
||||
userWallet: UserWallet,
|
||||
initialCurrency: CryptoCurrency,
|
||||
|
|
@ -115,6 +119,40 @@ internal class DefaultSwapRepositoryV2 @Inject constructor(
|
|||
}.awaitAll().filterNotNull()
|
||||
}
|
||||
|
||||
override suspend fun getSwapQuote(
|
||||
userWallet: UserWallet,
|
||||
fromCryptoCurrency: CryptoCurrency,
|
||||
toCryptoCurrency: CryptoCurrency,
|
||||
fromAmount: BigDecimal,
|
||||
provider: ExpressProvider,
|
||||
rateType: ExpressRateType,
|
||||
): SwapQuoteModel = withContext(coroutineDispatcher.io) {
|
||||
val response = tangemExpressApi.getExchangeQuote(
|
||||
fromAmount = fromAmount.movePointRight(fromCryptoCurrency.decimals).toString(),
|
||||
fromNetwork = fromCryptoCurrency.network.backendId,
|
||||
fromContractAddress = fromCryptoCurrency.getContractAddress(),
|
||||
fromDecimals = fromCryptoCurrency.decimals,
|
||||
toNetwork = toCryptoCurrency.network.backendId,
|
||||
toContractAddress = toCryptoCurrency.getContractAddress(),
|
||||
toDecimals = toCryptoCurrency.decimals,
|
||||
providerId = provider.providerId,
|
||||
rateType = rateType.name.lowercase(),
|
||||
userWalletId = userWallet.walletId.stringValue,
|
||||
refCode = ExpressUtils.getRefCode(
|
||||
userWallet = userWallet,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
),
|
||||
).getOrThrow()
|
||||
|
||||
val toTokenAmount = requireNotNull(response.toAmount.toBigDecimalOrNull()?.movePointLeft(response.toDecimals))
|
||||
|
||||
return@withContext SwapQuoteModel(
|
||||
provider = provider,
|
||||
toTokenAmount = toTokenAmount,
|
||||
allowanceContract = response.allowanceContract,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun CoroutineScope.getPairsInternal(
|
||||
userWallet: UserWallet,
|
||||
initialCurrency: CryptoCurrency,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,289 @@
|
|||
package com.tangem.data.swap
|
||||
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||
import com.tangem.data.swap.converter.transaction.SavedSwapStatusConverter
|
||||
import com.tangem.data.swap.converter.transaction.SavedSwapTransactionConverter
|
||||
import com.tangem.data.swap.converter.transaction.SavedSwapTransactionListConverter
|
||||
import com.tangem.data.swap.models.LastSwappedCryptoCurrencyDTO
|
||||
import com.tangem.data.swap.models.SwapStatusDTO
|
||||
import com.tangem.data.swap.models.SwapTransactionDTO
|
||||
import com.tangem.data.swap.models.SwapTransactionListDTO
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectList
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectListSync
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectMapSync
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.SwapTransactionRepository
|
||||
import com.tangem.domain.swap.models.SwapStatusModel
|
||||
import com.tangem.domain.swap.models.SwapTransactionListModel
|
||||
import com.tangem.domain.swap.models.SwapTransactionModel
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.models.requireColdWallet
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.addOrReplace
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal class DefaultSwapTransactionRepository(
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
) : SwapTransactionRepository {
|
||||
|
||||
private val listConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SavedSwapTransactionListConverter(excludedBlockchains)
|
||||
}
|
||||
private val converter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SavedSwapTransactionConverter(excludedBlockchains)
|
||||
}
|
||||
private val savedStatusConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SavedSwapStatusConverter()
|
||||
}
|
||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||
|
||||
override suspend fun storeTransaction(
|
||||
userWalletId: UserWalletId,
|
||||
fromCryptoCurrency: CryptoCurrency,
|
||||
toCryptoCurrency: CryptoCurrency,
|
||||
transaction: SwapTransactionModel,
|
||||
) {
|
||||
transaction.status?.let {
|
||||
storeTransactionState(
|
||||
txId = transaction.txId,
|
||||
status = it,
|
||||
refundTokenCurrency = null,
|
||||
)
|
||||
}
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedTransactions: List<SwapTransactionListDTO>? = mutablePreferences.getObjectList(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
|
||||
)
|
||||
val tokenTransactions = savedTransactions
|
||||
?.firstOrNull {
|
||||
it.checkId(
|
||||
checkUserWalletId = userWalletId,
|
||||
fromCurrencyId = fromCryptoCurrency.id,
|
||||
toCurrencyId = toCryptoCurrency.id,
|
||||
)
|
||||
}
|
||||
?.transactions
|
||||
?.addOrReplace(
|
||||
item = converter.convert(transaction),
|
||||
predicate = { it.txId == transaction.txId },
|
||||
) ?: listOf(converter.convert(transaction))
|
||||
|
||||
mutablePreferences.setObject(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
|
||||
value = savedTransactions?.updateList(
|
||||
userWalletId = userWalletId,
|
||||
fromCryptoCurrency = fromCryptoCurrency,
|
||||
toCryptoCurrency = toCryptoCurrency,
|
||||
transactions = tokenTransactions,
|
||||
) ?: listOf(
|
||||
listConverter.default(
|
||||
userWalletId = userWalletId,
|
||||
fromCryptoCurrency = fromCryptoCurrency,
|
||||
toCryptoCurrency = toCryptoCurrency,
|
||||
tokenTransactions = listOf(converter.convert(transaction)),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getTransactions(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
): Flow<List<SwapTransactionListModel>?> {
|
||||
return withContext(dispatchers.io) {
|
||||
val txStatuses = appPreferencesStore.getObjectMapSync<SwapStatusDTO>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
)
|
||||
appPreferencesStore.getObjectList<SwapTransactionListDTO>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
|
||||
).map { savedTransactions ->
|
||||
val currencyTxs = savedTransactions
|
||||
?.filter {
|
||||
it.userWalletId == userWallet.walletId.stringValue &&
|
||||
(
|
||||
it.toCryptoCurrencyId == cryptoCurrencyId.value ||
|
||||
it.fromCryptoCurrencyId == cryptoCurrencyId.value
|
||||
)
|
||||
}
|
||||
|
||||
currencyTxs?.mapNotNull {
|
||||
listConverter.convertBack(
|
||||
value = it,
|
||||
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
|
||||
txStatuses = txStatuses,
|
||||
)
|
||||
}
|
||||
}.flowOn(dispatchers.io)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun removeTransaction(
|
||||
userWalletId: UserWalletId,
|
||||
fromCryptoCurrency: CryptoCurrency,
|
||||
toCryptoCurrency: CryptoCurrency,
|
||||
txId: String,
|
||||
) {
|
||||
clearTransactionsStatuses(txId = txId)
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedList: List<SwapTransactionListDTO>? = mutablePreferences.getObjectList(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
|
||||
)
|
||||
val tokenTransactions = savedList
|
||||
?.firstOrNull {
|
||||
it.checkId(
|
||||
checkUserWalletId = userWalletId,
|
||||
fromCurrencyId = fromCryptoCurrency.id,
|
||||
toCurrencyId = toCryptoCurrency.id,
|
||||
)
|
||||
}
|
||||
?.transactions
|
||||
?.filterNot { it.txId == txId }
|
||||
|
||||
val editedList =
|
||||
if (tokenTransactions.isNullOrEmpty()) {
|
||||
savedList?.filterNot {
|
||||
it.checkId(
|
||||
checkUserWalletId = userWalletId,
|
||||
fromCurrencyId = fromCryptoCurrency.id,
|
||||
toCurrencyId = toCryptoCurrency.id,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
savedList.updateList(
|
||||
userWalletId = userWalletId,
|
||||
fromCryptoCurrency = fromCryptoCurrency,
|
||||
toCryptoCurrency = toCryptoCurrency,
|
||||
transactions = tokenTransactions,
|
||||
)
|
||||
}
|
||||
|
||||
if (editedList.isNullOrEmpty()) {
|
||||
mutablePreferences.remove(key = PreferencesKeys.SWAP_TRANSACTIONS_KEY)
|
||||
} else {
|
||||
mutablePreferences.setObject(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
|
||||
value = editedList,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun storeTransactionState(
|
||||
txId: String,
|
||||
status: SwapStatusModel,
|
||||
refundTokenCurrency: CryptoCurrency?,
|
||||
) {
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedMap = mutablePreferences.getObjectMap<SwapStatusDTO>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
)
|
||||
|
||||
val updatesMap = savedMap.toMutableMap()
|
||||
updatesMap[txId] = savedStatusConverter.convertBack(
|
||||
status.copy(
|
||||
refundTokensResponse = refundTokenCurrency?.let {
|
||||
userTokensResponseFactory.createResponseToken(refundTokenCurrency)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
mutablePreferences.setObjectMap(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
value = updatesMap,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getLastSwappedCryptoCurrencyId(userWalletId: UserWalletId): String? {
|
||||
val lastSwappedCurrencies = appPreferencesStore.getObjectListSync<LastSwappedCryptoCurrencyDTO>(
|
||||
key = PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY,
|
||||
)
|
||||
|
||||
return lastSwappedCurrencies.find { userWalletId.stringValue == it.userWalletId }?.cryptoCurrencyId
|
||||
}
|
||||
|
||||
override suspend fun storeLastSwappedCryptoCurrencyId(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
) {
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val lastSwappedCryptoCurrencies: List<LastSwappedCryptoCurrencyDTO>? = mutablePreferences.getObjectList(
|
||||
key = PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY,
|
||||
)
|
||||
|
||||
val newList = if (lastSwappedCryptoCurrencies != null) {
|
||||
lastSwappedCryptoCurrencies.filter {
|
||||
it.userWalletId != userWalletId.stringValue
|
||||
} + LastSwappedCryptoCurrencyDTO(userWalletId.stringValue, cryptoCurrencyId.value)
|
||||
} else {
|
||||
listOf(LastSwappedCryptoCurrencyDTO(userWalletId.stringValue, cryptoCurrencyId.value))
|
||||
}
|
||||
|
||||
mutablePreferences.setObjectList(
|
||||
key = PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY,
|
||||
value = newList,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SwapTransactionListDTO.checkId(
|
||||
checkUserWalletId: UserWalletId,
|
||||
fromCurrencyId: CryptoCurrency.ID,
|
||||
toCurrencyId: CryptoCurrency.ID,
|
||||
): Boolean {
|
||||
return userWalletId == checkUserWalletId.stringValue &&
|
||||
toCryptoCurrencyId == toCurrencyId.value &&
|
||||
fromCryptoCurrencyId == fromCurrencyId.value
|
||||
}
|
||||
|
||||
private fun List<SwapTransactionListDTO>.updateList(
|
||||
userWalletId: UserWalletId,
|
||||
fromCryptoCurrency: CryptoCurrency,
|
||||
toCryptoCurrency: CryptoCurrency,
|
||||
transactions: List<SwapTransactionDTO>,
|
||||
): List<SwapTransactionListDTO> {
|
||||
return addOrReplace(
|
||||
item = listConverter.default(
|
||||
userWalletId = userWalletId,
|
||||
fromCryptoCurrency = fromCryptoCurrency,
|
||||
toCryptoCurrency = toCryptoCurrency,
|
||||
tokenTransactions = transactions,
|
||||
),
|
||||
predicate = {
|
||||
it.checkId(
|
||||
checkUserWalletId = userWalletId,
|
||||
fromCurrencyId = fromCryptoCurrency.id,
|
||||
toCurrencyId = toCryptoCurrency.id,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun clearTransactionsStatuses(txId: String) {
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedList = mutablePreferences.getObjectMap<SwapStatusModel>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
)
|
||||
val editedList = savedList.filterNot { it.key == txId }
|
||||
|
||||
if (editedList.isEmpty()) {
|
||||
mutablePreferences.remove(key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY)
|
||||
} else {
|
||||
mutablePreferences.setObjectMap(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
value = editedList,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.data.swap.converter
|
||||
|
||||
import com.tangem.datasource.api.express.models.response.ExchangeStatusResponse
|
||||
import com.tangem.domain.swap.models.SwapStatus
|
||||
import com.tangem.domain.swap.models.SwapStatusModel
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class SwapStatusConverter : Converter<ExchangeStatusResponse, SwapStatusModel> {
|
||||
override fun convert(value: ExchangeStatusResponse): SwapStatusModel {
|
||||
return SwapStatusModel(
|
||||
providerId = value.providerId,
|
||||
status = SwapStatus.entries.firstOrNull {
|
||||
it.name.lowercase() == value.status.name.lowercase()
|
||||
},
|
||||
txId = value.externalTxId,
|
||||
txExternalUrl = value.externalTxUrl,
|
||||
txExternalId = value.externalTxId,
|
||||
refundNetwork = value.refundNetwork,
|
||||
refundContractAddress = value.refundContractAddress,
|
||||
createdAt = value.createdAt,
|
||||
averageDuration = value.averageDuration,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.data.swap.converter.transaction
|
||||
|
||||
import com.tangem.data.swap.models.SavedSwapStatus
|
||||
import com.tangem.data.swap.models.SwapStatusDTO
|
||||
import com.tangem.domain.swap.models.SwapStatus
|
||||
import com.tangem.domain.swap.models.SwapStatusModel
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
|
||||
internal class SavedSwapStatusConverter : TwoWayConverter<SwapStatusDTO, SwapStatusModel> {
|
||||
|
||||
override fun convert(value: SwapStatusDTO) = SwapStatusModel(
|
||||
providerId = value.providerId,
|
||||
status = SwapStatus.entries.firstOrNull {
|
||||
it.name.lowercase() == value.status?.name?.lowercase()
|
||||
},
|
||||
txId = value.txExternalId,
|
||||
txExternalUrl = value.txExternalUrl,
|
||||
txExternalId = value.txExternalId,
|
||||
refundNetwork = value.refundNetwork,
|
||||
refundContractAddress = value.refundContractAddress,
|
||||
createdAt = value.createdAt,
|
||||
averageDuration = value.averageDuration,
|
||||
)
|
||||
|
||||
override fun convertBack(value: SwapStatusModel) = SwapStatusDTO(
|
||||
providerId = value.providerId,
|
||||
status = SavedSwapStatus.entries.firstOrNull {
|
||||
it.name.lowercase() == value.status?.name?.lowercase()
|
||||
},
|
||||
txId = value.txExternalId,
|
||||
txExternalUrl = value.txExternalUrl,
|
||||
txExternalId = value.txExternalId,
|
||||
refundNetwork = value.refundNetwork,
|
||||
refundContractAddress = value.refundContractAddress,
|
||||
createdAt = value.createdAt,
|
||||
averageDuration = value.averageDuration,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.tangem.data.swap.converter.transaction
|
||||
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
||||
import com.tangem.data.swap.models.SwapStatusDTO
|
||||
import com.tangem.data.swap.models.SwapTransactionDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.swap.models.SwapTransactionModel
|
||||
import com.tangem.utils.converter.TwoWayConverter
|
||||
|
||||
internal class SavedSwapTransactionConverter(
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
) : TwoWayConverter<SwapTransactionModel, SwapTransactionDTO> {
|
||||
private val responseCryptoCurrenciesFactory = ResponseCryptoCurrenciesFactory(excludedBlockchains)
|
||||
private val statusConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SavedSwapStatusConverter()
|
||||
}
|
||||
|
||||
override fun convert(value: SwapTransactionModel) = SwapTransactionDTO(
|
||||
txId = value.txId,
|
||||
timestamp = value.timestamp,
|
||||
fromCryptoAmount = value.fromCryptoAmount,
|
||||
toCryptoAmount = value.toCryptoAmount,
|
||||
provider = value.provider,
|
||||
status = value.status,
|
||||
)
|
||||
|
||||
override fun convertBack(value: SwapTransactionDTO) = SwapTransactionModel(
|
||||
txId = value.txId,
|
||||
timestamp = value.timestamp,
|
||||
fromCryptoAmount = value.fromCryptoAmount,
|
||||
toCryptoAmount = value.toCryptoAmount,
|
||||
provider = value.provider,
|
||||
status = value.status,
|
||||
)
|
||||
|
||||
fun convertBack(
|
||||
value: SwapTransactionDTO,
|
||||
scanResponse: ScanResponse,
|
||||
txStatuses: Map<String, SwapStatusDTO>,
|
||||
): SwapTransactionModel {
|
||||
val status = txStatuses[value.txId]
|
||||
val refundCurrency = status?.refundTokensResponse?.let { id ->
|
||||
responseCryptoCurrenciesFactory.createCurrency(
|
||||
responseToken = id,
|
||||
scanResponse = scanResponse,
|
||||
)
|
||||
}
|
||||
val statusWithRefundCurrency = status?.copy(refundCurrency = refundCurrency)
|
||||
|
||||
return SwapTransactionModel(
|
||||
txId = value.txId,
|
||||
timestamp = value.timestamp,
|
||||
fromCryptoAmount = value.fromCryptoAmount,
|
||||
toCryptoAmount = value.toCryptoAmount,
|
||||
provider = value.provider,
|
||||
status = statusWithRefundCurrency?.let(statusConverter::convert),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package com.tangem.data.swap.converter.transaction
|
||||
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
|
||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||
import com.tangem.data.swap.models.SwapStatusDTO
|
||||
import com.tangem.data.swap.models.SwapTransactionDTO
|
||||
import com.tangem.data.swap.models.SwapTransactionListDTO
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.swap.models.SwapTransactionListModel
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class SavedSwapTransactionListConverter(
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
) : Converter<SwapTransactionListModel, SwapTransactionListDTO> {
|
||||
|
||||
private val responseCryptoCurrenciesFactory = ResponseCryptoCurrenciesFactory(excludedBlockchains)
|
||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||
private val savedSwapTransactionConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SavedSwapTransactionConverter(excludedBlockchains)
|
||||
}
|
||||
|
||||
override fun convert(value: SwapTransactionListModel) = SwapTransactionListDTO(
|
||||
userWalletId = value.userWalletId,
|
||||
fromCryptoCurrencyId = value.fromCryptoCurrencyId,
|
||||
toCryptoCurrencyId = value.toCryptoCurrencyId,
|
||||
fromTokensResponse = userTokensResponseFactory.createResponseToken(
|
||||
value.fromCryptoCurrency,
|
||||
),
|
||||
toTokensResponse = userTokensResponseFactory.createResponseToken(
|
||||
value.toCryptoCurrency,
|
||||
),
|
||||
transactions = savedSwapTransactionConverter.convertList(value.transactions),
|
||||
)
|
||||
|
||||
fun convertBack(
|
||||
value: SwapTransactionListDTO,
|
||||
scanResponse: ScanResponse,
|
||||
txStatuses: Map<String, SwapStatusDTO>,
|
||||
): SwapTransactionListModel? {
|
||||
val fromToken = value.fromTokensResponse
|
||||
val toToken = value.toTokensResponse
|
||||
return if (fromToken == null || toToken == null) {
|
||||
null
|
||||
} else {
|
||||
val fromCryptoCurrency = responseCryptoCurrenciesFactory.createCurrency(
|
||||
responseToken = fromToken,
|
||||
scanResponse = scanResponse,
|
||||
) ?: return null
|
||||
val toCryptoCurrency = responseCryptoCurrenciesFactory.createCurrency(
|
||||
responseToken = toToken,
|
||||
scanResponse = scanResponse,
|
||||
) ?: return null
|
||||
|
||||
return SwapTransactionListModel(
|
||||
transactions = value.transactions.map { tx ->
|
||||
savedSwapTransactionConverter.convertBack(tx, scanResponse, txStatuses)
|
||||
},
|
||||
userWalletId = value.userWalletId,
|
||||
fromCryptoCurrencyId = value.fromCryptoCurrencyId,
|
||||
toCryptoCurrencyId = value.toCryptoCurrencyId,
|
||||
fromCryptoCurrency = fromCryptoCurrency,
|
||||
toCryptoCurrency = toCryptoCurrency,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun default(
|
||||
userWalletId: UserWalletId,
|
||||
fromCryptoCurrency: CryptoCurrency,
|
||||
toCryptoCurrency: CryptoCurrency,
|
||||
tokenTransactions: List<SwapTransactionDTO>,
|
||||
) = SwapTransactionListDTO(
|
||||
userWalletId = userWalletId.stringValue,
|
||||
fromCryptoCurrencyId = fromCryptoCurrency.id.value,
|
||||
toCryptoCurrencyId = toCryptoCurrency.id.value,
|
||||
fromTokensResponse = userTokensResponseFactory.createResponseToken(fromCryptoCurrency),
|
||||
toTokensResponse = userTokensResponseFactory.createResponseToken(toCryptoCurrency),
|
||||
transactions = tokenTransactions,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
package com.tangem.data.swap.di
|
||||
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.express.converter.ExpressErrorConverter
|
||||
import com.tangem.data.swap.DefaultSwapErrorResolver
|
||||
import com.tangem.data.swap.DefaultSwapRepositoryV2
|
||||
import com.tangem.data.swap.DefaultSwapTransactionRepository
|
||||
import com.tangem.datasource.api.express.TangemExpressApi
|
||||
import com.tangem.datasource.api.express.models.response.ExpressErrorResponse
|
||||
import com.tangem.datasource.di.NetworkMoshi
|
||||
|
|
@ -11,6 +13,7 @@ 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.swap.SwapTransactionRepository
|
||||
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -49,4 +52,18 @@ internal object SwapDataModule {
|
|||
currencyStatusOperations = currencyStatusOperations,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSwapTransactionRepository(
|
||||
coroutineDispatcher: CoroutineDispatcherProvider,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
): SwapTransactionRepository {
|
||||
return DefaultSwapTransactionRepository(
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
dispatchers = coroutineDispatcher,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.data.swap.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class LastSwappedCryptoCurrencyDTO(
|
||||
@Json(name = "userWalletId")
|
||||
val userWalletId: String,
|
||||
@Json(name = "cryptoCurrencyId")
|
||||
val cryptoCurrencyId: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package com.tangem.data.swap.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import org.joda.time.DateTime
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class SwapStatusDTO(
|
||||
@Json(name = "providerId")
|
||||
val providerId: String,
|
||||
@Json(name = "status")
|
||||
val status: SavedSwapStatus? = null,
|
||||
@Json(name = "txId")
|
||||
val txId: String? = null,
|
||||
@Json(name = "txExternalUrl")
|
||||
val txExternalUrl: String? = null,
|
||||
@Json(name = "txExternalId")
|
||||
val txExternalId: String? = null,
|
||||
@Json(name = "refundNetwork")
|
||||
val refundNetwork: String? = null,
|
||||
@Json(name = "refundContractAddress")
|
||||
val refundContractAddress: String? = null,
|
||||
@Json(name = "refundTokensResponse")
|
||||
val refundTokensResponse: UserTokensResponse.Token? = null,
|
||||
@Json(ignore = true)
|
||||
val refundCurrency: CryptoCurrency? = null,
|
||||
@Json(name = "createdAt")
|
||||
val createdAt: DateTime? = null,
|
||||
@Json(name = "averageDuration")
|
||||
val averageDuration: Int? = null,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
internal enum class SavedSwapStatus {
|
||||
@Json(name = "New")
|
||||
New,
|
||||
|
||||
@Json(name = "Waiting")
|
||||
Waiting,
|
||||
|
||||
@Json(name = "WaitingTxHash")
|
||||
WaitingTxHash,
|
||||
|
||||
@Json(name = "Confirming")
|
||||
Confirming,
|
||||
|
||||
@Json(name = "Verifying")
|
||||
Verifying,
|
||||
|
||||
@Json(name = "Exchanging")
|
||||
Exchanging,
|
||||
|
||||
@Json(name = "Failed")
|
||||
Failed,
|
||||
|
||||
@Json(name = "Sending")
|
||||
Sending,
|
||||
|
||||
@Json(name = "Finished")
|
||||
Finished,
|
||||
|
||||
@Json(name = "Refunded")
|
||||
Refunded,
|
||||
|
||||
@Json(name = "Cancelled")
|
||||
Cancelled,
|
||||
|
||||
@Json(name = "TxFailed")
|
||||
TxFailed,
|
||||
|
||||
@Json(name = "Unknown")
|
||||
Unknown,
|
||||
|
||||
@Json(name = "Paused")
|
||||
Paused,
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.data.swap.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.swap.models.SwapStatusModel
|
||||
import java.math.BigDecimal
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class SwapTransactionListDTO(
|
||||
@Json(name = "userWalletId")
|
||||
val userWalletId: String,
|
||||
@Json(name = "fromCryptoCurrencyId")
|
||||
val fromCryptoCurrencyId: String,
|
||||
@Json(name = "toCryptoCurrencyId")
|
||||
val toCryptoCurrencyId: String,
|
||||
@Json(name = "fromTokensResponse")
|
||||
val fromTokensResponse: UserTokensResponse.Token? = null,
|
||||
@Json(name = "toTokensResponse")
|
||||
val toTokensResponse: UserTokensResponse.Token? = null,
|
||||
@Json(name = "transactions")
|
||||
val transactions: List<SwapTransactionDTO>,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class SwapTransactionDTO(
|
||||
@Json(name = "txId")
|
||||
val txId: String,
|
||||
@Json(name = "timestamp")
|
||||
val timestamp: Long,
|
||||
@Json(name = "fromCryptoAmount")
|
||||
val fromCryptoAmount: BigDecimal,
|
||||
@Json(name = "toCryptoAmount")
|
||||
val toCryptoAmount: BigDecimal,
|
||||
@Json(name = "provider")
|
||||
val provider: ExpressProvider,
|
||||
@Json(name = "status")
|
||||
val status: SwapStatusModel? = null,
|
||||
)
|
||||
|
|
@ -3,17 +3,43 @@ package com.tangem.domain.swap.models
|
|||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
||||
/**
|
||||
* Model of currencies available to swap
|
||||
*
|
||||
* @param fromGroup group of currencies available swap from
|
||||
* @param toGroup group of currencies available swap to
|
||||
*/
|
||||
data class SwapCurrencies(
|
||||
val fromGroup: SwapCurrenciesGroup,
|
||||
val toGroup: SwapCurrenciesGroup,
|
||||
)
|
||||
|
||||
/**
|
||||
* Return swap group depending on [swapDirection]
|
||||
*/
|
||||
fun SwapCurrencies.getGroupWithReverse(swapDirection: SwapDirection): SwapCurrenciesGroup {
|
||||
return when (swapDirection) {
|
||||
SwapDirection.Reverse -> fromGroup
|
||||
SwapDirection.Direct -> toGroup
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap group model
|
||||
*
|
||||
* @param available list of available currencies to swap
|
||||
* @param available list of unavailable currencies to swap
|
||||
* @param afterSearch flag indicates whether user searched token
|
||||
*/
|
||||
data class SwapCurrenciesGroup(
|
||||
val available: List<SwapCryptoCurrency>,
|
||||
val unavailable: List<SwapCryptoCurrency>,
|
||||
val afterSearch: Boolean,
|
||||
)
|
||||
|
||||
/**
|
||||
* Crypto currency with providers to swap
|
||||
*/
|
||||
data class SwapCryptoCurrency(
|
||||
val currencyStatus: CryptoCurrencyStatus,
|
||||
val providers: List<ExpressProvider>,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.domain.swap.models
|
||||
|
||||
import com.tangem.domain.swap.models.SwapDirection.Direct
|
||||
import com.tangem.domain.swap.models.SwapDirection.Reverse
|
||||
|
||||
/**
|
||||
* Swap direction
|
||||
*
|
||||
* Initial currency can be swap to or from.
|
||||
* If swap being swap from direction is [Direct],
|
||||
* Otherwise direction is [Reverse]
|
||||
*/
|
||||
enum class SwapDirection {
|
||||
Direct,
|
||||
Reverse,
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.domain.swap.models
|
||||
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Quote model holds data about current amounts of swaps and fees
|
||||
*
|
||||
* @property provider swap provider
|
||||
* @property toTokenAmount amount of token you want to receive
|
||||
* @property allowanceContract whether swap occurs via third token
|
||||
*/
|
||||
data class SwapQuoteModel(
|
||||
val provider: ExpressProvider,
|
||||
val toTokenAmount: BigDecimal,
|
||||
val allowanceContract: String?,
|
||||
)
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.tangem.domain.swap.models
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import org.joda.time.DateTime
|
||||
|
||||
/**
|
||||
* Swapped status model
|
||||
*/
|
||||
data class SwapStatusModel(
|
||||
val providerId: String,
|
||||
val status: SwapStatus? = null,
|
||||
val txId: String? = null,
|
||||
val txExternalUrl: String? = null,
|
||||
val txExternalId: String? = null,
|
||||
val refundNetwork: String? = null,
|
||||
val refundContractAddress: String? = null,
|
||||
val refundTokensResponse: UserTokensResponse.Token? = null,
|
||||
val refundCurrency: CryptoCurrency? = null,
|
||||
val createdAt: DateTime? = null,
|
||||
val averageDuration: Int? = null,
|
||||
) {
|
||||
val hasLongTime: Boolean
|
||||
get() = if (createdAt != null && averageDuration != null) {
|
||||
DateTime.now().minusSeconds(averageDuration * 5) > createdAt
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
enum class SwapStatus {
|
||||
New,
|
||||
Waiting,
|
||||
WaitingTxHash,
|
||||
Confirming,
|
||||
Verifying,
|
||||
Exchanging,
|
||||
Failed,
|
||||
Sending,
|
||||
Finished,
|
||||
Refunded,
|
||||
Cancelled,
|
||||
TxFailed,
|
||||
Unknown,
|
||||
Paused,
|
||||
;
|
||||
|
||||
val isTerminal: Boolean
|
||||
get() = this == Refunded ||
|
||||
this == Finished ||
|
||||
this == Cancelled ||
|
||||
this == TxFailed ||
|
||||
this == Paused ||
|
||||
this == Unknown
|
||||
|
||||
val isAutoDisposable: Boolean
|
||||
get() = this == Finished
|
||||
|
||||
companion object {
|
||||
fun SwapStatus?.isFailed(): Boolean = this == Failed || this == TxFailed
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.domain.swap.models
|
||||
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* List of saved swap transactions
|
||||
*/
|
||||
data class SwapTransactionListModel(
|
||||
val userWalletId: String,
|
||||
val fromCryptoCurrencyId: String,
|
||||
val toCryptoCurrencyId: String,
|
||||
val fromCryptoCurrency: CryptoCurrency,
|
||||
val toCryptoCurrency: CryptoCurrency,
|
||||
val transactions: List<SwapTransactionModel>,
|
||||
)
|
||||
|
||||
/**
|
||||
* Saved swap transactions
|
||||
*/
|
||||
data class SwapTransactionModel(
|
||||
val txId: String,
|
||||
val timestamp: Long,
|
||||
val fromCryptoAmount: BigDecimal,
|
||||
val toCryptoAmount: BigDecimal,
|
||||
val provider: ExpressProvider,
|
||||
val status: SwapStatusModel? = null,
|
||||
)
|
||||
|
|
@ -1,10 +1,17 @@
|
|||
package com.tangem.domain.swap
|
||||
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.express.models.ExpressRateType
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.models.SwapPairModel
|
||||
import com.tangem.domain.swap.models.SwapQuoteModel
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Swap repository
|
||||
*/
|
||||
interface SwapRepositoryV2 {
|
||||
|
||||
/**
|
||||
|
|
@ -26,4 +33,24 @@ interface SwapRepositoryV2 {
|
|||
initialCurrency: CryptoCurrency,
|
||||
cryptoCurrencyList: List<CryptoCurrency>,
|
||||
): List<SwapPairModel>
|
||||
|
||||
/**
|
||||
* Returns swap quotes on selected pair
|
||||
*
|
||||
* @param userWallet selected user wallet
|
||||
* @param fromCryptoCurrency currency being swapped from
|
||||
* @param toCryptoCurrency currency being swapped to
|
||||
* @param fromAmount swap amount
|
||||
* @param provider selected express provider
|
||||
* @param rateType rate type
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun getSwapQuote(
|
||||
userWallet: UserWallet,
|
||||
fromCryptoCurrency: CryptoCurrency,
|
||||
toCryptoCurrency: CryptoCurrency,
|
||||
fromAmount: BigDecimal,
|
||||
provider: ExpressProvider,
|
||||
rateType: ExpressRateType,
|
||||
): SwapQuoteModel
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package com.tangem.domain.swap
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.models.SwapStatusModel
|
||||
import com.tangem.domain.swap.models.SwapTransactionListModel
|
||||
import com.tangem.domain.swap.models.SwapTransactionModel
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Swap repository for statuses
|
||||
*/
|
||||
interface SwapTransactionRepository {
|
||||
|
||||
/**
|
||||
* Store new swap transaction
|
||||
*
|
||||
* @param userWalletId selected user wallet id
|
||||
* @param fromCryptoCurrency currency swap from
|
||||
* @param toCryptoCurrency currency swap to
|
||||
* @param transaction swap transaction
|
||||
*/
|
||||
suspend fun storeTransaction(
|
||||
userWalletId: UserWalletId,
|
||||
fromCryptoCurrency: CryptoCurrency,
|
||||
toCryptoCurrency: CryptoCurrency,
|
||||
transaction: SwapTransactionModel,
|
||||
)
|
||||
|
||||
/**
|
||||
* Get list of swap transactions
|
||||
*
|
||||
* @param userWallet selected user wallet
|
||||
* @param cryptoCurrencyId transactions for specific crypto currency
|
||||
*/
|
||||
suspend fun getTransactions(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
): Flow<List<SwapTransactionListModel>?>
|
||||
|
||||
/**
|
||||
* Remove stores swap transaction
|
||||
*
|
||||
* @param userWalletId selected user wallet id
|
||||
* @param fromCryptoCurrency currency swap from
|
||||
* @param toCryptoCurrency currency swap to
|
||||
* @param txId transaction id to remove
|
||||
*/
|
||||
suspend fun removeTransaction(
|
||||
userWalletId: UserWalletId,
|
||||
fromCryptoCurrency: CryptoCurrency,
|
||||
toCryptoCurrency: CryptoCurrency,
|
||||
txId: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Update swap transaction
|
||||
*
|
||||
* @param txId transaction id to update
|
||||
* @param status new transaction status
|
||||
* @param refundTokenCurrency refund token
|
||||
*/
|
||||
suspend fun storeTransactionState(txId: String, status: SwapStatusModel, refundTokenCurrency: CryptoCurrency?)
|
||||
|
||||
/**
|
||||
* Save last swapped crypto currency token
|
||||
*
|
||||
* @param userWalletId selected user wallet id
|
||||
* @param cryptoCurrencyId last swapped currency id
|
||||
*/
|
||||
suspend fun storeLastSwappedCryptoCurrencyId(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID)
|
||||
|
||||
/**
|
||||
* Get last swapped crypto currency token
|
||||
*
|
||||
* @param userWalletId selected user wallet id
|
||||
*/
|
||||
suspend fun getLastSwappedCryptoCurrencyId(userWalletId: UserWalletId): String?
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package com.tangem.domain.swap.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.SwapErrorResolver
|
||||
import com.tangem.domain.swap.SwapRepositoryV2
|
||||
import com.tangem.domain.swap.models.SwapCryptoCurrency
|
||||
import com.tangem.domain.swap.models.SwapCurrencies
|
||||
import com.tangem.domain.swap.models.SwapCurrenciesGroup
|
||||
import com.tangem.domain.swap.models.SwapPairModel
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
||||
/**
|
||||
* Get list of swap pairs
|
||||
*/
|
||||
class GetSwapPairsUseCase(
|
||||
private val swapRepositoryV2: SwapRepositoryV2,
|
||||
private val swapErrorResolver: SwapErrorResolver,
|
||||
) {
|
||||
|
||||
/**
|
||||
* @param userWallet selected user wallet
|
||||
* @param initialCurrency initial currency to swap (to or from)
|
||||
* @param cryptoCurrencyStatusList list of added cryptocurrencies
|
||||
*/
|
||||
suspend operator fun invoke(
|
||||
userWallet: UserWallet,
|
||||
initialCurrency: CryptoCurrency,
|
||||
cryptoCurrencyStatusList: List<CryptoCurrencyStatus>,
|
||||
) = Either.catch {
|
||||
val pairs = swapRepositoryV2.getPairs(
|
||||
userWallet = userWallet,
|
||||
initialCurrency = initialCurrency,
|
||||
cryptoCurrencyStatusList = cryptoCurrencyStatusList,
|
||||
)
|
||||
|
||||
val fromGroup = pairs.groupPairs(
|
||||
initialCurrency = initialCurrency,
|
||||
filteringCurrency = { it.from },
|
||||
groupingCurrency = { it.to },
|
||||
cryptoCurrencyStatusList = cryptoCurrencyStatusList,
|
||||
)
|
||||
val toGroup = pairs.groupPairs(
|
||||
initialCurrency = initialCurrency,
|
||||
filteringCurrency = { it.to },
|
||||
groupingCurrency = { it.from },
|
||||
cryptoCurrencyStatusList = cryptoCurrencyStatusList,
|
||||
)
|
||||
|
||||
SwapCurrencies(
|
||||
fromGroup = fromGroup,
|
||||
toGroup = toGroup,
|
||||
)
|
||||
}.mapLeft(swapErrorResolver::resolve)
|
||||
|
||||
private fun List<SwapPairModel>.groupPairs(
|
||||
initialCurrency: CryptoCurrency,
|
||||
filteringCurrency: (SwapPairModel) -> CryptoCurrencyStatus,
|
||||
groupingCurrency: (SwapPairModel) -> CryptoCurrencyStatus,
|
||||
cryptoCurrencyStatusList: List<CryptoCurrencyStatus>,
|
||||
): SwapCurrenciesGroup {
|
||||
val availableCryptoCurrencies = filter { pair -> filteringCurrency(pair).currency.id == initialCurrency.id }
|
||||
// Search available to swap currency
|
||||
.filter { pair ->
|
||||
cryptoCurrencyStatusList.any { currencyStatus ->
|
||||
currencyStatus.currency.id == pair.to.currency.id
|
||||
}
|
||||
}.map { pair -> SwapCryptoCurrency(groupingCurrency(pair), pair.providers) }
|
||||
|
||||
val unavailableCryptoCurrencies =
|
||||
cryptoCurrencyStatusList - availableCryptoCurrencies.map { it.currencyStatus }.toSet()
|
||||
|
||||
return SwapCurrenciesGroup(
|
||||
available = availableCryptoCurrencies,
|
||||
unavailable = unavailableCryptoCurrencies.map { pair -> SwapCryptoCurrency(pair, emptyList()) },
|
||||
afterSearch = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tangem.domain.swap.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.express.models.ExpressError
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.express.models.ExpressRateType
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.SwapErrorResolver
|
||||
import com.tangem.domain.swap.SwapRepositoryV2
|
||||
import com.tangem.domain.swap.models.SwapQuoteModel
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Get swap quote for selected pair
|
||||
*/
|
||||
class GetSwapQuoteUseCase(
|
||||
private val swapRepositoryV2: SwapRepositoryV2,
|
||||
private val swapErrorResolver: SwapErrorResolver,
|
||||
) {
|
||||
|
||||
/**
|
||||
* @param userWallet selected user wallet
|
||||
* @param fromCryptoCurrency currency swap from
|
||||
* @param toCryptoCurrency currency swap to
|
||||
* @param fromAmount swap amount
|
||||
* @param provider swap provider
|
||||
*/
|
||||
suspend operator fun invoke(
|
||||
userWallet: UserWallet,
|
||||
fromCryptoCurrency: CryptoCurrency,
|
||||
toCryptoCurrency: CryptoCurrency,
|
||||
fromAmount: BigDecimal,
|
||||
provider: ExpressProvider,
|
||||
): Either<ExpressError, SwapQuoteModel> = Either.catch {
|
||||
swapRepositoryV2.getSwapQuote(
|
||||
userWallet = userWallet,
|
||||
fromCryptoCurrency = fromCryptoCurrency,
|
||||
toCryptoCurrency = toCryptoCurrency,
|
||||
fromAmount = fromAmount,
|
||||
provider = provider,
|
||||
rateType = ExpressRateType.Float, // todo rate type
|
||||
)
|
||||
}.mapLeft(swapErrorResolver::resolve)
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package com.tangem.domain.swap.usecase
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.SwapTransactionRepository
|
||||
import com.tangem.domain.swap.models.SwapCurrencies
|
||||
import com.tangem.domain.swap.models.SwapCurrenciesGroup
|
||||
import com.tangem.domain.swap.models.SwapDirection
|
||||
import com.tangem.domain.swap.models.getGroupWithReverse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.utils.extensions.orZero
|
||||
|
||||
/**
|
||||
* Select initial pair to swap
|
||||
*/
|
||||
class SelectInitialPairUseCase(
|
||||
private val swapTransactionRepository: SwapTransactionRepository,
|
||||
) {
|
||||
|
||||
/**
|
||||
* @param userWallet selected user wallet
|
||||
* @param primaryCryptoCurrency initial currency
|
||||
* @param secondaryCryptoCurrency selected currency
|
||||
* @param swapCurrencies list of currencies with providers
|
||||
* @param swapDirection swap direction
|
||||
*/
|
||||
suspend operator fun invoke(
|
||||
userWallet: UserWallet,
|
||||
primaryCryptoCurrency: CryptoCurrency,
|
||||
secondaryCryptoCurrency: CryptoCurrency?,
|
||||
swapCurrencies: SwapCurrencies,
|
||||
swapDirection: SwapDirection,
|
||||
): CryptoCurrencyStatus? {
|
||||
val swapCurrenciesGroup = swapCurrencies.getGroupWithReverse(swapDirection)
|
||||
return tryToGetAlreadySelectedCurrency(secondaryCryptoCurrency, swapCurrenciesGroup)
|
||||
?: tryGetFromCache(userWallet, primaryCryptoCurrency, swapCurrenciesGroup)
|
||||
?: tryGetWithMaxAmount(swapCurrenciesGroup)
|
||||
?: swapCurrenciesGroup.available.firstOrNull()?.currencyStatus
|
||||
}
|
||||
|
||||
private fun tryToGetAlreadySelectedCurrency(
|
||||
secondaryCryptoCurrency: CryptoCurrency?,
|
||||
swapCurrenciesGroup: SwapCurrenciesGroup,
|
||||
): CryptoCurrencyStatus? {
|
||||
return secondaryCryptoCurrency?.let {
|
||||
swapCurrenciesGroup.available.firstOrNull {
|
||||
secondaryCryptoCurrency.id == it.currencyStatus.currency.id
|
||||
}?.currencyStatus
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun tryGetFromCache(
|
||||
userWallet: UserWallet,
|
||||
primaryCryptoCurrency: CryptoCurrency,
|
||||
swapCurrenciesGroup: SwapCurrenciesGroup,
|
||||
): CryptoCurrencyStatus? {
|
||||
val id = swapTransactionRepository.getLastSwappedCryptoCurrencyId(userWallet.walletId) ?: return null
|
||||
|
||||
return if (id != primaryCryptoCurrency.id.value) {
|
||||
swapCurrenciesGroup.available.find { it.currencyStatus.currency.id.value == id }?.currencyStatus
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryGetWithMaxAmount(swapCurrenciesGroup: SwapCurrenciesGroup): CryptoCurrencyStatus? {
|
||||
return swapCurrenciesGroup.available.maxByOrNull {
|
||||
it.currencyStatus.value.fiatAmount.orZero()
|
||||
}?.currencyStatus
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue