Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-27 13:11:06 +05:00
parent d4e8d3f45f
commit 0334e93ec2
13 changed files with 145 additions and 118 deletions

View file

@ -8,6 +8,7 @@ import com.tangem.data.common.network.NetworkFactory
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import timber.log.Timber
import javax.inject.Inject
@ -47,11 +48,35 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
blockchain = blockchain.getTestnetVersion() ?: blockchain
}
val network = networkFactory.create(
blockchain = blockchain,
extraDerivationPath = responseToken.derivationPath,
userWallet = userWallet,
) ?: return null
return createCurrency(responseToken = responseToken, userWallet = userWallet, network = network)
}
fun createCurrency(
responseToken: UserTokensResponse.Token,
userWallet: UserWallet,
network: Network,
): CryptoCurrency? {
var blockchain = Blockchain.fromNetworkId(responseToken.networkId)
if (blockchain == null || blockchain == Blockchain.Unknown) {
Timber.e("Unable to find a blockchain with the network ID: ${responseToken.networkId}")
return null
}
if (userWallet is UserWallet.Cold && userWallet.scanResponse.cardTypesResolver.isTestCard()) {
blockchain = blockchain.getTestnetVersion() ?: blockchain
}
val sdkToken = createSdkToken(responseToken)
return if (sdkToken == null) {
createCoin(blockchain, responseToken, userWallet)
createCoin(blockchain, responseToken, network)
} else {
createToken(blockchain, sdkToken, responseToken.derivationPath, userWallet)
createToken(blockchain, sdkToken, network)
}
}
@ -70,14 +95,8 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
private fun createCoin(
blockchain: Blockchain,
responseToken: UserTokensResponse.Token,
userWallet: UserWallet,
network: Network,
): CryptoCurrency.Coin? {
val network = networkFactory.create(
blockchain = blockchain,
extraDerivationPath = responseToken.derivationPath,
userWallet = userWallet,
) ?: return null
return CryptoCurrency.Coin(
id = getCoinId(network, blockchain.toCoinId()),
network = network,
@ -101,18 +120,7 @@ class ResponseCryptoCurrenciesFactory @Inject constructor(
}
}
private fun createToken(
blockchain: Blockchain,
sdkToken: Token,
responseDerivationPath: String?,
userWallet: UserWallet,
): CryptoCurrency.Token? {
val network = networkFactory.create(
blockchain = blockchain,
extraDerivationPath = responseDerivationPath,
userWallet = userWallet,
) ?: return null
private fun createToken(blockchain: Blockchain, sdkToken: Token, network: Network): CryptoCurrency.Token? {
val id = getTokenId(network, sdkToken)
return CryptoCurrency.Token(

View file

@ -67,6 +67,24 @@ class NetworkFactory @Inject constructor(
)
}
/**
* Create
*
* @param blockchain blockchain
* @param derivationPath derivation path
* @param userWallet user wallet
*/
fun create(blockchain: Blockchain, derivationPath: Network.DerivationPath, userWallet: UserWallet): Network? {
return create(
blockchain = blockchain,
derivationPath = derivationPath,
canHandleTokens = userWallet.canHandleToken(
blockchain = blockchain,
excludedBlockchains = excludedBlockchains,
),
)
}
/**
* Create
*

View file

@ -2,6 +2,7 @@ package com.tangem.data.swap
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.common.currency.UserTokensResponseFactory
import com.tangem.data.common.network.NetworkFactory
import com.tangem.data.swap.converter.transaction.SavedSwapStatusConverter
import com.tangem.data.swap.converter.transaction.SavedSwapTransactionConverter
import com.tangem.data.swap.converter.transaction.SavedSwapTransactionListConverter
@ -31,11 +32,15 @@ import kotlinx.coroutines.flow.flowOn
internal class DefaultSwapTransactionRepository(
private val appPreferencesStore: AppPreferencesStore,
private val responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
private val networkFactory: NetworkFactory,
private val dispatchers: CoroutineDispatcherProvider,
) : SwapTransactionRepository {
private val listConverter by lazy(LazyThreadSafetyMode.NONE) {
SavedSwapTransactionListConverter(responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory)
SavedSwapTransactionListConverter(
responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory,
networkFactory = networkFactory,
)
}
private val converter by lazy(LazyThreadSafetyMode.NONE) {
SavedSwapTransactionConverter(responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory)
@ -124,52 +129,25 @@ internal class DefaultSwapTransactionRepository(
}
}.flowOn(dispatchers.default)
override suspend fun removeTransaction(
userWalletId: UserWalletId,
fromCryptoCurrency: CryptoCurrency,
toCryptoCurrency: CryptoCurrency,
txId: String,
) {
override suspend fun removeTransaction(userWalletId: UserWalletId, 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 }
?.asSequence()
?.map {
it.copy(transactions = it.transactions.filterNot { it.txId == txId })
}?.filterNot { it.transactions.isEmpty() }
?.toList()
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()) {
if (tokenTransactions.isNullOrEmpty()) {
mutablePreferences.remove(key = PreferencesKeys.SWAP_TRANSACTIONS_KEY)
} else {
mutablePreferences.setObject(
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
value = editedList,
value = tokenTransactions,
)
}
}

View file

@ -1,11 +1,16 @@
package com.tangem.data.swap.converter.transaction
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.common.currency.UserTokensResponseFactory
import com.tangem.data.common.network.NetworkFactory
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.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.swap.models.SwapTransactionListModel
@ -13,6 +18,7 @@ import com.tangem.utils.converter.Converter
internal class SavedSwapTransactionListConverter(
private val responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
private val networkFactory: NetworkFactory,
) : Converter<SwapTransactionListModel, SwapTransactionListDTO> {
private val userTokensResponseFactory = UserTokensResponseFactory()
@ -45,13 +51,17 @@ internal class SavedSwapTransactionListConverter(
return if (fromToken == null || toToken == null) {
null
} else {
val fromNetwork = createSwapTransactionNetwork(fromToken, userWallet) ?: return null
val fromCryptoCurrency = responseCryptoCurrenciesFactory.createCurrency(
responseToken = fromToken,
userWallet = userWallet,
network = fromNetwork,
) ?: return null
val toNetwork = createSwapTransactionNetwork(fromToken, userWallet) ?: return null
val toCryptoCurrency = responseCryptoCurrenciesFactory.createCurrency(
responseToken = toToken,
userWallet = userWallet,
network = toNetwork,
) ?: return null
return SwapTransactionListModel(
@ -83,4 +93,22 @@ internal class SavedSwapTransactionListConverter(
toTokensResponse = userTokensResponseFactory.createResponseToken(currency = toCryptoCurrency, accountId = null),
transactions = tokenTransactions,
)
private fun createSwapTransactionNetwork(token: UserTokensResponse.Token, userWallet: UserWallet): Network? {
val blockchain = Blockchain.fromNetworkId(token.networkId) ?: return null
return if (token.derivationPath == null) {
networkFactory.create(
blockchain = blockchain,
derivationPath = Network.DerivationPath.None,
userWallet = userWallet,
)
} else {
networkFactory.create(
blockchain = blockchain,
extraDerivationPath = token.derivationPath,
userWallet = userWallet,
)
}
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.data.swap.di
import com.squareup.moshi.Moshi
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.common.network.NetworkFactory
import com.tangem.data.express.converter.ExpressErrorConverter
import com.tangem.data.swap.DefaultSwapErrorResolver
import com.tangem.data.swap.DefaultSwapRepositoryV2
@ -66,11 +67,13 @@ internal object SwapDataModule {
fun provideSwapTransactionRepository(
appPreferencesStore: AppPreferencesStore,
responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
networkFactory: NetworkFactory,
dispatchers: CoroutineDispatcherProvider,
): SwapTransactionRepository {
return DefaultSwapTransactionRepository(
appPreferencesStore = appPreferencesStore,
responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory,
networkFactory = networkFactory,
dispatchers = dispatchers,
)
}

View file

@ -108,8 +108,8 @@ internal class DefaultYieldSupplyRepository(
statusMap[cryptoCurrency.id.value] = yieldSupplyEnterStatus
}
override fun getTokenProtocolStatus(cryptoCurrencyToken: CryptoCurrency): YieldSupplyEnterStatus? {
return statusMap[cryptoCurrencyToken.id.value]
override fun getTokenProtocolStatus(cryptoCurrency: CryptoCurrency): YieldSupplyEnterStatus? {
return statusMap[cryptoCurrency.id.value]
}
private fun List<YieldMarketToken>.enrichNetworkIds(): List<YieldMarketToken> {

View file

@ -44,16 +44,9 @@ interface SwapTransactionRepository {
* 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,
)
suspend fun removeTransaction(userWalletId: UserWalletId, txId: String)
/**
* Update swap transaction

View file

@ -65,13 +65,10 @@ interface YieldSupplyRepository {
* for the definitive protocol status to be fetched from the network. This information
* is transient and not intended to be persisted across app restarts.
*
* @param cryptoCurrencyToken the currency or token the action relates to
* @param cryptoCurrency the currency or token the action relates to
* @param yieldSupplyEnterStatus the last action intent: [YieldSupplyEnterStatus.Enter] or [YieldSupplyEnterStatus.Exit]
*/
suspend fun saveTokenProtocolStatus(
cryptoCurrencyToken: CryptoCurrency,
yieldSupplyEnterStatus: YieldSupplyEnterStatus,
)
suspend fun saveTokenProtocolStatus(cryptoCurrency: CryptoCurrency, yieldSupplyEnterStatus: YieldSupplyEnterStatus)
/**
* Get the last saved user-initiated yield protocol action for the given currency, if any.
@ -79,8 +76,8 @@ interface YieldSupplyRepository {
* Used to determine whether the UI should display an intermediate "processing" state
* until the protocol status retrieved from backend reflects the change.
*
* @param cryptoCurrencyToken the currency or token to query
* @param cryptoCurrency the currency or token to query
* @return the last action intent or null if nothing has been recorded
*/
fun getTokenProtocolStatus(cryptoCurrencyToken: CryptoCurrency): YieldSupplyEnterStatus?
fun getTokenProtocolStatus(cryptoCurrency: CryptoCurrency): YieldSupplyEnterStatus?
}

View file

@ -2,6 +2,7 @@ package com.tangem.feature.swap
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.common.currency.UserTokensResponseFactory
import com.tangem.data.common.network.NetworkFactory
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectList
@ -24,10 +25,14 @@ internal class DefaultSwapTransactionRepository(
private val appPreferencesStore: AppPreferencesStore,
private val dispatchers: CoroutineDispatcherProvider,
responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
networkFactory: NetworkFactory,
) : SwapTransactionRepository {
private val converter by lazy(LazyThreadSafetyMode.NONE) {
SavedSwapTransactionListConverter(responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory)
SavedSwapTransactionListConverter(
responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory,
networkFactory = networkFactory,
)
}
private val userTokensResponseFactory = UserTokensResponseFactory()
@ -128,52 +133,25 @@ internal class DefaultSwapTransactionRepository(
.flowOn(dispatchers.default)
}
override suspend fun removeTransaction(
userWalletId: UserWalletId,
fromCryptoCurrency: CryptoCurrency,
toCryptoCurrency: CryptoCurrency,
txId: String,
) {
override suspend fun removeTransaction(userWalletId: UserWalletId, txId: String) {
clearTransactionsStatuses(txId = txId)
appPreferencesStore.editData { mutablePreferences ->
val savedList: List<SavedSwapTransactionListModelInner>? = 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 }
?.asSequence()
?.map {
it.copy(transactions = it.transactions.filterNot { it.txId == txId })
}?.filterNot { it.transactions.isEmpty() }
?.toList()
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()) {
if (tokenTransactions.isNullOrEmpty()) {
mutablePreferences.remove(key = PreferencesKeys.SWAP_TRANSACTIONS_KEY)
} else {
mutablePreferences.setObject(
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
value = editedList,
value = tokenTransactions,
)
}
}

View file

@ -1,8 +1,13 @@
package com.tangem.feature.swap.converters
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.common.currency.UserTokensResponseFactory
import com.tangem.data.common.network.NetworkFactory
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel
@ -13,6 +18,7 @@ import com.tangem.utils.converter.Converter
internal class SavedSwapTransactionListConverter(
private val responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
private val networkFactory: NetworkFactory,
) : Converter<SavedSwapTransactionListModel, SavedSwapTransactionListModelInner> {
private val userTokensResponseFactory = UserTokensResponseFactory()
@ -43,13 +49,17 @@ internal class SavedSwapTransactionListConverter(
return if (fromToken == null || toToken == null) {
null
} else {
val fromNetwork = createSwapTransactionNetwork(fromToken, userWallet) ?: return null
val fromCryptoCurrency = responseCryptoCurrenciesFactory.createCurrency(
responseToken = fromToken,
userWallet = userWallet,
network = fromNetwork,
) ?: return null
val toNetwork = createSwapTransactionNetwork(fromToken, userWallet) ?: return null
val toCryptoCurrency = responseCryptoCurrenciesFactory.createCurrency(
responseToken = toToken,
userWallet = userWallet,
network = toNetwork,
) ?: return null
return SavedSwapTransactionListModel(
@ -91,4 +101,22 @@ internal class SavedSwapTransactionListConverter(
toTokensResponse = userTokensResponseFactory.createResponseToken(currency = toCryptoCurrency, accountId = null),
transactions = tokenTransactions,
)
private fun createSwapTransactionNetwork(token: UserTokensResponse.Token, userWallet: UserWallet): Network? {
val blockchain = Blockchain.fromNetworkId(token.networkId) ?: return null
return if (token.derivationPath == null) {
networkFactory.create(
blockchain = blockchain,
derivationPath = Network.DerivationPath.None,
userWallet = userWallet,
)
} else {
networkFactory.create(
blockchain = blockchain,
extraDerivationPath = token.derivationPath,
userWallet = userWallet,
)
}
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.feature.swap.di
import com.squareup.moshi.Moshi
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.common.network.NetworkFactory
import com.tangem.datasource.api.express.TangemExpressApi
import com.tangem.datasource.api.express.models.response.ExpressErrorResponse
import com.tangem.datasource.crypto.DataSignatureVerifier
@ -60,11 +61,13 @@ internal class SwapDataModule {
fun provideSwapTransactionRepository(
appPreferencesStore: AppPreferencesStore,
responseCryptoCurrenciesFactory: ResponseCryptoCurrenciesFactory,
networkFactory: NetworkFactory,
dispatcherProvider: CoroutineDispatcherProvider,
): SwapTransactionRepository {
return DefaultSwapTransactionRepository(
appPreferencesStore = appPreferencesStore,
responseCryptoCurrenciesFactory = responseCryptoCurrenciesFactory,
networkFactory = networkFactory,
dispatchers = dispatcherProvider,
)
}

View file

@ -23,12 +23,7 @@ interface SwapTransactionRepository {
cryptoCurrencyId: CryptoCurrency.ID,
): Flow<List<SavedSwapTransactionListModel>?>
suspend fun removeTransaction(
userWalletId: UserWalletId,
fromCryptoCurrency: CryptoCurrency,
toCryptoCurrency: CryptoCurrency,
txId: String,
)
suspend fun removeTransaction(userWalletId: UserWalletId, txId: String)
suspend fun storeTransactionState(
txId: String,

View file

@ -88,8 +88,6 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
if (shouldDispose) {
swapTransactionRepository.removeTransaction(
userWalletId = userWallet.walletId,
fromCryptoCurrency = selectedTx.fromCryptoCurrency,
toCryptoCurrency = selectedTx.toCryptoCurrency,
txId = selectedTx.info.txId,
)
}