Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-30 17:38:10 +05:00
parent 4be3782a0a
commit 2ea3f2ca9d
11 changed files with 98 additions and 8 deletions

View file

@ -35,6 +35,7 @@ dependencies {
implementation(projects.domain.networks)
implementation(projects.domain.staking.models)
implementation(projects.domain.staking)
implementation(projects.domain.account)
/** Tangem SDK */
implementation(tangemDeps.blockchain) {

View file

@ -15,6 +15,9 @@ 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.getObjectMap
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.supplier.MultiAccountListSupplier
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
@ -27,6 +30,7 @@ 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.flowOf
import kotlinx.coroutines.flow.flowOn
internal class DefaultSwapTransactionRepository(
@ -34,6 +38,8 @@ internal class DefaultSwapTransactionRepository(
private val responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
private val networkFactory: NetworkFactory,
private val dispatchers: CoroutineDispatcherProvider,
private val multiAccountListSupplier: MultiAccountListSupplier,
private val accountsFeatureToggles: AccountsFeatureToggles,
) : SwapTransactionRepository {
private val listConverter by lazy(LazyThreadSafetyMode.NONE) {
@ -54,13 +60,15 @@ internal class DefaultSwapTransactionRepository(
userWalletId: UserWalletId,
fromCryptoCurrency: CryptoCurrency,
toCryptoCurrency: CryptoCurrency,
fromAccount: Account.CryptoPortfolio?,
toAccount: Account.CryptoPortfolio?,
transaction: SwapTransactionModel,
) {
transaction.status?.let {
storeTransactionState(
txId = transaction.txId,
status = it,
accountWithCurrency = null,
accountWithCurrency = fromAccount?.accountId to fromCryptoCurrency,
)
}
appPreferencesStore.editData { mutablePreferences ->
@ -87,12 +95,16 @@ internal class DefaultSwapTransactionRepository(
userWalletId = userWalletId,
fromCryptoCurrency = fromCryptoCurrency,
toCryptoCurrency = toCryptoCurrency,
fromAccount = fromAccount,
toAccount = toAccount,
transactions = tokenTransactions,
) ?: listOf(
listConverter.default(
userWalletId = userWalletId,
fromCryptoCurrency = fromCryptoCurrency,
toCryptoCurrency = toCryptoCurrency,
fromAccount = fromAccount,
toAccount = toAccount,
tokenTransactions = listOf(converter.convert(transaction)),
),
),
@ -110,7 +122,12 @@ internal class DefaultSwapTransactionRepository(
flow2 = appPreferencesStore.getObjectMap<SwapStatusDTO>(
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
),
) { savedTransactions, txStatuses ->
flow3 = if (accountsFeatureToggles.isFeatureEnabled) {
multiAccountListSupplier()
} else {
flowOf(emptyList())
},
) { savedTransactions, txStatuses, multiAccountList ->
val currencyTxs = savedTransactions
?.filter {
it.userWalletId == userWallet.walletId.stringValue &&
@ -123,6 +140,7 @@ internal class DefaultSwapTransactionRepository(
currencyTxs?.mapNotNull {
listConverter.convertBack(
value = it,
multiAccountList = multiAccountList,
userWallet = userWallet,
txStatuses = txStatuses,
)
@ -221,10 +239,13 @@ internal class DefaultSwapTransactionRepository(
fromCryptoCurrencyId == fromCurrencyId.value
}
@Suppress("LongParameterList")
private fun List<SwapTransactionListDTO>.updateList(
userWalletId: UserWalletId,
fromCryptoCurrency: CryptoCurrency,
toCryptoCurrency: CryptoCurrency,
fromAccount: Account.CryptoPortfolio?,
toAccount: Account.CryptoPortfolio?,
transactions: List<SwapTransactionDTO>,
): List<SwapTransactionListDTO> {
return addOrReplace(
@ -232,6 +253,8 @@ internal class DefaultSwapTransactionRepository(
userWalletId = userWalletId,
fromCryptoCurrency = fromCryptoCurrency,
toCryptoCurrency = toCryptoCurrency,
fromAccount = fromAccount,
toAccount = toAccount,
tokenTransactions = transactions,
),
predicate = {

View file

@ -9,6 +9,9 @@ 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.api.tangemTech.models.UserTokensResponse
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
@ -32,11 +35,11 @@ internal class SavedSwapTransactionListConverter(
toCryptoCurrencyId = value.toCryptoCurrencyId,
fromTokensResponse = userTokensResponseFactory.createResponseToken(
currency = value.fromCryptoCurrency,
accountId = null,
accountId = value.fromAccount?.accountId,
),
toTokensResponse = userTokensResponseFactory.createResponseToken(
currency = value.toCryptoCurrency,
accountId = null,
accountId = value.toAccount?.accountId,
),
transactions = savedSwapTransactionConverter.convertList(value.transactions),
)
@ -45,9 +48,12 @@ internal class SavedSwapTransactionListConverter(
value: SwapTransactionListDTO,
userWallet: UserWallet,
txStatuses: Map<String, SwapStatusDTO>,
multiAccountList: List<AccountList>,
): SwapTransactionListModel? {
val fromToken = value.fromTokensResponse
val toToken = value.toTokensResponse
val fromDerivationIndex = fromToken?.getDerivationIndex()
val toDerivationIndex = toToken?.getDerivationIndex()
return if (fromToken == null || toToken == null) {
null
} else {
@ -73,14 +79,25 @@ internal class SavedSwapTransactionListConverter(
toCryptoCurrencyId = value.toCryptoCurrencyId,
fromCryptoCurrency = fromCryptoCurrency,
toCryptoCurrency = toCryptoCurrency,
fromAccount = getAccountByDerivationIndex(
multiAccountList = multiAccountList,
derivationIndex = fromDerivationIndex,
),
toAccount = getAccountByDerivationIndex(
multiAccountList = multiAccountList,
derivationIndex = toDerivationIndex,
),
)
}
}
@Suppress("LongParameterList")
fun default(
userWalletId: UserWalletId,
fromCryptoCurrency: CryptoCurrency,
toCryptoCurrency: CryptoCurrency,
fromAccount: Account.CryptoPortfolio?,
toAccount: Account.CryptoPortfolio?,
tokenTransactions: List<SwapTransactionDTO>,
) = SwapTransactionListDTO(
userWalletId = userWalletId.stringValue,
@ -88,9 +105,12 @@ internal class SavedSwapTransactionListConverter(
toCryptoCurrencyId = toCryptoCurrency.id.value,
fromTokensResponse = userTokensResponseFactory.createResponseToken(
currency = fromCryptoCurrency,
accountId = null,
accountId = fromAccount?.accountId,
),
toTokensResponse = userTokensResponseFactory.createResponseToken(
currency = toCryptoCurrency,
accountId = toAccount?.accountId,
),
toTokensResponse = userTokensResponseFactory.createResponseToken(currency = toCryptoCurrency, accountId = null),
transactions = tokenTransactions,
)
@ -108,7 +128,24 @@ internal class SavedSwapTransactionListConverter(
blockchain = blockchain,
extraDerivationPath = token.derivationPath,
userWallet = userWallet,
accountIndex = token.accountId?.toIntOrNull()?.let { DerivationIndex(it).getOrNull() },
)
}
}
private fun getAccountByDerivationIndex(
multiAccountList: List<AccountList>,
derivationIndex: DerivationIndex?,
): Account.CryptoPortfolio? {
if (derivationIndex == null) return null
return multiAccountList.asSequence().firstNotNullOfOrNull { accountList ->
accountList.accounts.asSequence().filterIsInstance<Account.CryptoPortfolio>()
.firstOrNull { it.derivationIndex == derivationIndex }
}
}
private fun UserTokensResponse.Token.getDerivationIndex(): DerivationIndex? {
return accountId?.toIntOrNull()?.let { DerivationIndex(it).getOrNull() }
}
}

View file

@ -12,6 +12,8 @@ import com.tangem.datasource.api.express.models.response.ExpressErrorResponse
import com.tangem.datasource.crypto.DataSignatureVerifier
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.supplier.MultiAccountListSupplier
import com.tangem.domain.express.ExpressRepository
import com.tangem.domain.quotes.single.SingleQuoteStatusFetcher
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
@ -68,12 +70,16 @@ internal object SwapDataModule {
appPreferencesStore: AppPreferencesStore,
responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
networkFactory: NetworkFactory,
multiAccountListSupplier: MultiAccountListSupplier,
accountsFeatureToggles: AccountsFeatureToggles,
dispatchers: CoroutineDispatcherProvider,
): SwapTransactionRepository {
return DefaultSwapTransactionRepository(
appPreferencesStore = appPreferencesStore,
responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory,
networkFactory = networkFactory,
multiAccountListSupplier = multiAccountListSupplier,
accountsFeatureToggles = accountsFeatureToggles,
dispatchers = dispatchers,
)
}

View file

@ -1,6 +1,7 @@
package com.tangem.domain.swap.models
import com.tangem.domain.express.models.ExpressProvider
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import java.math.BigDecimal
@ -13,6 +14,8 @@ data class SwapTransactionListModel(
val toCryptoCurrencyId: String,
val fromCryptoCurrency: CryptoCurrency,
val toCryptoCurrency: CryptoCurrency,
val fromAccount: Account.CryptoPortfolio?,
val toAccount: Account.CryptoPortfolio?,
val transactions: List<SwapTransactionModel>,
)

View file

@ -1,5 +1,6 @@
package com.tangem.domain.swap
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
@ -20,12 +21,17 @@ interface SwapTransactionRepository {
* @param userWalletId selected user wallet id
* @param fromCryptoCurrency currency swap from
* @param toCryptoCurrency currency swap to
* @param fromAccount account swap from
* @param toAccount account swap to
* @param transaction swap transaction
*/
@Suppress("LongParameterList")
suspend fun storeTransaction(
userWalletId: UserWalletId,
fromCryptoCurrency: CryptoCurrency,
toCryptoCurrency: CryptoCurrency,
fromAccount: Account.CryptoPortfolio?,
toAccount: Account.CryptoPortfolio?,
transaction: SwapTransactionModel,
)

View file

@ -3,6 +3,7 @@ package com.tangem.domain.swap.usecase
import arrow.core.Either
import com.tangem.domain.express.models.ExpressProvider
import com.tangem.domain.express.models.ExpressProviderType.Companion.shouldStoreSwapTransaction
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.swap.SwapErrorResolver
@ -21,6 +22,8 @@ class SwapTransactionSentUseCase(
userWallet: UserWallet,
fromCryptoCurrencyStatus: CryptoCurrencyStatus,
toCryptoCurrencyStatus: CryptoCurrencyStatus,
fromAccount: Account.CryptoPortfolio?,
toAccount: Account.CryptoPortfolio?,
swapDataTransactionModel: SwapDataTransactionModel,
provider: ExpressProvider,
txHash: String,
@ -32,6 +35,8 @@ class SwapTransactionSentUseCase(
userWalletId = userWallet.walletId,
fromCryptoCurrency = fromCryptoCurrencyStatus.currency,
toCryptoCurrency = toCryptoCurrencyStatus.currency,
fromAccount = fromAccount,
toAccount = toAccount,
transaction = SwapTransactionModel(
txId = swapDataTransactionModel.txId,
provider = provider,

View file

@ -6,9 +6,9 @@ import androidx.compose.ui.text.input.KeyboardType
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.features.send.v2.impl.R
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationTextFieldUM
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.v2.impl.R
import com.tangem.utils.transformer.Transformer
internal class SendDestinationInitialStateTransformer(

View file

@ -2,6 +2,7 @@ package com.tangem.features.swap.v2.impl.common
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.express.models.ExpressRateType
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
@ -16,6 +17,7 @@ internal data class ConfirmData(
val feeError: GetFeeError?,
val fromCryptoCurrencyStatus: CryptoCurrencyStatus?,
val toCryptoCurrencyStatus: CryptoCurrencyStatus?,
val fromAccount: Account.CryptoPortfolio?,
val quote: SwapQuoteUM?,
val rateType: ExpressRateType?,
)

View file

@ -135,6 +135,7 @@ internal class SendWithSwapConfirmModel @Inject constructor(
onDirect = { secondaryCurrencyStatus },
onReverse = { primaryCurrencyStatus },
),
fromAccount = params.accountFlow.value,
quote = amountUM?.selectedQuote,
rateType = amountUM?.swapRateType,
)

View file

@ -6,6 +6,7 @@ import com.tangem.domain.express.models.ExpressError
import com.tangem.domain.express.models.ExpressOperationType
import com.tangem.domain.express.models.ExpressProvider
import com.tangem.domain.express.models.ExpressProviderType
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.swap.models.SwapDataModel
@ -64,7 +65,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
}
}
suspend fun onCexTransaction(
private suspend fun onCexTransaction(
confirmData: ConfirmData,
isAmountSubtractAvailable: Boolean,
onSendSuccess: (String, Long, SwapDataModel) -> Unit,
@ -74,6 +75,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
) {
val fromStatus = confirmData.fromCryptoCurrencyStatus ?: return
val toStatus = confirmData.toCryptoCurrencyStatus ?: return
val fromAccount = confirmData.fromAccount
val provider = confirmData.quote?.provider ?: return
val rateType = confirmData.rateType ?: return
val amountValue = confirmData.enteredAmount ?: return
@ -102,6 +104,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
createAndSendCexTransaction(
fromAmount = fromAmount,
fromStatus = fromStatus,
fromAccount = fromAccount,
toStatus = toStatus,
fee = confirmData.fee,
provider = provider,
@ -116,6 +119,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
fromAmount: BigDecimal,
fromStatus: CryptoCurrencyStatus,
toStatus: CryptoCurrencyStatus,
fromAccount: Account.CryptoPortfolio?,
fee: Fee,
provider: ExpressProvider,
swapData: SwapDataModel,
@ -160,6 +164,8 @@ internal class SwapTransactionSender @AssistedInject constructor(
userWallet = userWallet,
fromCryptoCurrencyStatus = fromStatus,
toCryptoCurrencyStatus = toStatus,
fromAccount = fromAccount,
toAccount = null,
swapDataTransactionModel = swapTransaction,
provider = provider,
txHash = txHash,