Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-16 13:35:39 +03:00
parent 8541957ee3
commit 189ffd9927
73 changed files with 547 additions and 409 deletions

View file

@ -16,7 +16,6 @@ import com.tangem.datasource.local.token.UserTokensResponseStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.core.utils.catchOn
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesFetcher.Params
import com.tangem.domain.wallets.models.UserWallet
@ -60,7 +59,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcher(
if (!userWallet.isMultiCurrency) error("${this::class.simpleName} supports only multi-currency wallet")
val response = if (userWallet is UserWallet.Cold && userWallet.isDemoWalletWithoutSavedTokens()) {
createDefaultUserTokensResponse(scanResponse = userWallet.scanResponse)
createDefaultUserTokensResponse(userWallet = userWallet)
} else {
safeApiCall(
call = {
@ -99,7 +98,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcher(
val userWalletId = userWallet.walletId
val response = userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId)
?: createDefaultUserTokensResponse(scanResponse = userWallet.requireColdWallet().scanResponse)
?: createDefaultUserTokensResponse(userWallet = userWallet)
if (error is ApiResponseError.HttpException && error.code == ApiResponseError.HttpException.Code.NOT_FOUND) {
Timber.w(error, "Requested currencies could not be found in the remote store for: $userWalletId")
@ -121,9 +120,9 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcher(
expressServiceLoader.update(userWallet = userWallet, userTokens = tokens)
}
private fun createDefaultUserTokensResponse(scanResponse: ScanResponse): UserTokensResponse {
private fun createDefaultUserTokensResponse(userWallet: UserWallet): UserTokensResponse {
return userTokensResponseFactory.createUserTokensResponse(
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(scanResponse),
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(userWallet),
isGroupedByNetwork = false,
isSortedByBalance = false,
)

View file

@ -48,7 +48,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducer @AssistedInject constr
responseCryptoCurrenciesFactory.createCurrencies(
response = response,
scanResponse = userWallet.scanResponse,
userWallet = userWallet,
).toSet()
}
.onEmpty { emit(fallback) }

View file

@ -184,7 +184,8 @@ internal class DefaultCurrenciesRepository(
)
val token = userTokensResponseFactory.createResponseToken(currency)
val updatedResponse = savedCurrencies.copy(tokens = savedCurrencies.tokens.filterNot { it == token })
val updatedResponse =
savedCurrencies.copy(tokens = savedCurrencies.tokens.filterNot { it == token })
userTokensSaver.storeAndPush(
userWalletId = userWalletId,
response = updatedResponse,
@ -229,10 +230,11 @@ internal class DefaultCurrenciesRepository(
): CryptoCurrency {
return withContext(dispatchers.io) {
val userWallet = userWalletsStore.getSyncStrict(userWalletId)
userWallet.requireColdWallet()
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false)
val currency = cardCryptoCurrencyFactory.createPrimaryCurrencyForSingleCurrencyCard(
userWallet.requireColdWallet().scanResponse,
userWallet = userWallet,
)
fetchExpressAssetsByNetworkIds(
@ -254,11 +256,12 @@ internal class DefaultCurrenciesRepository(
val scanResponse = userWallet.requireColdWallet().scanResponse
val currencies = if (scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(scanResponse = scanResponse)
cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(userWallet = userWallet)
} else {
cardCryptoCurrencyFactory.createPrimaryCurrencyForSingleCurrencyCard(scanResponse = scanResponse).run {
listOf(this)
}
cardCryptoCurrencyFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet = userWallet)
.run {
listOf(this)
}
}
fetchExpressAssetsByNetworkIds(
@ -277,10 +280,11 @@ internal class DefaultCurrenciesRepository(
): CryptoCurrency {
return withContext(dispatchers.io) {
val userWallet = userWalletsStore.getSyncStrict(userWalletId)
userWallet.requireColdWallet()
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false)
val currency = cardCryptoCurrencyFactory.createCurrenciesForSingleCurrencyCardWithToken(
scanResponse = userWallet.requireColdWallet().scanResponse,
userWallet = userWallet,
)
.find { it.id == id }
requireNotNull(currency) { "Unable to find currency with provided ID: $id" }
@ -320,7 +324,10 @@ internal class DefaultCurrenciesRepository(
},
)
responseCryptoCurrenciesFactory.createCurrencies(storedTokens, userWallet.requireColdWallet().scanResponse)
responseCryptoCurrenciesFactory.createCurrencies(
storedTokens,
userWallet = userWallet,
)
}
override suspend fun getMultiCurrencyWalletCachedCurrenciesSync(userWalletId: UserWalletId) =
@ -335,7 +342,10 @@ internal class DefaultCurrenciesRepository(
},
)
responseCryptoCurrenciesFactory.createCurrencies(storedTokens, userWallet.requireColdWallet().scanResponse)
responseCryptoCurrenciesFactory.createCurrencies(
storedTokens,
userWallet = userWallet,
)
}
override suspend fun getMultiCurrencyWalletCurrency(
@ -360,7 +370,7 @@ internal class DefaultCurrenciesRepository(
responseCryptoCurrenciesFactory.createCurrency(
currencyId = id,
response = response,
scanResponse = userWallet.requireColdWallet().scanResponse,
userWallet = userWallet,
)
}
@ -394,7 +404,7 @@ internal class DefaultCurrenciesRepository(
val coin = responseCryptoCurrenciesFactory.createCurrency(
responseToken = storedCoin,
scanResponse = userWallet.requireColdWallet().scanResponse,
userWallet = userWallet,
)
coin as? CryptoCurrency.Coin ?: error("Unable to create currency")
@ -438,12 +448,15 @@ internal class DefaultCurrenciesRepository(
cryptoCurrencyStatus: CryptoCurrencyStatus,
): Boolean {
val blockchain = cryptoCurrencyStatus.currency.network.toBlockchain()
val isBitcoinBlockchain = blockchain == Blockchain.Bitcoin || blockchain == Blockchain.BitcoinTestnet
val isBitcoinBlockchain =
blockchain == Blockchain.Bitcoin || blockchain == Blockchain.BitcoinTestnet
return when {
cryptoCurrencyStatus.currency is CryptoCurrency.Coin && isBitcoinBlockchain -> {
val outgoingTransactions = cryptoCurrencyStatus.value.pendingTransactions.filter { it.isOutgoing }
val outgoingTransactions =
cryptoCurrencyStatus.value.pendingTransactions.filter { it.isOutgoing }
outgoingTransactions.isNotEmpty()
}
blockchain.isEvm() -> false
blockchain == Blockchain.Tron || blockchain == Blockchain.TronTestnet -> false
else -> {
@ -481,6 +494,7 @@ internal class DefaultCurrenciesRepository(
balance = balance,
)
}
is FeePaidSdkCurrency.FeeResource -> FeePaidCurrency.FeeResource(currency = feePaidCurrency.currency)
}
}
@ -508,7 +522,8 @@ internal class DefaultCurrenciesRepository(
.coins
.firstOrNull()
?: error("Token not found")
val network = foundToken.networks.firstOrNull { it.networkId == networkId } ?: error("Network not found")
val network = foundToken.networks.firstOrNull { it.networkId == networkId }
?: error("Network not found")
CryptoCurrencyFactory.Token(
symbol = foundToken.symbol,
name = foundToken.name,
@ -521,7 +536,7 @@ internal class DefaultCurrenciesRepository(
token = token,
networkId = networkId,
extraDerivationPath = null,
scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
userWallet = userWallet,
) ?: error("Unable to create token")
}
@ -559,23 +574,25 @@ internal class DefaultCurrenciesRepository(
responseCryptoCurrenciesFactory.createCurrencies(
response = storedTokens.copy(tokens = filterResponse),
scanResponse = userWallet.requireColdWallet().scanResponse,
userWallet = userWallet,
)
}
}
else -> {
val currencies = if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
getSingleCurrencyWalletWithCardCurrencies(userWallet.walletId)
} else {
val currency =
getSingleCurrencyWalletPrimaryCurrency(userWalletId = userWallet.walletId)
if (currency.id.rawCurrencyId == currencyRawId) {
listOf(currency)
else -> {
val currencies =
if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
getSingleCurrencyWalletWithCardCurrencies(userWallet.walletId)
} else {
emptyList()
val currency =
getSingleCurrencyWalletPrimaryCurrency(userWalletId = userWallet.walletId)
if (currency.id.rawCurrencyId == currencyRawId) {
listOf(currency)
} else {
emptyList()
}
}
}
flow {
emit(currencies)
}
@ -603,14 +620,15 @@ internal class DefaultCurrenciesRepository(
}
override fun getCardTypesResolver(userWalletId: UserWalletId): CardTypesResolver {
return userWalletsStore.getSyncStrict(userWalletId).requireColdWallet().cardTypesResolver // TODO [REDACTED_TASK_KEY]
return userWalletsStore.getSyncStrict(userWalletId)
.requireColdWallet().cardTypesResolver // TODO [REDACTED_TASK_KEY]
}
private fun getMultiCurrencyWalletCurrencies(userWallet: UserWallet): Flow<List<CryptoCurrency>> {
return getSavedUserTokensResponse(userWallet.walletId).map { storedTokens ->
responseCryptoCurrenciesFactory.createCurrencies(
response = storedTokens,
scanResponse = userWallet.requireColdWallet().scanResponse,
userWallet = userWallet,
)
}
}
@ -700,7 +718,10 @@ internal class DefaultCurrenciesRepository(
?: createDefaultUserTokensResponse(userWallet = userWallet)
if (e is ApiResponseError.HttpException && e.code == ApiResponseError.HttpException.Code.NOT_FOUND) {
Timber.w(e, "Requested currencies could not be found in the remote store for: $userWalletId")
Timber.w(
e,
"Requested currencies could not be found in the remote store for: $userWalletId",
)
userTokensSaver.push(userWalletId, response)
} else {
@ -712,8 +733,8 @@ internal class DefaultCurrenciesRepository(
private fun createDefaultUserTokensResponse(userWallet: UserWallet) =
userTokensResponseFactory.createUserTokensResponse(
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(
userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY]
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(
userWallet = userWallet,
),
isGroupedByNetwork = false,
isSortedByBalance = false,
@ -732,9 +753,11 @@ internal class DefaultCurrenciesRepository(
!userWallet.isMultiCurrency && isMultiCurrencyWalletExpected -> {
"Multi currency wallet expected, but single currency wallet was found: $userWalletId"
}
userWallet.isMultiCurrency && !isMultiCurrencyWalletExpected -> {
"Single currency wallet expected, but multi currency wallet was found: $userWalletId"
}
else -> null
}

View file

@ -125,7 +125,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
every { userWalletsStore.getSyncStrict(params.userWalletId) } returns mockUserWallet
coEvery { userTokensResponseStore.getSyncOrNull(userWalletId = params.userWalletId) } returns null
every {
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(mockUserWallet.scanResponse)
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(mockUserWallet)
} returns defaultCoins
coEvery {
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
@ -141,7 +141,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
coVerifyOrder {
userWalletsStore.getSyncStrict(key = params.userWalletId)
userTokensResponseStore.getSyncOrNull(userWalletId = params.userWalletId)
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(scanResponse = mockUserWallet.scanResponse)
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(mockUserWallet)
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
userTokensSaver.store(userWalletId = params.userWalletId, response = userTokensResponse)
expressServiceLoader.update(userWallet = mockUserWallet, userTokens = userTokensResponse.toLeastTokens())
@ -187,7 +187,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
}
coVerify(inverse = true) {
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(scanResponse = any())
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(any())
}
}
@ -229,7 +229,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
coVerify(inverse = true) {
userTokensResponseStore.getSyncOrNull(userWalletId = any())
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(scanResponse = any())
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(any())
}
}
@ -267,7 +267,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
coEvery { tangemTechApi.getUserTokens(userId = params.userWalletId.stringValue) } returns apiResponse
coEvery { userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId) } returns null
coEvery {
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(mockUserWallet.scanResponse)
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(mockUserWallet)
} returns defaultCoins
coEvery {
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
@ -335,7 +335,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
coVerify(inverse = true) {
userTokensSaver.push(userWalletId = any(), response = any())
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(scanResponse = any())
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(any())
}
}
@ -377,7 +377,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
coEvery { tangemTechApi.getUserTokens(userId = params.userWalletId.stringValue) } returns apiResponse
coEvery { userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId) } returns null
coEvery {
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(mockUserWallet.scanResponse)
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(mockUserWallet)
} returns defaultCoins
coEvery {
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
@ -394,7 +394,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
userWalletsStore.getSyncStrict(key = params.userWalletId)
tangemTechApi.getUserTokens(userId = params.userWalletId.stringValue)
userTokensResponseStore.getSyncOrNull(userWalletId = userWalletId)
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(mockUserWallet.scanResponse)
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(mockUserWallet)
userTokensSaver.push(userWalletId = params.userWalletId, response = userTokensResponse)
customTokensMerger.mergeIfPresented(userWalletId = params.userWalletId, response = userTokensResponse)
userTokensSaver.store(userWalletId = params.userWalletId, response = userTokensResponse)
@ -447,7 +447,7 @@ internal class DefaultMultiWalletCryptoCurrenciesFetcherTest {
}
coVerify(inverse = true) {
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(scanResponse = any())
cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyWallet(any())
}
}

View file

@ -72,7 +72,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
}
verify(inverse = true) {
responseCryptoCurrenciesFactory.createCurrencies(response = any(), scanResponse = any())
responseCryptoCurrenciesFactory.createCurrencies(response = any(), userWallet = any())
}
}
@ -114,14 +114,14 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
every {
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
scanResponse = userWallet.scanResponse,
userWallet = userWallet,
)
} returns cryptoCurrencies.toList()
every {
responseCryptoCurrenciesFactory.createCurrencies(
response = updatedUserTokensResponse,
scanResponse = userWallet.scanResponse,
userWallet = userWallet,
)
} returns updatedCryptoCurrencies.toList()
@ -143,7 +143,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
userTokensResponseStore.get(params.userWalletId)
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
scanResponse = userWallet.scanResponse,
userWallet = userWallet,
)
}
@ -161,7 +161,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
verifyOrder {
responseCryptoCurrenciesFactory.createCurrencies(
response = updatedUserTokensResponse,
scanResponse = userWallet.scanResponse,
userWallet = userWallet,
)
}
}
@ -185,7 +185,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
every {
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
scanResponse = userWallet.scanResponse,
userWallet = userWallet,
)
} returns cryptoCurrencies.toList()
@ -207,7 +207,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
userTokensResponseStore.get(params.userWalletId)
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
scanResponse = userWallet.scanResponse,
userWallet = userWallet,
)
}
@ -251,7 +251,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
every {
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
scanResponse = userWallet.scanResponse,
userWallet = userWallet,
)
} returns cryptoCurrencies.toList()
@ -282,7 +282,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
verifyOrder {
responseCryptoCurrenciesFactory.createCurrencies(
response = userTokensResponse,
scanResponse = userWallet.scanResponse,
userWallet = userWallet,
)
}
}
@ -307,7 +307,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
}
verify(inverse = true) {
responseCryptoCurrenciesFactory.createCurrencies(response = any(), scanResponse = any())
responseCryptoCurrenciesFactory.createCurrencies(response = any(), userWallet = any())
}
}
@ -335,7 +335,7 @@ internal class DefaultMultiWalletCryptoCurrenciesProducerTest {
verify(inverse = true) {
userTokensResponseStore.get(any())
responseCryptoCurrenciesFactory.createCurrencies(response = any(), scanResponse = any())
responseCryptoCurrenciesFactory.createCurrencies(response = any(), userWallet = any())
}
}