Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-30 12:29:05 +05:00
parent 4c51193221
commit c8a181c829
13 changed files with 135 additions and 73 deletions

View file

@ -13,7 +13,7 @@ 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.datasource.local.preferences.utils.getObjectMap
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
@ -24,9 +24,8 @@ import com.tangem.domain.swap.models.SwapTransactionModel
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.addOrReplace
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
internal class DefaultSwapTransactionRepository(
private val appPreferencesStore: AppPreferencesStore,
@ -95,36 +94,34 @@ internal class DefaultSwapTransactionRepository(
}
}
override suspend fun getTransactions(
override 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
)
}
): Flow<List<SwapTransactionListModel>?> = combine(
flow = appPreferencesStore.getObjectList<SwapTransactionListDTO>(
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
),
flow2 = appPreferencesStore.getObjectMap<SwapStatusDTO>(
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
),
) { savedTransactions, txStatuses ->
val currencyTxs = savedTransactions
?.filter {
it.userWalletId == userWallet.walletId.stringValue &&
(
it.toCryptoCurrencyId == cryptoCurrencyId.value ||
it.fromCryptoCurrencyId == cryptoCurrencyId.value
)
}
currencyTxs?.mapNotNull {
listConverter.convertBack(
value = it,
userWallet = userWallet,
txStatuses = txStatuses,
)
}
}.flowOn(dispatchers.io)
currencyTxs?.mapNotNull {
listConverter.convertBack(
value = it,
userWallet = userWallet,
txStatuses = txStatuses,
)
}
}
}.flowOn(dispatchers.default)
override suspend fun removeTransaction(
userWalletId: UserWalletId,
@ -188,7 +185,7 @@ internal class DefaultSwapTransactionRepository(
)
val updatesMap = savedMap.toMutableMap()
updatesMap[txId] = savedStatusConverter.convertBack(
updatesMap[txId] = savedStatusConverter.convert(
status.copy(
refundTokensResponse = refundTokenCurrency?.let {
userTokensResponseFactory.createResponseToken(refundTokenCurrency)

View file

@ -6,9 +6,9 @@ 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> {
internal class SavedSwapStatusConverter : TwoWayConverter<SwapStatusModel, SwapStatusDTO> {
override fun convert(value: SwapStatusDTO) = SwapStatusModel(
override fun convertBack(value: SwapStatusDTO) = SwapStatusModel(
providerId = value.providerId,
status = SwapStatus.entries.firstOrNull {
it.name.lowercase() == value.status?.name?.lowercase()
@ -22,7 +22,7 @@ internal class SavedSwapStatusConverter : TwoWayConverter<SwapStatusDTO, SwapSta
averageDuration = value.averageDuration,
)
override fun convertBack(value: SwapStatusModel) = SwapStatusDTO(
override fun convert(value: SwapStatusModel) = SwapStatusDTO(
providerId = value.providerId,
status = SavedSwapStatus.entries.firstOrNull {
it.name.lowercase() == value.status?.name?.lowercase()

View file

@ -3,8 +3,10 @@ package com.tangem.data.swap.converter.transaction
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.swap.models.SwapStatusDTO
import com.tangem.data.swap.models.SwapTransactionDTO
import com.tangem.data.swap.models.SwapTxTypeDTO
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.swap.models.SwapTransactionModel
import com.tangem.domain.swap.models.SwapTxType
import com.tangem.utils.converter.TwoWayConverter
internal class SavedSwapTransactionConverter(
@ -21,7 +23,10 @@ internal class SavedSwapTransactionConverter(
fromCryptoAmount = value.fromCryptoAmount,
toCryptoAmount = value.toCryptoAmount,
provider = value.provider,
status = value.status,
status = value.status?.let(statusConverter::convert),
swapTxType = SwapTxTypeDTO.entries.firstOrNull {
it.name.lowercase() == value.swapTxType?.name?.lowercase()
},
)
override fun convertBack(value: SwapTransactionDTO) = SwapTransactionModel(
@ -30,7 +35,10 @@ internal class SavedSwapTransactionConverter(
fromCryptoAmount = value.fromCryptoAmount,
toCryptoAmount = value.toCryptoAmount,
provider = value.provider,
status = value.status,
status = value.status?.let(statusConverter::convertBack),
swapTxType = SwapTxType.entries.firstOrNull {
it.name.lowercase() == value.swapTxType?.name?.lowercase()
},
)
fun convertBack(
@ -53,7 +61,10 @@ internal class SavedSwapTransactionConverter(
fromCryptoAmount = value.fromCryptoAmount,
toCryptoAmount = value.toCryptoAmount,
provider = value.provider,
status = statusWithRefundCurrency?.let(statusConverter::convert),
status = statusWithRefundCurrency?.let(statusConverter::convertBack),
swapTxType = SwapTxType.entries.firstOrNull {
it.name.lowercase() == value.swapTxType?.name?.lowercase()
},
)
}
}

View file

@ -4,7 +4,6 @@ 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)
@ -36,5 +35,7 @@ internal data class SwapTransactionDTO(
@Json(name = "provider")
val provider: ExpressProvider,
@Json(name = "status")
val status: SwapStatusModel? = null,
val status: SwapStatusDTO? = null,
@Json(name = "swapTxType")
val swapTxType: SwapTxTypeDTO? = SwapTxTypeDTO.Swap,
)

View file

@ -0,0 +1,13 @@
package com.tangem.data.swap.models
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = false)
internal enum class SwapTxTypeDTO {
@Json(name = "Swap")
Swap,
@Json(name = "SendWithSwap")
SendWithSwap,
}

View file

@ -26,4 +26,5 @@ data class SwapTransactionModel(
val toCryptoAmount: BigDecimal,
val provider: ExpressProvider,
val status: SwapStatusModel? = null,
val swapTxType: SwapTxType? = SwapTxType.Swap,
)

View file

@ -0,0 +1,6 @@
package com.tangem.domain.swap.models
enum class SwapTxType {
Swap,
SendWithSwap,
}

View file

@ -1,11 +1,11 @@
package com.tangem.domain.swap
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
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.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.coroutines.flow.Flow
/**
@ -34,7 +34,7 @@ interface SwapTransactionRepository {
* @param userWallet selected user wallet
* @param cryptoCurrencyId transactions for specific crypto currency
*/
suspend fun getTransactions(
fun getTransactions(
userWallet: UserWallet,
cryptoCurrencyId: CryptoCurrency.ID,
): Flow<List<SwapTransactionListModel>?>

View file

@ -8,10 +8,7 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.swap.SwapErrorResolver
import com.tangem.domain.swap.SwapRepositoryV2
import com.tangem.domain.swap.SwapTransactionRepository
import com.tangem.domain.swap.models.SwapDataTransactionModel
import com.tangem.domain.swap.models.SwapStatus
import com.tangem.domain.swap.models.SwapStatusModel
import com.tangem.domain.swap.models.SwapTransactionModel
import com.tangem.domain.swap.models.*
@Suppress("LongParameterList")
class SwapTransactionSentUseCase(
@ -28,15 +25,8 @@ class SwapTransactionSentUseCase(
provider: ExpressProvider,
txHash: String,
timestamp: Long,
swapTxType: SwapTxType,
) = Either.catch {
swapRepositoryV2.swapTransactionSent(
userWallet = userWallet,
fromCryptoCurrencyStatus = fromCryptoCurrencyStatus,
toAddress = swapDataTransactionModel.txTo,
txId = swapDataTransactionModel.txId,
txHash = txHash,
txExtraId = swapDataTransactionModel.txExtraId,
)
if (provider.type.shouldStoreSwapTransaction()) {
swapTransactionRepository.storeTransaction(
userWalletId = userWallet.walletId,
@ -56,12 +46,22 @@ class SwapTransactionSentUseCase(
txExternalId = (swapDataTransactionModel as? SwapDataTransactionModel.CEX)?.externalTxId,
averageDuration = null,
),
swapTxType = swapTxType,
),
)
}
swapTransactionRepository.storeLastSwappedCryptoCurrencyId(
userWalletId = userWallet.walletId,
cryptoCurrencyId = toCryptoCurrencyStatus.currency.id,
)
swapRepositoryV2.swapTransactionSent(
userWallet = userWallet,
fromCryptoCurrencyStatus = fromCryptoCurrencyStatus,
toAddress = swapDataTransactionModel.txTo,
txId = swapDataTransactionModel.txId,
txHash = txHash,
txExtraId = swapDataTransactionModel.txExtraId,
)
}.mapLeft(swapErrorResolver::resolve)
}

View file

@ -9,6 +9,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.swap.models.SwapDataModel
import com.tangem.domain.swap.models.SwapDataTransactionModel
import com.tangem.domain.swap.models.SwapTxType
import com.tangem.domain.swap.usecase.GetSwapDataUseCase
import com.tangem.domain.swap.usecase.SwapTransactionSentUseCase
import com.tangem.domain.transaction.error.SendTransactionError
@ -158,6 +159,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
provider = provider,
txHash = txHash,
timestamp = timestamp,
swapTxType = SwapTxType.SendWithSwap,
)
onSendSuccess(txHash, timestamp, swapData)
},

View file

@ -92,21 +92,37 @@ internal class DefaultSwapTransactionRepository(
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
),
) { savedTransactions, txStatuses ->
val currencyTxs = savedTransactions?.filter {
it.userWalletId == userWallet.walletId.stringValue &&
(
it.toCryptoCurrencyId == cryptoCurrencyId.value ||
it.fromCryptoCurrencyId == cryptoCurrencyId.value
)
val currencyToTxs = savedTransactions?.filter {
val isUserWallet = it.userWalletId == userWallet.walletId.stringValue
val toCurrency = it.toCryptoCurrencyId == cryptoCurrencyId.value
isUserWallet && toCurrency
}
currencyTxs?.mapNotNull {
val currencyFromTxs = savedTransactions?.filter {
val isUserWallet = it.userWalletId == userWallet.walletId.stringValue
val fromCurrency = it.fromCryptoCurrencyId == cryptoCurrencyId.value
isUserWallet && fromCurrency
}
val toTxs = currencyToTxs?.mapNotNull {
converter.convertBack(
value = it,
userWallet = userWallet,
txStatuses = txStatuses,
onFilter = { it.swapTxTypeDTO == SwapTxTypeDTO.Swap },
)
}.orEmpty()
val fromTxs = currencyFromTxs?.mapNotNull {
converter.convertBack(
value = it,
userWallet = userWallet,
txStatuses = txStatuses,
)
}
}.orEmpty()
fromTxs + toTxs
}
.flowOn(dispatchers.default)
}

View file

@ -34,6 +34,7 @@ internal class SavedSwapTransactionListConverter(
value: SavedSwapTransactionListModelInner,
userWallet: UserWallet,
txStatuses: Map<String, ExchangeStatusModel>,
onFilter: (SavedSwapTransactionModel) -> Boolean = { true },
): SavedSwapTransactionListModel? {
val fromToken = value.fromTokensResponse
val toToken = value.toTokensResponse
@ -50,17 +51,19 @@ internal class SavedSwapTransactionListConverter(
) ?: return null
return SavedSwapTransactionListModel(
transactions = value.transactions.map { tx ->
val status = txStatuses[tx.txId]
val refundCurrency = status?.refundTokensResponse?.let { id ->
responseCryptoCurrenciesFactory.createCurrency(
responseToken = id,
userWallet = userWallet,
)
}
val statusWithRefundCurrency = status?.copy(refundCurrency = refundCurrency)
tx.copy(status = statusWithRefundCurrency)
},
transactions = value.transactions
.filter(onFilter)
.map { tx ->
val status = txStatuses[tx.txId]
val refundCurrency = status?.refundTokensResponse?.let { id ->
responseCryptoCurrenciesFactory.createCurrency(
responseToken = id,
userWallet = userWallet,
)
}
val statusWithRefundCurrency = status?.copy(refundCurrency = refundCurrency)
tx.copy(status = statusWithRefundCurrency)
},
userWalletId = value.userWalletId,
fromCryptoCurrencyId = value.fromCryptoCurrencyId,
toCryptoCurrencyId = value.toCryptoCurrencyId,

View file

@ -50,4 +50,16 @@ data class SavedSwapTransactionModel(
val provider: SwapProvider,
@Json(name = "status")
val status: ExchangeStatusModel? = null,
)
@Json(name = "swapTxType")
val swapTxTypeDTO: SwapTxTypeDTO? = SwapTxTypeDTO.Swap,
)
// TODO refactor to use separate models to store
@JsonClass(generateAdapter = false)
enum class SwapTxTypeDTO {
@Json(name = "Swap")
Swap,
@Json(name = "SendWithSwap")
SendWithSwap,
}