Updated on 2026-08-14
This commit is contained in:
parent
4b5adcd08a
commit
5ed381c263
56 changed files with 631 additions and 455 deletions
|
|
@ -54,7 +54,6 @@ internal class TokensListMigration(
|
|||
}
|
||||
is Either.Right -> {
|
||||
currentUserWallet = selectedWalletEither.value
|
||||
val derivationStyle = currentUserWallet.scanResponse.derivationStyleProvider.getDerivationStyle()
|
||||
|
||||
when (val currenciesEither = getCurrenciesUseCase(userWalletId = selectedWalletEither.value.walletId)) {
|
||||
is Either.Left -> {
|
||||
|
|
@ -65,7 +64,7 @@ internal class TokensListMigration(
|
|||
TokensListCryptoCurrencies(
|
||||
coins = currenciesEither.value
|
||||
.filterIsInstance<CryptoCurrency.Coin>()
|
||||
.filterNot { it.isCustomCurrency(derivationStyle) }
|
||||
.filterNot { it.isCustom }
|
||||
.also { currentNewCoins = it }
|
||||
.map { Blockchain.fromId(it.network.id.value) },
|
||||
tokens = currenciesEither.value
|
||||
|
|
@ -91,12 +90,6 @@ internal class TokensListMigration(
|
|||
}
|
||||
}
|
||||
|
||||
private fun CryptoCurrency.Coin.isCustomCurrency(derivationStyle: DerivationStyle?): Boolean {
|
||||
if (derivationPath == null || derivationStyle == null) return false
|
||||
|
||||
return derivationPath != Blockchain.fromId(network.id.value).derivationPath(derivationStyle)?.rawPath
|
||||
}
|
||||
|
||||
private fun getLegacyCryptoCurrencies(): TokensListCryptoCurrencies {
|
||||
val wallets = store.state.walletState.walletsDataFromStores
|
||||
val derivationStyle = store.state.globalState.scanResponse?.derivationStyleProvider?.getDerivationStyle()
|
||||
|
|
@ -157,12 +150,14 @@ internal class TokensListMigration(
|
|||
cryptoCurrencyFactory.createToken(
|
||||
sdkToken = it.token,
|
||||
blockchain = it.blockchain,
|
||||
extraDerivationPath = null,
|
||||
derivationStyleProvider = currentUserWallet.scanResponse.derivationStyleProvider,
|
||||
)
|
||||
},
|
||||
changedCoins = changedBlockchainList.mapNotNull {
|
||||
cryptoCurrencyFactory.createCoin(
|
||||
blockchain = it,
|
||||
extraDerivationPath = null,
|
||||
derivationStyleProvider = currentUserWallet.scanResponse.derivationStyleProvider,
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ object TokensMiddleware {
|
|||
|
||||
val customTokensCandidates = currencyList
|
||||
.filter { Blockchain.fromId(it.network.id.value).getSupportedCurves().contains(curve) }
|
||||
.mapNotNull(CryptoCurrency::derivationPath)
|
||||
.mapNotNull { it.network.derivationPath.value }
|
||||
.map(::DerivationPath)
|
||||
|
||||
val bothCandidates = (manageTokensCandidates + customTokensCandidates).distinct().toMutableList()
|
||||
|
|
@ -306,7 +306,7 @@ object TokensMiddleware {
|
|||
|
||||
currencyList.find { it is CryptoCurrency.Coin && Blockchain.fromId(it.network.id.value) == Blockchain.Cardano }
|
||||
?.let { currency ->
|
||||
currency.derivationPath?.let {
|
||||
currency.network.derivationPath.value?.let {
|
||||
bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ internal class CryptoCurrencyConverter : TwoWayConverter<Currency, CryptoCurrenc
|
|||
is Currency.Blockchain -> requireNotNull(
|
||||
cryptoCurrencyFactory.createCoin(
|
||||
blockchain = value.blockchain,
|
||||
extraDerivationPath = value.derivationPath,
|
||||
derivationStyleProvider = requireNotNull(
|
||||
store.state.globalState
|
||||
.userWalletsListManager
|
||||
|
|
@ -31,6 +32,7 @@ internal class CryptoCurrencyConverter : TwoWayConverter<Currency, CryptoCurrenc
|
|||
cryptoCurrencyFactory.createToken(
|
||||
sdkToken = value.token,
|
||||
blockchain = value.blockchain,
|
||||
extraDerivationPath = value.derivationPath,
|
||||
derivationStyleProvider = requireNotNull(
|
||||
store.state.globalState
|
||||
.userWalletsListManager
|
||||
|
|
@ -49,7 +51,7 @@ internal class CryptoCurrencyConverter : TwoWayConverter<Currency, CryptoCurrenc
|
|||
return when (value) {
|
||||
is CryptoCurrency.Coin -> Currency.Blockchain(
|
||||
blockchain = blockchain,
|
||||
derivationPath = value.derivationPath,
|
||||
derivationPath = value.network.derivationPath.value,
|
||||
)
|
||||
is CryptoCurrency.Token -> Currency.Token(
|
||||
token = Token(
|
||||
|
|
@ -60,7 +62,7 @@ internal class CryptoCurrencyConverter : TwoWayConverter<Currency, CryptoCurrenc
|
|||
id = value.id.value,
|
||||
),
|
||||
blockchain = blockchain,
|
||||
derivationPath = value.derivationPath,
|
||||
derivationPath = value.network.derivationPath.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import com.tangem.core.navigation.AppScreen
|
|||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.common.extensions.toCoinId
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.feature.swap.presentation.SwapFragment
|
||||
|
|
@ -128,9 +127,7 @@ class TradeCryptoMiddleware {
|
|||
.getOrCreateWalletManager(
|
||||
userWallet = action.userWallet,
|
||||
blockchain = blockchain,
|
||||
derivationPath = blockchain.derivationPath(
|
||||
style = action.userWallet.scanResponse.derivationStyleProvider.getDerivationStyle(),
|
||||
),
|
||||
derivationPath = currency.network.derivationPath.value,
|
||||
)
|
||||
|
||||
if (walletManager !is EthereumWalletManager) {
|
||||
|
|
@ -317,9 +314,7 @@ class TradeCryptoMiddleware {
|
|||
.getOrCreateWalletManager(
|
||||
userWallet = action.userWallet,
|
||||
blockchain = blockchain,
|
||||
derivationPath = blockchain.derivationPath(
|
||||
style = action.userWallet.scanResponse.derivationStyleProvider.getDerivationStyle(),
|
||||
),
|
||||
derivationPath = currency.network.derivationPath.value,
|
||||
)
|
||||
|
||||
if (walletManager == null) {
|
||||
|
|
@ -360,9 +355,7 @@ class TradeCryptoMiddleware {
|
|||
.getOrCreateWalletManager(
|
||||
userWallet = action.userWallet,
|
||||
blockchain = blockchain,
|
||||
derivationPath = blockchain.derivationPath(
|
||||
style = action.userWallet.scanResponse.derivationStyleProvider.getDerivationStyle(),
|
||||
),
|
||||
derivationPath = currency.network.derivationPath.value,
|
||||
)
|
||||
|
||||
if (walletManager == null) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.tangem.utils.extensions
|
|||
/**
|
||||
* Removes an element from the collection based on the provided predicate.
|
||||
*
|
||||
* !!!This function is not thread-safe!!!
|
||||
*
|
||||
* @param predicate The condition to remove an element.
|
||||
* @return [Boolean] indicating whether an element was removed.
|
||||
*/
|
||||
|
|
@ -33,7 +35,8 @@ inline fun <T> MutableList<T>.replaceBy(item: T, predicate: (T) -> Boolean): Boo
|
|||
|
||||
/**
|
||||
* Adds the specified element to the list or replaces an existing element.
|
||||
* The predicate defines the condition to replace the existing element.
|
||||
*
|
||||
* !!!This function is not thread-safe!!!
|
||||
*
|
||||
* @param item The element to be added or replace the existing one.
|
||||
* @param predicate The condition to replace an existing element.
|
||||
|
|
|
|||
39
core/utils/src/main/java/com/tangem/utils/extensions/Set.kt
Normal file
39
core/utils/src/main/java/com/tangem/utils/extensions/Set.kt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.utils.extensions
|
||||
|
||||
/**
|
||||
* Replaces an element in the set with the provided item based on the predicate.
|
||||
*
|
||||
* !!!This function is not thread-safe!!!
|
||||
*
|
||||
* @param item The element to replace the existing one.
|
||||
* @param predicate The condition to replace an existing element.
|
||||
* @return [Boolean] indicating whether an element was replaced.
|
||||
*/
|
||||
inline fun <T> MutableSet<T>.replaceBy(item: T, predicate: (T) -> Boolean): Boolean {
|
||||
val foundItem = firstOrNull(predicate) ?: return false
|
||||
|
||||
remove(foundItem)
|
||||
add(item)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified element to the set or replaces an existing element.
|
||||
*
|
||||
* !!!This function is not thread-safe!!!
|
||||
*
|
||||
* @param item The element to be added or replace the existing one.
|
||||
* @param predicate The condition to replace an existing element.
|
||||
* @return The modified [Set] after adding or replacing the element.
|
||||
*/
|
||||
inline fun <T> Set<T>.addOrReplace(item: T, predicate: (T) -> Boolean): Set<T> {
|
||||
val mutableList = this.toMutableSet()
|
||||
val isReplaced = mutableList.replaceBy(item, predicate)
|
||||
|
||||
if (!isReplaced) {
|
||||
mutableList.add(item)
|
||||
}
|
||||
|
||||
return mutableList
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
|||
import com.tangem.datasource.local.token.UserMarketCoinsStore
|
||||
import com.tangem.datasource.local.token.UserTokensStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.common.extensions.toCoinId
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.core.error.DataError
|
||||
|
|
@ -35,8 +36,8 @@ internal class DefaultCurrenciesRepository(
|
|||
) : CurrenciesRepository {
|
||||
|
||||
private val demoConfig = DemoConfig()
|
||||
private val responseCurrenciesFactory = ResponseCurrenciesFactory(demoConfig)
|
||||
private val cardCurrenciesFactory = CardCurrenciesFactory(demoConfig)
|
||||
private val responseCurrenciesFactory = ResponseCryptoCurrenciesFactory(demoConfig)
|
||||
private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(demoConfig)
|
||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||
|
||||
override suspend fun saveTokens(
|
||||
|
|
@ -90,6 +91,7 @@ internal class DefaultCurrenciesRepository(
|
|||
.mapNotNull {
|
||||
CryptoCurrencyFactory().createCoin(
|
||||
blockchain = getBlockchain(networkId = it.network.id),
|
||||
extraDerivationPath = it.network.derivationPath.value,
|
||||
derivationStyleProvider = getUserWallet(userWalletId).scanResponse.derivationStyleProvider,
|
||||
)
|
||||
}
|
||||
|
|
@ -99,7 +101,7 @@ internal class DefaultCurrenciesRepository(
|
|||
return any {
|
||||
val blockchain = getBlockchain(networkId = token.network.id)
|
||||
|
||||
it.id == getCoinId(blockchain).rawCurrencyId
|
||||
it.id == blockchain.toCoinId()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,10 +175,7 @@ internal class DefaultCurrenciesRepository(
|
|||
"Unable to find tokens response for user wallet with provided ID: $userWalletId"
|
||||
}
|
||||
|
||||
return responseCurrenciesFactory.createCurrencies(
|
||||
response = storedTokens,
|
||||
card = userWallet.scanResponse.card,
|
||||
)
|
||||
return responseCurrenciesFactory.createCurrencies(storedTokens, userWallet.scanResponse)
|
||||
}
|
||||
|
||||
override suspend fun getMultiCurrencyWalletCurrency(
|
||||
|
|
@ -190,7 +189,7 @@ internal class DefaultCurrenciesRepository(
|
|||
"Unable to find tokens response for user wallet with provided ID: $userWalletId"
|
||||
}
|
||||
|
||||
responseCurrenciesFactory.createCurrency(id, response, userWallet.scanResponse.card)
|
||||
responseCurrenciesFactory.createCurrency(id, response, userWallet.scanResponse)
|
||||
}
|
||||
|
||||
override suspend fun getNetworkCoin(userWalletId: UserWalletId, networkId: Network.ID): CryptoCurrency.Coin {
|
||||
|
|
@ -206,10 +205,7 @@ internal class DefaultCurrenciesRepository(
|
|||
val storedCoin = storedTokens.tokens.find { it.networkId == Blockchain.fromId(networkId.value).toNetworkId() }
|
||||
?: error("Coin in this network $networkId not found")
|
||||
|
||||
val coin = responseCurrenciesFactory.createCurrency(
|
||||
responseToken = storedCoin,
|
||||
card = userWallet.scanResponse.card,
|
||||
)
|
||||
val coin = responseCurrenciesFactory.createCurrency(storedCoin, userWallet.scanResponse)
|
||||
|
||||
return coin as? CryptoCurrency.Coin ?: error("Unable to create currency")
|
||||
}
|
||||
|
|
@ -242,7 +238,7 @@ internal class DefaultCurrenciesRepository(
|
|||
return userTokensStore.get(userWallet.walletId).map { storedTokens ->
|
||||
responseCurrenciesFactory.createCurrencies(
|
||||
response = storedTokens,
|
||||
card = userWallet.scanResponse.card,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -288,10 +284,7 @@ internal class DefaultCurrenciesRepository(
|
|||
if (NOT_FOUND_HTTP_CODE in errorMessage) {
|
||||
val response = userTokensStore.getSyncOrNull(userWallet.walletId)
|
||||
?: userTokensResponseFactory.createUserTokensResponse(
|
||||
currencies = cardCurrenciesFactory.createDefaultCoinsForMultiCurrencyCard(
|
||||
card = userWallet.scanResponse.card,
|
||||
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
|
||||
),
|
||||
currencies = cardCurrenciesFactory.createDefaultCoinsForMultiCurrencyCard(userWallet.scanResponse),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
package com.tangem.data.tokens.repository
|
||||
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.tokens.utils.CardCurrenciesFactory
|
||||
import com.tangem.data.tokens.utils.NetworkConverter
|
||||
import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory
|
||||
import com.tangem.data.tokens.utils.NetworkStatusFactory
|
||||
import com.tangem.data.tokens.utils.ResponseCurrenciesFactory
|
||||
import com.tangem.data.tokens.utils.ResponseCryptoCurrenciesFactory
|
||||
import com.tangem.datasource.local.token.UserTokensStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
|
|
@ -13,7 +12,6 @@ import com.tangem.domain.tokens.models.CryptoCurrency
|
|||
import com.tangem.domain.tokens.models.Network
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.addOrReplace
|
||||
|
|
@ -29,25 +27,18 @@ internal class DefaultNetworksRepository(
|
|||
) : NetworksRepository {
|
||||
|
||||
private val demoConfig by lazy { DemoConfig() }
|
||||
private val networkConverter by lazy { NetworkConverter() }
|
||||
private val cardCurrenciesFactory by lazy { CardCurrenciesFactory(demoConfig) }
|
||||
private val responseCurrenciesFactory by lazy { ResponseCurrenciesFactory(demoConfig) }
|
||||
private val cardCurrenciesFactory by lazy { CardCryptoCurrenciesFactory(demoConfig) }
|
||||
private val responseCurrenciesFactory by lazy { ResponseCryptoCurrenciesFactory(demoConfig) }
|
||||
private val networkStatusFactory by lazy { NetworkStatusFactory() }
|
||||
|
||||
private val networksStatuses: MutableStateFlow<List<NetworkStatus>> = MutableStateFlow(emptyList())
|
||||
|
||||
override fun getNetworks(networksIds: Set<Network.ID>): Set<Network> {
|
||||
return networkConverter.convertSet(networksIds)
|
||||
}
|
||||
private val networksStatuses: MutableStateFlow<Set<NetworkStatus>> = MutableStateFlow(hashSetOf())
|
||||
|
||||
override fun getNetworkStatusesUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
networks: Set<Network>,
|
||||
): Flow<Set<NetworkStatus>> = channelFlow {
|
||||
launch(dispatchers.io) {
|
||||
networksStatuses.collect {
|
||||
send(it.toSet())
|
||||
}
|
||||
networksStatuses.collect(::send)
|
||||
}
|
||||
|
||||
launch(dispatchers.io) {
|
||||
|
|
@ -57,7 +48,7 @@ internal class DefaultNetworksRepository(
|
|||
|
||||
override suspend fun getNetworkStatusesSync(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
networks: Set<Network>,
|
||||
refresh: Boolean,
|
||||
): Set<NetworkStatus> = withContext(dispatchers.io) {
|
||||
fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh)
|
||||
|
|
@ -66,14 +57,14 @@ internal class DefaultNetworksRepository(
|
|||
|
||||
private suspend fun fetchNetworksStatusesIfCacheExpired(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
networks: Set<Network>,
|
||||
refresh: Boolean,
|
||||
) {
|
||||
coroutineScope {
|
||||
networks
|
||||
.map { networkId ->
|
||||
.map { network ->
|
||||
async {
|
||||
fetchNetworkStatusIfCacheExpired(userWalletId, networkId, refresh)
|
||||
fetchNetworkStatusIfCacheExpired(userWalletId, network, refresh)
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
|
|
@ -82,67 +73,70 @@ internal class DefaultNetworksRepository(
|
|||
|
||||
private suspend fun fetchNetworkStatusIfCacheExpired(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
network: Network,
|
||||
refresh: Boolean,
|
||||
) {
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getNetworksStatusesCacheKey(userWalletId, networkId),
|
||||
key = getNetworksStatusesCacheKey(userWalletId, network),
|
||||
skipCache = refresh,
|
||||
block = { fetchNetworkStatus(userWalletId, networkId) },
|
||||
block = { fetchNetworkStatus(userWalletId, network) },
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun fetchNetworkStatus(userWalletId: UserWalletId, networkId: Network.ID) {
|
||||
val currencies = getCurrencies(userWalletId)
|
||||
.asSequence()
|
||||
.filter { it.network.id == networkId }
|
||||
private suspend fun fetchNetworkStatus(userWalletId: UserWalletId, network: Network) {
|
||||
val currencies = getCurrencies(userWalletId, network)
|
||||
|
||||
val result = walletManagersFacade.update(
|
||||
userWalletId = userWalletId,
|
||||
networkId = networkId,
|
||||
network = network,
|
||||
extraTokens = currencies.filterIsInstance<CryptoCurrency.Token>().toSet(),
|
||||
)
|
||||
|
||||
// Invalidate cache key if wallet manager update failed
|
||||
when (result) {
|
||||
is UpdateWalletManagerResult.Verified,
|
||||
is UpdateWalletManagerResult.NoAccount,
|
||||
-> Unit
|
||||
is UpdateWalletManagerResult.Unreachable,
|
||||
is UpdateWalletManagerResult.MissedDerivation,
|
||||
-> cacheRegistry.invalidate(getNetworksStatusesCacheKey(userWalletId, networkId))
|
||||
}
|
||||
|
||||
val networkStatus = networkStatusFactory.createNetworkStatus(
|
||||
networkId = networkId,
|
||||
network = network,
|
||||
result = result,
|
||||
currencies = currencies.toSet(),
|
||||
)
|
||||
|
||||
networksStatuses.update { statuses ->
|
||||
statuses.addOrReplace(networkStatus) { it.networkId == networkStatus.networkId }
|
||||
statuses.addOrReplace(networkStatus) { it.network == networkStatus.network }
|
||||
}
|
||||
|
||||
invalidateCacheKeyIfNeeded(userWalletId, networkStatus)
|
||||
}
|
||||
|
||||
private suspend fun getCurrencies(userWalletId: UserWalletId): List<CryptoCurrency> {
|
||||
private suspend fun getCurrencies(userWalletId: UserWalletId, network: Network): Sequence<CryptoCurrency> {
|
||||
val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"Unable to find user wallet with provided ID: $userWalletId"
|
||||
}
|
||||
|
||||
return if (userWallet.isMultiCurrency) {
|
||||
val currencies = if (userWallet.isMultiCurrency) {
|
||||
val response = requireNotNull(userTokensStore.getSyncOrNull(userWalletId)) {
|
||||
"Unable to find tokens response for user wallet with provided ID: $userWalletId"
|
||||
}
|
||||
|
||||
responseCurrenciesFactory.createCurrencies(response, userWallet.scanResponse.card)
|
||||
responseCurrenciesFactory.createCurrencies(response, userWallet.scanResponse).asSequence()
|
||||
} else {
|
||||
val currency = cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse)
|
||||
|
||||
listOf(currency)
|
||||
sequenceOf(currency)
|
||||
}
|
||||
|
||||
return currencies.filter { it.network == network }
|
||||
}
|
||||
|
||||
private suspend fun invalidateCacheKeyIfNeeded(userWalletId: UserWalletId, networkStatus: NetworkStatus) {
|
||||
when (networkStatus.value) {
|
||||
is NetworkStatus.Verified,
|
||||
is NetworkStatus.NoAccount,
|
||||
-> Unit
|
||||
is NetworkStatus.Unreachable,
|
||||
is NetworkStatus.MissedDerivation,
|
||||
-> cacheRegistry.invalidate(getNetworksStatusesCacheKey(userWalletId, networkStatus.network))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNetworksStatusesCacheKey(userWalletId: UserWalletId, nerworkId: Network.ID): String {
|
||||
return "network_status_${userWalletId}_${nerworkId.value}"
|
||||
private fun getNetworksStatusesCacheKey(userWalletId: UserWalletId, network: Network): String {
|
||||
return "network_status_${userWalletId}_${network.id}_${network.derivationPath.value}"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package com.tangem.data.tokens.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
|
||||
internal class CardCryptoCurrenciesFactory(private val demoConfig: DemoConfig) {
|
||||
|
||||
private val cryptoCurrencyFactory = CryptoCurrencyFactory()
|
||||
|
||||
fun createDefaultCoinsForMultiCurrencyCard(scanResponse: ScanResponse): List<CryptoCurrency.Coin> {
|
||||
val cardDerivationStyleProvider = scanResponse.derivationStyleProvider
|
||||
val card = scanResponse.card
|
||||
|
||||
var blockchains = if (demoConfig.isDemoCardId(card.cardId)) {
|
||||
demoConfig.demoBlockchains
|
||||
} else {
|
||||
listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
}
|
||||
|
||||
if (card.isTestCard) {
|
||||
blockchains = blockchains.mapNotNull { it.getTestnetVersion() }
|
||||
}
|
||||
|
||||
return blockchains.mapNotNull {
|
||||
cryptoCurrencyFactory.createCoin(
|
||||
blockchain = it,
|
||||
extraDerivationPath = null,
|
||||
derivationStyleProvider = cardDerivationStyleProvider,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency {
|
||||
val cardDerivationStyleProvider = scanResponse.derivationStyleProvider
|
||||
val resolver = scanResponse.cardTypesResolver
|
||||
val blockchain = resolver.getBlockchain()
|
||||
|
||||
val coin = cryptoCurrencyFactory.createCoin(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
derivationStyleProvider = cardDerivationStyleProvider,
|
||||
)
|
||||
requireNotNull(coin) { "Coin for the single currency card cannot be null" }
|
||||
|
||||
val primaryToken = resolver.getPrimaryToken()?.let { token ->
|
||||
cryptoCurrencyFactory.createToken(
|
||||
sdkToken = token,
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
derivationStyleProvider = cardDerivationStyleProvider,
|
||||
)
|
||||
}
|
||||
|
||||
return primaryToken ?: coin
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package com.tangem.data.tokens.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.common.DerivationStyleProvider
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
|
||||
internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) {
|
||||
|
||||
private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory() }
|
||||
|
||||
fun createDefaultCoinsForMultiCurrencyCard(
|
||||
card: CardDTO,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): List<CryptoCurrency.Coin> {
|
||||
var blockchains = if (demoConfig.isDemoCardId(card.cardId)) {
|
||||
demoConfig.demoBlockchains
|
||||
} else {
|
||||
listOf(Blockchain.Bitcoin, Blockchain.Ethereum)
|
||||
}
|
||||
|
||||
if (card.isTestCard) {
|
||||
blockchains = blockchains.mapNotNull { it.getTestnetVersion() }
|
||||
}
|
||||
|
||||
return blockchains.mapNotNull { cryptoCurrencyFactory.createCoin(it, derivationStyleProvider) }
|
||||
}
|
||||
|
||||
fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency {
|
||||
val derivationStyleProvider = scanResponse.derivationStyleProvider
|
||||
val resolver = scanResponse.cardTypesResolver
|
||||
val blockchain = resolver.getBlockchain()
|
||||
|
||||
val coin = requireNotNull(cryptoCurrencyFactory.createCoin(blockchain, derivationStyleProvider)) {
|
||||
"Coin for the single currency card cannot be null"
|
||||
}
|
||||
val primaryToken = resolver.getPrimaryToken()?.let { token ->
|
||||
cryptoCurrencyFactory.createToken(token, blockchain, derivationStyleProvider)
|
||||
}
|
||||
|
||||
return primaryToken ?: coin
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.data.tokens.utils
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.common.DerivationStyleProvider
|
||||
import com.tangem.domain.common.extensions.toCoinId
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import timber.log.Timber
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
|
|
@ -12,6 +13,7 @@ class CryptoCurrencyFactory {
|
|||
fun createToken(
|
||||
sdkToken: SdkToken,
|
||||
blockchain: Blockchain,
|
||||
extraDerivationPath: String?,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): CryptoCurrency.Token? {
|
||||
if (blockchain == Blockchain.Unknown) {
|
||||
|
|
@ -19,35 +21,40 @@ class CryptoCurrencyFactory {
|
|||
return null
|
||||
}
|
||||
|
||||
val id = getTokenId(blockchain, sdkToken)
|
||||
val network = getNetwork(blockchain, extraDerivationPath, derivationStyleProvider) ?: return null
|
||||
val id = getTokenId(network, sdkToken)
|
||||
|
||||
return CryptoCurrency.Token(
|
||||
id = id,
|
||||
network = getNetwork(blockchain) ?: return null,
|
||||
network = network,
|
||||
name = sdkToken.name,
|
||||
symbol = sdkToken.symbol,
|
||||
iconUrl = getTokenIconUrl(blockchain, sdkToken),
|
||||
decimals = sdkToken.decimals,
|
||||
isCustom = isCustomToken(id),
|
||||
isCustom = isCustomToken(id, network),
|
||||
contractAddress = sdkToken.contractAddress,
|
||||
derivationPath = getDerivationPath(blockchain, derivationStyleProvider),
|
||||
)
|
||||
}
|
||||
|
||||
fun createCoin(blockchain: Blockchain, derivationStyleProvider: DerivationStyleProvider): CryptoCurrency.Coin? {
|
||||
fun createCoin(
|
||||
blockchain: Blockchain,
|
||||
extraDerivationPath: String?,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): CryptoCurrency.Coin? {
|
||||
if (blockchain == Blockchain.Unknown) {
|
||||
Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain")
|
||||
return null
|
||||
}
|
||||
val network = getNetwork(blockchain, extraDerivationPath, derivationStyleProvider) ?: return null
|
||||
|
||||
return CryptoCurrency.Coin(
|
||||
id = getCoinId(blockchain),
|
||||
network = getNetwork(blockchain) ?: return null,
|
||||
id = getCoinId(network, blockchain.toCoinId()),
|
||||
network = network,
|
||||
name = blockchain.fullName,
|
||||
symbol = blockchain.currency,
|
||||
iconUrl = getCoinIconUrl(blockchain),
|
||||
decimals = blockchain.decimals(),
|
||||
derivationPath = getDerivationPath(blockchain, derivationStyleProvider),
|
||||
isCustom = isCustomCoin(network),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package com.tangem.data.tokens.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.tokens.models.Network
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class NetworkConverter : Converter<Network.ID, Network?> {
|
||||
|
||||
override fun convert(value: Network.ID): Network? {
|
||||
val blockchain = Blockchain.fromId(value.value)
|
||||
|
||||
return getNetwork(blockchain)
|
||||
}
|
||||
|
||||
override fun convertList(input: Collection<Network.ID>): List<Network> {
|
||||
return input.mapNotNull(::convert)
|
||||
}
|
||||
|
||||
override fun convertSet(input: Collection<Network.ID>): Set<Network> {
|
||||
return input.mapNotNullTo(hashSetOf(), ::convert)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,19 @@
|
|||
package com.tangem.data.tokens.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.domain.common.DerivationStyleProvider
|
||||
import com.tangem.domain.tokens.models.Network
|
||||
import timber.log.Timber
|
||||
|
||||
internal fun getNetwork(blockchain: Blockchain): Network? {
|
||||
internal fun getBlockchain(networkId: Network.ID): Blockchain {
|
||||
return Blockchain.fromId(networkId.value)
|
||||
}
|
||||
|
||||
internal fun getNetwork(
|
||||
blockchain: Blockchain,
|
||||
extraDerivationPath: String?,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): Network? {
|
||||
if (blockchain == Blockchain.Unknown) {
|
||||
Timber.e("Unable to convert Unknown blockchain to the domain network model")
|
||||
return null
|
||||
|
|
@ -14,10 +23,26 @@ internal fun getNetwork(blockchain: Blockchain): Network? {
|
|||
id = Network.ID(blockchain.id),
|
||||
name = blockchain.fullName,
|
||||
isTestnet = blockchain.isTestnet(),
|
||||
derivationPath = getDerivationPath(blockchain, extraDerivationPath, derivationStyleProvider),
|
||||
standardType = getNetworkStandardType(blockchain),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getDerivationPath(
|
||||
blockchain: Blockchain,
|
||||
extraDerivationPath: String?,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): Network.DerivationPath {
|
||||
val cardDerivationPath = getCardDerivationPath(blockchain, derivationStyleProvider)
|
||||
|
||||
return when {
|
||||
cardDerivationPath.isNullOrBlank() -> Network.DerivationPath.None
|
||||
extraDerivationPath == cardDerivationPath -> Network.DerivationPath.Card(extraDerivationPath)
|
||||
!extraDerivationPath.isNullOrBlank() -> Network.DerivationPath.Custom(extraDerivationPath)
|
||||
else -> Network.DerivationPath.None
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNetworkStandardType(blockchain: Blockchain): Network.StandardType {
|
||||
return when (blockchain) {
|
||||
Blockchain.Ethereum, Blockchain.EthereumTestnet -> Network.StandardType.ERC20
|
||||
|
|
@ -26,4 +51,8 @@ private fun getNetworkStandardType(blockchain: Blockchain): Network.StandardType
|
|||
Blockchain.Tron, Blockchain.TronTestnet -> Network.StandardType.TRC20
|
||||
else -> Network.StandardType.Unspecified(blockchain.name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCardDerivationPath(blockchain: Blockchain, derivationStyleProvider: DerivationStyleProvider): String? {
|
||||
return blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath
|
||||
}
|
||||
|
|
@ -14,12 +14,12 @@ import java.math.BigDecimal
|
|||
internal class NetworkStatusFactory {
|
||||
|
||||
fun createNetworkStatus(
|
||||
networkId: Network.ID,
|
||||
network: Network,
|
||||
result: UpdateWalletManagerResult,
|
||||
currencies: Set<CryptoCurrency>,
|
||||
): NetworkStatus {
|
||||
return NetworkStatus(
|
||||
networkId = networkId,
|
||||
network = network,
|
||||
value = when (result) {
|
||||
is UpdateWalletManagerResult.MissedDerivation -> NetworkStatus.MissedDerivation
|
||||
is UpdateWalletManagerResult.Unreachable -> NetworkStatus.Unreachable
|
||||
|
|
|
|||
|
|
@ -3,47 +3,57 @@ package com.tangem.data.tokens.utils
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.common.DerivationStyleProvider
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.domain.common.extensions.toCoinId
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import timber.log.Timber
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
|
||||
internal class ResponseCurrenciesFactory(private val demoConfig: DemoConfig) {
|
||||
internal class ResponseCryptoCurrenciesFactory(private val demoConfig: DemoConfig) {
|
||||
|
||||
fun createCurrency(currencyId: CryptoCurrency.ID, response: UserTokensResponse, card: CardDTO): CryptoCurrency {
|
||||
fun createCurrency(
|
||||
currencyId: CryptoCurrency.ID,
|
||||
response: UserTokensResponse,
|
||||
scanResponse: ScanResponse,
|
||||
): CryptoCurrency {
|
||||
val responseTokenId = currencyId.rawCurrencyId
|
||||
|
||||
val token = requireNotNull(response.tokens.firstOrNull { it.id == responseTokenId }) {
|
||||
"Unable find a token with provided ID: $responseTokenId"
|
||||
}
|
||||
|
||||
return requireNotNull(createCurrency(token, card)) {
|
||||
return requireNotNull(createCurrency(token, scanResponse)) {
|
||||
"Unable to create a currency with provided ID: $currencyId"
|
||||
}
|
||||
}
|
||||
|
||||
fun createCurrencies(response: UserTokensResponse, card: CardDTO): List<CryptoCurrency> {
|
||||
return response.tokens.mapNotNull { createCurrency(it, card) }
|
||||
fun createCurrencies(response: UserTokensResponse, scanResponse: ScanResponse): List<CryptoCurrency> {
|
||||
return response.tokens.mapNotNull { createCurrency(it, scanResponse) }
|
||||
}
|
||||
|
||||
fun createCurrency(responseToken: UserTokensResponse.Token, card: CardDTO): CryptoCurrency? {
|
||||
fun createCurrency(responseToken: UserTokensResponse.Token, scanResponse: ScanResponse): 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
|
||||
}
|
||||
|
||||
val cardDerivationStyleProvider = scanResponse.derivationStyleProvider
|
||||
val card = scanResponse.card
|
||||
|
||||
if (demoConfig.isDemoCardId(card.cardId)) {
|
||||
blockchain = blockchain.getTestnetVersion() ?: blockchain
|
||||
}
|
||||
|
||||
val sdkToken = createSdkToken(responseToken)
|
||||
return if (sdkToken == null) {
|
||||
createCoin(blockchain, responseToken)
|
||||
createCoin(blockchain, responseToken, cardDerivationStyleProvider)
|
||||
} else {
|
||||
createToken(blockchain, sdkToken, responseToken.derivationPath)
|
||||
createToken(blockchain, sdkToken, responseToken.derivationPath, cardDerivationStyleProvider)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,31 +69,43 @@ internal class ResponseCurrenciesFactory(private val demoConfig: DemoConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun createCoin(blockchain: Blockchain, responseToken: UserTokensResponse.Token): CryptoCurrency.Coin? {
|
||||
private fun createCoin(
|
||||
blockchain: Blockchain,
|
||||
responseToken: UserTokensResponse.Token,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): CryptoCurrency.Coin? {
|
||||
val network = getNetwork(blockchain, responseToken.derivationPath, derivationStyleProvider) ?: return null
|
||||
|
||||
return CryptoCurrency.Coin(
|
||||
id = getCoinId(blockchain),
|
||||
network = getNetwork(blockchain) ?: return null,
|
||||
id = getCoinId(network, blockchain.toCoinId()),
|
||||
network = network,
|
||||
name = responseToken.name,
|
||||
symbol = responseToken.symbol,
|
||||
decimals = responseToken.decimals,
|
||||
derivationPath = responseToken.derivationPath,
|
||||
iconUrl = getCoinIconUrl(blockchain),
|
||||
isCustom = isCustomCoin(network),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createToken(blockchain: Blockchain, sdkToken: Token, derivationPath: String?): CryptoCurrency.Token? {
|
||||
val id = getTokenId(blockchain, sdkToken)
|
||||
private fun createToken(
|
||||
blockchain: Blockchain,
|
||||
sdkToken: Token,
|
||||
responseDerivationPath: String?,
|
||||
derivationStyleProvider: DerivationStyleProvider,
|
||||
): CryptoCurrency.Token? {
|
||||
val network = getNetwork(blockchain, responseDerivationPath, derivationStyleProvider)
|
||||
?: return null
|
||||
val id = getTokenId(network, sdkToken)
|
||||
|
||||
return CryptoCurrency.Token(
|
||||
id = id,
|
||||
network = getNetwork(blockchain) ?: return null,
|
||||
network = network,
|
||||
name = sdkToken.name,
|
||||
symbol = sdkToken.symbol,
|
||||
decimals = sdkToken.decimals,
|
||||
derivationPath = derivationPath,
|
||||
iconUrl = getTokenIconUrl(blockchain, sdkToken),
|
||||
contractAddress = sdkToken.contractAddress,
|
||||
isCustom = isCustomToken(id),
|
||||
isCustom = isCustomToken(id, network),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,13 @@ package com.tangem.data.tokens.utils
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.IconsUtil
|
||||
import com.tangem.domain.common.DerivationStyleProvider
|
||||
import com.tangem.domain.common.extensions.toCoinId
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency.ID
|
||||
import com.tangem.domain.tokens.models.Network
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency.ID.Body as CurrencyIdBody
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency.ID.Prefix.COIN_PREFIX as COIN_ID_PREFIX
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency.ID.Prefix.CUSTOM_TOKEN_PREFIX as CUSTOM_TOKEN_ID_PREFIX
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency.ID.Prefix.TOKEN_PREFIX as TOKEN_ID_PREFIX
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency.ID.Suffix.ContractAddress as CustomCurrencyIdSuffix
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency.ID.Suffix.RawID as CurrencyIdSuffix
|
||||
|
|
@ -18,24 +17,27 @@ private const val DEFAULT_TOKENS_ICONS_HOST = "https://s3.eu-central-1.amazonaws
|
|||
private const val TOKEN_ICON_SIZE = "large"
|
||||
private const val TOKEN_ICON_EXT = "png"
|
||||
|
||||
internal fun isCustomToken(tokenId: ID): Boolean {
|
||||
return tokenId.rawCurrencyId == null
|
||||
internal fun isCustomToken(tokenId: ID, network: Network): Boolean {
|
||||
return network.derivationPath is Network.DerivationPath.Custom || tokenId.rawCurrencyId == null
|
||||
}
|
||||
|
||||
internal fun getDerivationPath(blockchain: Blockchain, derivationStyleProvider: DerivationStyleProvider): String? {
|
||||
return blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath
|
||||
internal fun isCustomCoin(network: Network): Boolean {
|
||||
return network.derivationPath is Network.DerivationPath.Custom
|
||||
}
|
||||
|
||||
internal fun getBlockchain(networkId: Network.ID): Blockchain {
|
||||
return Blockchain.fromId(networkId.value)
|
||||
internal fun getCoinId(network: Network, coinId: String): ID {
|
||||
return ID(COIN_ID_PREFIX, getCurrencyIdBody(network), CurrencyIdSuffix(rawId = coinId))
|
||||
}
|
||||
|
||||
internal fun getCoinId(blockchain: Blockchain): ID {
|
||||
return getTokenOrCoinId(blockchain, token = null)
|
||||
}
|
||||
internal fun getTokenId(network: Network, sdkToken: SdkToken): ID {
|
||||
val sdkTokenId = sdkToken.id
|
||||
val suffix = if (sdkTokenId == null) {
|
||||
CustomCurrencyIdSuffix(contractAddress = sdkToken.contractAddress)
|
||||
} else {
|
||||
CurrencyIdSuffix(rawId = sdkTokenId)
|
||||
}
|
||||
|
||||
internal fun getTokenId(blockchain: Blockchain, token: SdkToken): ID {
|
||||
return getTokenOrCoinId(blockchain, token)
|
||||
return ID(TOKEN_ID_PREFIX, getCurrencyIdBody(network), suffix)
|
||||
}
|
||||
|
||||
internal fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? {
|
||||
|
|
@ -58,15 +60,16 @@ internal fun getCoinIconUrl(blockchain: Blockchain): String? {
|
|||
return coinId?.let(::getTokenIconUrlFromDefaultHost)
|
||||
}
|
||||
|
||||
private fun getTokenOrCoinId(blockchain: Blockchain, token: SdkToken?): ID {
|
||||
val sdkTokenId = token?.id
|
||||
val (prefix, suffix) = when {
|
||||
token == null -> COIN_ID_PREFIX to CurrencyIdSuffix(rawId = blockchain.toCoinId())
|
||||
sdkTokenId == null -> CUSTOM_TOKEN_ID_PREFIX to CustomCurrencyIdSuffix(contractAddress = token.contractAddress)
|
||||
else -> TOKEN_ID_PREFIX to CurrencyIdSuffix(rawId = sdkTokenId)
|
||||
private fun getCurrencyIdBody(network: Network): CurrencyIdBody {
|
||||
return when (val path = network.derivationPath) {
|
||||
is Network.DerivationPath.Custom -> CurrencyIdBody.NetworkIdWithDerivationPath(
|
||||
rawId = network.id.value,
|
||||
derivationPath = path.value,
|
||||
)
|
||||
is Network.DerivationPath.Card,
|
||||
is Network.DerivationPath.None,
|
||||
-> CurrencyIdBody.NetworkId(network.id.value)
|
||||
}
|
||||
|
||||
return ID(prefix, Network.ID(blockchain.id), suffix)
|
||||
}
|
||||
|
||||
private fun getTokenIconUrlFromDefaultHost(tokenId: String): String {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ internal class UserTokensResponseFactory {
|
|||
return UserTokensResponse.Token(
|
||||
id = currency.id.rawCurrencyId,
|
||||
networkId = blockchain.toNetworkId(),
|
||||
derivationPath = currency.derivationPath,
|
||||
derivationPath = currency.network.derivationPath.value,
|
||||
name = currency.name,
|
||||
symbol = currency.symbol,
|
||||
decimals = currency.decimals,
|
||||
|
|
|
|||
|
|
@ -19,12 +19,11 @@ class DefaultTxHistoryRepository(
|
|||
private val userWalletsStore: UserWalletsStore,
|
||||
) : TxHistoryRepository {
|
||||
|
||||
override suspend fun getTxHistoryItemsCount(networkId: Network.ID, derivationPath: String?): Int {
|
||||
override suspend fun getTxHistoryItemsCount(network: Network): Int {
|
||||
val userWallet = getUserWallet()
|
||||
val state = walletManagersFacade.getTxHistoryState(
|
||||
userWalletId = userWallet.walletId,
|
||||
networkId = networkId,
|
||||
rawDerivationPath = derivationPath,
|
||||
network = network,
|
||||
)
|
||||
return when (state) {
|
||||
is TxHistoryState.Failed.FetchError -> throw TxHistoryStateError.DataError(state.exception)
|
||||
|
|
@ -34,11 +33,7 @@ class DefaultTxHistoryRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getTxHistoryItems(
|
||||
networkId: Network.ID,
|
||||
derivationPath: String?,
|
||||
pageSize: Int,
|
||||
): Flow<PagingData<TxHistoryItem>> {
|
||||
override fun getTxHistoryItems(network: Network, pageSize: Int): Flow<PagingData<TxHistoryItem>> {
|
||||
val userWallet = getUserWallet()
|
||||
return Pager(
|
||||
config = PagingConfig(
|
||||
|
|
@ -49,8 +44,7 @@ class DefaultTxHistoryRepository(
|
|||
loadPage = { page: Int, pageSize: Int ->
|
||||
walletManagersFacade.getTxHistoryItems(
|
||||
userWalletId = userWallet.walletId,
|
||||
networkId = networkId,
|
||||
rawDerivationPath = derivationPath,
|
||||
network = network,
|
||||
page = page,
|
||||
pageSize = pageSize,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import com.tangem.crypto.hdWallet.DerivationPath
|
|||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.datasource.local.walletmanager.WalletManagersStore
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.common.util.hasDerivation
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
|
|
@ -39,42 +38,46 @@ class DefaultWalletManagersFacade(
|
|||
|
||||
override suspend fun update(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
network: Network,
|
||||
extraTokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
val blockchain = Blockchain.fromId(networkId.value)
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val derivationPath = network.derivationPath.value
|
||||
|
||||
return getAndUpdateWalletManager(userWallet, blockchain, extraTokens)
|
||||
return getAndUpdateWalletManager(userWallet, blockchain, derivationPath, extraTokens)
|
||||
}
|
||||
|
||||
override suspend fun getExploreUrl(userWalletId: UserWalletId, networkId: Network.ID): String {
|
||||
override suspend fun getExploreUrl(userWalletId: UserWalletId, network: Network): String {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
|
||||
val blockchain = Blockchain.fromId(networkId.value)
|
||||
|
||||
return getOrCreateWalletManager(
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWallet = userWallet,
|
||||
blockchain = blockchain,
|
||||
derivationPath = blockchain
|
||||
.derivationPath(userWallet.scanResponse.derivationStyleProvider.getDerivationStyle()),
|
||||
derivationPath = network.derivationPath.value,
|
||||
)
|
||||
?.wallet
|
||||
?.getExploreUrl()
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
override suspend fun getTxHistoryState(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
rawDerivationPath: String?,
|
||||
): TxHistoryState {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
val blockchain = Blockchain.fromId(networkId.value)
|
||||
val derivationPath = rawDerivationPath?.let(::DerivationPath)
|
||||
val walletManager = requireNotNull(getOrCreateWalletManager(userWallet, blockchain, derivationPath)) {
|
||||
requireNotNull(walletManager) {
|
||||
"Unable to get a wallet manager for blockchain: $blockchain"
|
||||
}
|
||||
|
||||
return walletManager.wallet.getExploreUrl()
|
||||
}
|
||||
|
||||
override suspend fun getTxHistoryState(userWalletId: UserWalletId, network: Network): TxHistoryState {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWallet = userWallet,
|
||||
blockchain = blockchain,
|
||||
derivationPath = network.derivationPath.value,
|
||||
)
|
||||
|
||||
requireNotNull(walletManager) {
|
||||
"Unable to get a wallet manager for blockchain: $blockchain"
|
||||
}
|
||||
|
||||
return walletManager
|
||||
.getTransactionHistoryState(walletManager.wallet.address)
|
||||
.let(txHistoryStateConverter::convert)
|
||||
|
|
@ -82,17 +85,22 @@ class DefaultWalletManagersFacade(
|
|||
|
||||
override suspend fun getTxHistoryItems(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
rawDerivationPath: String?,
|
||||
network: Network,
|
||||
page: Int,
|
||||
pageSize: Int,
|
||||
): PaginationWrapper<TxHistoryItem> {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
val blockchain = Blockchain.fromId(networkId.value)
|
||||
val derivationPath = rawDerivationPath?.let(::DerivationPath)
|
||||
val walletManager = requireNotNull(getOrCreateWalletManager(userWallet, blockchain, derivationPath)) {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWallet = userWallet,
|
||||
blockchain = blockchain,
|
||||
derivationPath = network.derivationPath.value,
|
||||
)
|
||||
|
||||
requireNotNull(walletManager) {
|
||||
"Unable to get a wallet manager for blockchain: $blockchain"
|
||||
}
|
||||
|
||||
val itemsResult = walletManager.getTransactionsHistory(
|
||||
address = walletManager.wallet.address,
|
||||
page = page,
|
||||
|
|
@ -118,12 +126,12 @@ class DefaultWalletManagersFacade(
|
|||
private suspend fun getAndUpdateWalletManager(
|
||||
userWallet: UserWallet,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: String?,
|
||||
extraTokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult {
|
||||
val scanResponse = userWallet.scanResponse
|
||||
val derivationPath = blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())
|
||||
|
||||
if (derivationPath != null && !scanResponse.hasDerivation(blockchain, derivationPath.rawPath)) {
|
||||
if (derivationPath != null && !scanResponse.hasDerivation(blockchain, derivationPath)) {
|
||||
Timber.e("Derivation missed for: $blockchain")
|
||||
return UpdateWalletManagerResult.MissedDerivation
|
||||
}
|
||||
|
|
@ -171,21 +179,21 @@ class DefaultWalletManagersFacade(
|
|||
override suspend fun getOrCreateWalletManager(
|
||||
userWallet: UserWallet,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: DerivationPath?,
|
||||
derivationPath: String?,
|
||||
): WalletManager? {
|
||||
val userWalletId = userWallet.walletId
|
||||
|
||||
var walletManager = walletManagersStore.getSyncOrNull(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = derivationPath?.rawPath,
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
|
||||
if (walletManager == null) {
|
||||
walletManager = walletManagerFactory.createWalletManager(
|
||||
scanResponse = userWallet.scanResponse,
|
||||
blockchain = blockchain,
|
||||
derivationPath = derivationPath,
|
||||
derivationPath = derivationPath?.let { DerivationPath(rawPath = it) },
|
||||
) ?: return null
|
||||
|
||||
walletManagersStore.store(userWalletId, walletManager)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.domain.walletmanager
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.domain.tokens.models.Network
|
||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||
|
|
@ -22,52 +21,53 @@ interface WalletManagersFacade {
|
|||
* Updates the wallet manager associated with a user's wallet and network.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param networkId The network ID.
|
||||
* @param network The network.
|
||||
* @param extraTokens Additional tokens.
|
||||
* @return The result of updating the wallet manager.
|
||||
*/
|
||||
suspend fun update(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
network: Network,
|
||||
extraTokens: Set<CryptoCurrency.Token>,
|
||||
): UpdateWalletManagerResult
|
||||
|
||||
suspend fun getExploreUrl(userWalletId: UserWalletId, networkId: Network.ID): String
|
||||
/**
|
||||
* Returns network explorer URL of the wallet manager associated with a user's wallet and network.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param network The network.
|
||||
*
|
||||
* @return The network explorer URL, maybe empty if the wallet manager was not found.
|
||||
* */
|
||||
suspend fun getExploreUrl(userWalletId: UserWalletId, network: Network): String
|
||||
|
||||
/**
|
||||
* Returns transactions count
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param networkId The network ID.
|
||||
* @param rawDerivationPath Derivation path in raw form.
|
||||
|
||||
* @param network The network.
|
||||
*/
|
||||
suspend fun getTxHistoryState(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
rawDerivationPath: String?,
|
||||
): TxHistoryState
|
||||
suspend fun getTxHistoryState(userWalletId: UserWalletId, network: Network): TxHistoryState
|
||||
|
||||
/**
|
||||
* Returns transaction history items wrapped to pagination
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param networkId The network ID.
|
||||
* @param rawDerivationPath Derivation path in raw form.
|
||||
* @param network The network.
|
||||
* @param page Pagination page.
|
||||
* @param pageSize Pagination size.
|
||||
*/
|
||||
suspend fun getTxHistoryItems(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
rawDerivationPath: String?,
|
||||
network: Network,
|
||||
page: Int,
|
||||
pageSize: Int,
|
||||
): PaginationWrapper<TxHistoryItem>
|
||||
|
||||
// TODO: Remove after refactoring
|
||||
suspend fun getOrCreateWalletManager(
|
||||
userWallet: UserWallet,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: DerivationPath?,
|
||||
derivationPath: String?,
|
||||
): WalletManager?
|
||||
}
|
||||
|
|
@ -11,8 +11,7 @@ import java.io.Serializable
|
|||
* @property symbol Symbol of the cryptocurrency.
|
||||
* @property decimals Number of decimal places used by the cryptocurrency.
|
||||
* @property iconUrl Optional URL of the cryptocurrency icon. `null` if not found.
|
||||
* @property derivationPath Optional path used for key derivation. `null` if the wallet does not support the
|
||||
* [HD Wallet](https://coinsutra.com/hd-wallets-deterministic-wallet/) feature.
|
||||
* @property isCustom Indicates whether the currency is a custom user-added currency or not.
|
||||
*/
|
||||
// FIXME: Remove serialization [REDACTED_JIRA]
|
||||
sealed class CryptoCurrency : Serializable {
|
||||
|
|
@ -23,7 +22,7 @@ sealed class CryptoCurrency : Serializable {
|
|||
abstract val symbol: String
|
||||
abstract val decimals: Int
|
||||
abstract val iconUrl: String?
|
||||
abstract val derivationPath: String?
|
||||
abstract val isCustom: Boolean
|
||||
|
||||
/**
|
||||
* Represents a native coin in the blockchain network.
|
||||
|
|
@ -35,7 +34,7 @@ sealed class CryptoCurrency : Serializable {
|
|||
override val symbol: String,
|
||||
override val decimals: Int,
|
||||
override val iconUrl: String?,
|
||||
override val derivationPath: String?,
|
||||
override val isCustom: Boolean,
|
||||
) : CryptoCurrency() {
|
||||
|
||||
init {
|
||||
|
|
@ -47,7 +46,6 @@ sealed class CryptoCurrency : Serializable {
|
|||
* Represents a token in the blockchain network, typically a non-native asset.
|
||||
*
|
||||
* @property contractAddress Address of the contract managing the token.
|
||||
* @property isCustom Indicates whether the token is a custom user-added token or not.
|
||||
*/
|
||||
data class Token(
|
||||
override val id: ID,
|
||||
|
|
@ -56,9 +54,8 @@ sealed class CryptoCurrency : Serializable {
|
|||
override val symbol: String,
|
||||
override val decimals: Int,
|
||||
override val iconUrl: String?,
|
||||
override val derivationPath: String?,
|
||||
override val isCustom: Boolean,
|
||||
val contractAddress: String,
|
||||
val isCustom: Boolean,
|
||||
) : CryptoCurrency() {
|
||||
|
||||
init {
|
||||
|
|
@ -80,34 +77,70 @@ sealed class CryptoCurrency : Serializable {
|
|||
// FIXME: Remove serialization [REDACTED_JIRA]
|
||||
data class ID(
|
||||
private val prefix: Prefix,
|
||||
private val networkId: Network.ID,
|
||||
private val body: Body,
|
||||
private val suffix: Suffix,
|
||||
) : Serializable {
|
||||
|
||||
val value: String = buildString {
|
||||
append(prefix.value)
|
||||
append(networkId.value)
|
||||
append(DELIMITER)
|
||||
append(PREFIX_DELIMITER)
|
||||
append(body.value)
|
||||
append(SUFFIX_DELIMITER)
|
||||
append(suffix.value)
|
||||
}
|
||||
|
||||
/** Represents a raw cryptocurrency ID. If it is a custom token, the value will be `null`. */
|
||||
val rawCurrencyId: String? = (suffix as? Suffix.RawID)?.rawId
|
||||
|
||||
val rawNetworkId: String = networkId.value
|
||||
/** Represents a raw cryptocurrency's network ID. */
|
||||
val rawNetworkId: String = when (body) {
|
||||
is Body.NetworkId -> body.rawId
|
||||
is Body.NetworkIdWithDerivationPath -> body.rawId
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the different types of prefixes that can be associated with a cryptocurrency ID.
|
||||
*
|
||||
* These prefixes can help in quickly categorizing the type of cryptocurrency.
|
||||
*/
|
||||
enum class Prefix(val value: String) {
|
||||
/** Prefix for standard coins. */
|
||||
COIN_PREFIX(value = "coin_"),
|
||||
COIN_PREFIX(value = "coin"),
|
||||
|
||||
/** Prefix for standard tokens. */
|
||||
TOKEN_PREFIX(value = "token_"),
|
||||
TOKEN_PREFIX(value = "token"),
|
||||
}
|
||||
|
||||
/** Prefix for custom tokens. */
|
||||
CUSTOM_TOKEN_PREFIX(value = "custom_"),
|
||||
/**
|
||||
* Represents the body part of the cryptocurrency ID.
|
||||
*
|
||||
* The body can be either a raw network ID or a raw network ID with a network derivation path.
|
||||
*/
|
||||
sealed class Body {
|
||||
|
||||
/** The value of the body. */
|
||||
abstract val value: String
|
||||
|
||||
/** Represents a raw network ID. */
|
||||
data class NetworkId(val rawId: String) : Body() {
|
||||
override val value: String = rawId
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a raw network ID with a network derivation path.
|
||||
*
|
||||
* Should be used for a cryptocurrencies with custom derivation path.
|
||||
* */
|
||||
data class NetworkIdWithDerivationPath(
|
||||
val rawId: String,
|
||||
val derivationPath: String,
|
||||
) : Body() {
|
||||
override val value: String = buildString {
|
||||
append(rawId)
|
||||
append(DERIVATION_PATH_DELIMITER)
|
||||
append(derivationPath.hashCode())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -132,8 +165,14 @@ sealed class CryptoCurrency : Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ID(value='$value')"
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val DELIMITER = '#'
|
||||
const val PREFIX_DELIMITER = '_'
|
||||
const val SUFFIX_DELIMITER = '#'
|
||||
const val DERIVATION_PATH_DELIMITER = 'd'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -142,6 +181,5 @@ sealed class CryptoCurrency : Serializable {
|
|||
require(symbol.isNotBlank()) { "Crypto currency symbol must not be blank" }
|
||||
require(iconUrl?.isNotBlank() ?: true) { "Crypto currency icon URL must not be blank" }
|
||||
require(decimals >= 0) { "Crypto currency decimal must not be less then zero, but it is: $decimals" }
|
||||
require(derivationPath?.isNotBlank() ?: true) { "Crypto currency derivation path must not be blank" }
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import java.io.Serializable
|
|||
*
|
||||
* @property id The unique identifier of the network.
|
||||
* @property name The human-readable name of the network, such as "Ethereum" or "Bitcoin".
|
||||
* @property derivationPath The path used to derive keys for this network.
|
||||
* @property isTestnet Indicates whether the network is a test network or a main network.
|
||||
* @property standardType The type of blockchain standard the network adheres to.
|
||||
*/
|
||||
|
|
@ -18,6 +19,7 @@ import java.io.Serializable
|
|||
data class Network(
|
||||
val id: ID,
|
||||
val name: String,
|
||||
val derivationPath: DerivationPath,
|
||||
val isTestnet: Boolean,
|
||||
val standardType: StandardType,
|
||||
) : Serializable {
|
||||
|
|
@ -39,6 +41,39 @@ data class Network(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a path used to derive cryptographic keys for a blockchain network.
|
||||
*
|
||||
* This class represents such paths in a generic manner, allowing for predefined card-based paths,
|
||||
* custom paths, or even no derivation path at all.
|
||||
*/
|
||||
sealed class DerivationPath {
|
||||
|
||||
/** The actual derivation path value, if any. */
|
||||
abstract val value: String?
|
||||
|
||||
/**
|
||||
* Represents a predefined card-based derivation path.
|
||||
*
|
||||
* @property value The derivation path string.
|
||||
*/
|
||||
data class Card(override val value: String) : DerivationPath()
|
||||
|
||||
/**
|
||||
* Represents a custom derivation path specified by the user.
|
||||
*
|
||||
* @property value The derivation path string.
|
||||
*/
|
||||
data class Custom(override val value: String) : DerivationPath()
|
||||
|
||||
/**
|
||||
* Represents a lack of derivation path.
|
||||
*/
|
||||
object None : DerivationPath() {
|
||||
override val value: String? = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the type of blockchain standard that a network adheres to.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class FetchCurrencyStatusUseCase(
|
|||
refresh: Boolean,
|
||||
) = coroutineScope {
|
||||
val fetchStatus = async {
|
||||
fetchNetworkStatus(userWalletId, currency.network.id, refresh)
|
||||
fetchNetworkStatus(userWalletId, currency.network, refresh)
|
||||
}
|
||||
val fetchQuote = async {
|
||||
fetchQuote(currency.id, refresh)
|
||||
|
|
@ -101,11 +101,11 @@ class FetchCurrencyStatusUseCase(
|
|||
|
||||
private suspend fun Raise<CurrencyStatusError>.fetchNetworkStatus(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
network: Network,
|
||||
refresh: Boolean,
|
||||
) {
|
||||
catch(
|
||||
block = { networksRepository.getNetworkStatusesSync(userWalletId, setOf(networkId), refresh) },
|
||||
block = { networksRepository.getNetworkStatusesSync(userWalletId, setOf(network), refresh) },
|
||||
) {
|
||||
raise(CurrencyStatusError.DataError(it))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class FetchTokenListUseCase(
|
|||
val fetchStatuses = async {
|
||||
fetchNetworksStatuses(
|
||||
userWalletId,
|
||||
currencies.mapTo(hashSetOf()) { it.network.id },
|
||||
currencies.mapTo(hashSetOf()) { it.network },
|
||||
refresh,
|
||||
)
|
||||
}
|
||||
|
|
@ -81,11 +81,11 @@ class FetchTokenListUseCase(
|
|||
|
||||
private suspend fun Raise<TokenListError>.fetchNetworksStatuses(
|
||||
userWalletId: UserWalletId,
|
||||
networksIds: Set<Network.ID>,
|
||||
networks: Set<Network>,
|
||||
refresh: Boolean,
|
||||
) {
|
||||
catch(
|
||||
block = { networksRepository.getNetworkStatusesSync(userWalletId, networksIds, refresh) },
|
||||
block = { networksRepository.getNetworkStatusesSync(userWalletId, networks, refresh) },
|
||||
) {
|
||||
raise(TokenListError.DataError(it))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,10 +27,16 @@ class RemoveCurrencyUseCase(
|
|||
}
|
||||
|
||||
suspend fun hasLinkedTokens(userWalletId: UserWalletId, currency: CryptoCurrency): Boolean {
|
||||
val walletCurrencies = currenciesRepository
|
||||
.getMultiCurrencyWalletCurrenciesSync(userWalletId = userWalletId, refresh = false)
|
||||
return when (currency) {
|
||||
is CryptoCurrency.Coin -> {
|
||||
val walletCurrencies = currenciesRepository.getMultiCurrencyWalletCurrenciesSync(
|
||||
userWalletId = userWalletId,
|
||||
refresh = false,
|
||||
)
|
||||
|
||||
return currency is CryptoCurrency.Coin &&
|
||||
walletCurrencies.any { it != currency && it.network.id == currency.network.id }
|
||||
walletCurrencies.any { it is CryptoCurrency.Token && it.network == currency.network }
|
||||
}
|
||||
is CryptoCurrency.Token -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import java.math.BigDecimal
|
|||
/**
|
||||
* Represents the status of a cryptocurrency asset within a network.
|
||||
*
|
||||
* This class encapsulates the details of a specific cryptocurrency, either a coin or token,
|
||||
* This class encapsulates the details of a specific cryptocurrency, either a coin or cryptocurrency,
|
||||
* along with its current status within the blockchain network. The status can include various states
|
||||
* like Loading, Unreachable, Loaded, etc.
|
||||
*
|
||||
|
|
@ -20,22 +20,22 @@ data class CryptoCurrencyStatus(
|
|||
) {
|
||||
|
||||
/**
|
||||
* Represents the various states a token can have, encapsulating different information based on the state.
|
||||
* Represents the various states a cryptocurrency can have, encapsulating different information based on the state.
|
||||
*
|
||||
* @property isError Indicates whether this status represents an error status.
|
||||
*/
|
||||
sealed class Status(val isError: Boolean) {
|
||||
|
||||
/** The amount of the token. */
|
||||
/** The amount of the cryptocurrency. */
|
||||
open val amount: BigDecimal? = null
|
||||
|
||||
/** The fiat equivalent of the token's amount. */
|
||||
/** The fiat equivalent of the cryptocurrency's amount. */
|
||||
open val fiatAmount: BigDecimal? = null
|
||||
|
||||
/** The exchange rate used for converting the token amount to fiat. */
|
||||
/** The exchange rate used for converting the cryptocurrency amount to fiat. */
|
||||
open val fiatRate: BigDecimal? = null
|
||||
|
||||
/** The change in price of the token. */
|
||||
/** The change in price of the cryptocurrency. */
|
||||
open val priceChange: BigDecimal? = null
|
||||
|
||||
/** Indicates if there are any transactions in progress related to the cryptocurrency network. */
|
||||
|
|
@ -48,25 +48,28 @@ data class CryptoCurrencyStatus(
|
|||
open val networkAddress: NetworkAddress? = null
|
||||
}
|
||||
|
||||
/** Represents the Loading state of a token, typically while fetching its details. */
|
||||
/** Represents the Loading state of a cryptocurrency, typically while fetching its details. */
|
||||
object Loading : Status(isError = false)
|
||||
|
||||
/** Represents a state where the token is not reachable. */
|
||||
/** Represents a state where the cryptocurrency is not reachable. */
|
||||
object Unreachable : Status(isError = true)
|
||||
|
||||
/** Represents a state where the token's derivation is missed. */
|
||||
/** Represents a state where the cryptocurrency's network amount not found. */
|
||||
object NoAmount : Status(isError = true)
|
||||
|
||||
/** Represents a state where the cryptocurrency's derivation is missed. */
|
||||
object MissedDerivation : Status(isError = true)
|
||||
|
||||
/** Represents a state where there is no account associated with the token. */
|
||||
/** Represents a state where there is no account associated with the cryptocurrency. */
|
||||
object NoAccount : Status(isError = false)
|
||||
|
||||
/**
|
||||
* Represents a Loaded state of a token with complete information.
|
||||
* Represents a Loaded state of a cryptocurrency with complete information.
|
||||
*
|
||||
* @property amount The amount of the token.
|
||||
* @property fiatAmount The fiat equivalent of the token's amount.
|
||||
* @property fiatRate The exchange rate used for converting the token amount to fiat.
|
||||
* @property priceChange The change in price of the token.
|
||||
* @property amount The amount of the cryptocurrency.
|
||||
* @property fiatAmount The fiat equivalent of the cryptocurrency's amount.
|
||||
* @property fiatRate The exchange rate used for converting the cryptocurrency amount to fiat.
|
||||
* @property priceChange The change in price of the cryptocurrency.
|
||||
* @property hasCurrentNetworkTransactions Indicates if there are any transactions in progress related to the
|
||||
* cryptocurrency network.
|
||||
* @property pendingTransactions The current cryptocurrency transactions.
|
||||
|
|
@ -82,12 +85,12 @@ data class CryptoCurrencyStatus(
|
|||
) : Status(isError = false)
|
||||
|
||||
/**
|
||||
* Represents a Custom state of a token, typically used for user-defined tokens.
|
||||
* Represents a Custom state of a cryptocurrency, typically used for user-defined tokens.
|
||||
*
|
||||
* @property amount The amount of the token.
|
||||
* @property fiatAmount The fiat equivalent of the token's amount (optional).
|
||||
* @property fiatRate The exchange rate used for converting the token amount to fiat (optional).
|
||||
* @property priceChange The change in price of the token (optional).
|
||||
* @property amount The amount of the cryptocurrency.
|
||||
* @property fiatAmount The fiat equivalent of the cryptocurrency's amount (optional).
|
||||
* @property fiatRate The exchange rate used for converting the cryptocurrency amount to fiat (optional).
|
||||
* @property priceChange The change in price of the cryptocurrency (optional).
|
||||
* @property hasCurrentNetworkTransactions Indicates if there are any transactions in progress related to the
|
||||
* cryptocurrency network.
|
||||
* @property pendingTransactions The current cryptocurrency transactions.
|
||||
|
|
@ -103,9 +106,9 @@ data class CryptoCurrencyStatus(
|
|||
) : Status(isError = false)
|
||||
|
||||
/**
|
||||
* Represents a state where the token is available, but there is no current quote available for it.
|
||||
* Represents a state where the cryptocurrency is available, but there is no current quote available for it.
|
||||
*
|
||||
* @property amount The amount of the token.
|
||||
* @property amount The amount of the cryptocurrency.
|
||||
* @property hasCurrentNetworkTransactions Indicates if there are any transactions in progress related to the
|
||||
* cryptocurrency network.
|
||||
* @property pendingTransactions The current cryptocurrency transactions.
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ import java.math.BigDecimal
|
|||
/**
|
||||
* Represents the status of a specific blockchain network.
|
||||
*
|
||||
* @property networkId The unique identifier of the network for which the status is provided.
|
||||
* @property network The network for which the status is provided.
|
||||
* @property value The specific status value, represented as a sealed class to encapsulate the various possible states of the network.
|
||||
*/
|
||||
data class NetworkStatus(
|
||||
val networkId: Network.ID,
|
||||
val network: Network,
|
||||
val value: Status,
|
||||
) {
|
||||
|
||||
|
|
|
|||
|
|
@ -58,11 +58,11 @@ internal class CurrenciesStatusesOperations(
|
|||
|
||||
emit(maybeLoadingCurrenciesStatuses)
|
||||
|
||||
val (networksIds, currenciesIds) = getIds(nonEmptyCurrencies)
|
||||
val (networks, currenciesIds) = getIds(nonEmptyCurrencies)
|
||||
|
||||
val currenciesFlow = combine(
|
||||
getQuotes(currenciesIds),
|
||||
getNetworksStatuses(networksIds),
|
||||
getNetworksStatuses(networks),
|
||||
) { maybeQuotes, maybeNetworksStatuses ->
|
||||
createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes, maybeNetworksStatuses)
|
||||
}
|
||||
|
|
@ -99,7 +99,7 @@ internal class CurrenciesStatusesOperations(
|
|||
}
|
||||
|
||||
private fun getCurrencyStatusFlow(currency: CryptoCurrency): Flow<Either<Error, CryptoCurrencyStatus>> {
|
||||
val (networksIds, currenciesIds) = getIds(nonEmptyListOf(currency))
|
||||
val (networks, currenciesIds) = getIds(nonEmptyListOf(currency))
|
||||
|
||||
val quoteFlow = getQuotes(currenciesIds)
|
||||
.map { maybeQuotes ->
|
||||
|
|
@ -108,15 +108,15 @@ internal class CurrenciesStatusesOperations(
|
|||
}
|
||||
}
|
||||
|
||||
val statusFlow = getNetworksStatuses(networksIds)
|
||||
val statusFlow = getNetworksStatuses(networks)
|
||||
.map { maybeStatuses ->
|
||||
maybeStatuses.map { statuses ->
|
||||
statuses.singleOrNull { it.networkId == currency.network.id }
|
||||
statuses.singleOrNull { it.network == currency.network }
|
||||
}
|
||||
}
|
||||
|
||||
return combine(quoteFlow, statusFlow) { maybeQuote, maybeNetworkStatus ->
|
||||
createStatus(currency, maybeQuote, maybeNetworkStatus)
|
||||
createCurrencyStatus(currency, maybeQuote, maybeNetworkStatus)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,13 +135,13 @@ internal class CurrenciesStatusesOperations(
|
|||
|
||||
currencies.map { currency ->
|
||||
val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId }
|
||||
val networkStatus = networksStatuses?.firstOrNull { it.networkId == currency.network.id }
|
||||
val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network }
|
||||
|
||||
createStatus(currency, quote, networkStatus, ignoreQuote = quotesRetrievingFailed)
|
||||
createCurrencyStatus(currency, quote, networkStatus, ignoreQuote = quotesRetrievingFailed)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createStatus(
|
||||
private fun createCurrencyStatus(
|
||||
currency: CryptoCurrency,
|
||||
maybeQuote: Either<Error, Quote?>,
|
||||
maybeNetworkStatus: Either<Error, NetworkStatus?>,
|
||||
|
|
@ -154,10 +154,10 @@ internal class CurrenciesStatusesOperations(
|
|||
null
|
||||
}
|
||||
|
||||
createStatus(currency, quote, networkStatus, ignoreQuote = quoteRetrievingFailed)
|
||||
createCurrencyStatus(currency, quote, networkStatus, ignoreQuote = quoteRetrievingFailed)
|
||||
}
|
||||
|
||||
private fun createStatus(
|
||||
private fun createCurrencyStatus(
|
||||
currency: CryptoCurrency,
|
||||
quote: Quote?,
|
||||
networkStatus: NetworkStatus?,
|
||||
|
|
@ -206,7 +206,7 @@ internal class CurrenciesStatusesOperations(
|
|||
.onEmpty { emit(Error.EmptyQuotes.left()) }
|
||||
}
|
||||
|
||||
private fun getNetworksStatuses(networks: NonEmptySet<Network.ID>): Flow<Either<Error, Set<NetworkStatus>>> {
|
||||
private fun getNetworksStatuses(networks: NonEmptySet<Network>): Flow<Either<Error, Set<NetworkStatus>>> {
|
||||
return networksRepository.getNetworkStatusesUpdates(userWalletId, networks)
|
||||
.map<Set<NetworkStatus>, Either<Error, Set<NetworkStatus>>> { it.right() }
|
||||
.catch { emit(Error.DataError(it).left()) }
|
||||
|
|
@ -215,17 +215,17 @@ internal class CurrenciesStatusesOperations(
|
|||
|
||||
private fun getIds(
|
||||
currencies: NonEmptyList<CryptoCurrency>,
|
||||
): Pair<NonEmptySet<Network.ID>, NonEmptySet<CryptoCurrency.ID>> {
|
||||
): Pair<NonEmptySet<Network>, NonEmptySet<CryptoCurrency.ID>> {
|
||||
val currencyIdToNetworkId = currencies.associate { currency ->
|
||||
currency.id to currency.network.id
|
||||
currency.id to currency.network
|
||||
}
|
||||
val currenciesIds = currencyIdToNetworkId.keys.toNonEmptySetOrNull()
|
||||
val networksIds = currencyIdToNetworkId.values.toNonEmptySetOrNull()
|
||||
val networks = currencyIdToNetworkId.values.toNonEmptySetOrNull()
|
||||
|
||||
requireNotNull(currenciesIds) { "Currencies IDs cannot be empty" }
|
||||
requireNotNull(networksIds) { "Networks IDs cannot be empty" }
|
||||
requireNotNull(networks) { "Networks IDs cannot be empty" }
|
||||
|
||||
return networksIds to currenciesIds
|
||||
return networks to currenciesIds
|
||||
}
|
||||
|
||||
sealed class Error {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ internal class CurrencyStatusOperations(
|
|||
}
|
||||
|
||||
private fun createStatus(status: NetworkStatus.Verified): CryptoCurrencyStatus.Status {
|
||||
val amount = status.amounts[currency.id] ?: return CryptoCurrencyStatus.Unreachable
|
||||
val amount = status.amounts[currency.id] ?: return CryptoCurrencyStatus.NoAmount
|
||||
val hasCurrentNetworkTransactions = status.pendingTransactions.isNotEmpty()
|
||||
val currentTransactions = status.pendingTransactions.getOrElse(currency.id, ::emptySet)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ internal class TokenListFiatBalanceOperations(
|
|||
}
|
||||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
-> {
|
||||
fiatBalance = TokenList.FiatBalance.Failed
|
||||
break
|
||||
|
|
|
|||
|
|
@ -9,25 +9,16 @@ import kotlinx.coroutines.flow.Flow
|
|||
* Repository for everything related to the blockchain networks
|
||||
* */
|
||||
interface NetworksRepository {
|
||||
|
||||
/**
|
||||
* Retrieves the details of the specified blockchain networks, identified by their unique IDs.
|
||||
*
|
||||
* @param networksIds The unique identifiers of the networks to be retrieved.
|
||||
* @return A set of [Network] objects corresponding to the specified network IDs.
|
||||
*/
|
||||
fun getNetworks(networksIds: Set<Network.ID>): Set<Network>
|
||||
|
||||
/**
|
||||
* Retrieves updates of network statuses of specified blockchain networks for a specific user wallet.
|
||||
*
|
||||
* Loads remote network statuses if they have expired.
|
||||
*
|
||||
* @param userWalletId The unique identifier of the user wallet.
|
||||
* @param networks A set of network IDs which statuses are to be retrieved.
|
||||
* @param networks A set of network which statuses are to be retrieved.
|
||||
* @return A [Flow] emitting a set of [NetworkStatus] objects corresponding to the specified networks.
|
||||
*/
|
||||
fun getNetworkStatusesUpdates(userWalletId: UserWalletId, networks: Set<Network.ID>): Flow<Set<NetworkStatus>>
|
||||
fun getNetworkStatusesUpdates(userWalletId: UserWalletId, networks: Set<Network>): Flow<Set<NetworkStatus>>
|
||||
|
||||
/**
|
||||
* Retrieves network statuses of specified blockchain networks for a specific user wallet.
|
||||
|
|
@ -35,13 +26,13 @@ interface NetworksRepository {
|
|||
* Loads remote network statuses if they have expired or if [refresh] is `true`.
|
||||
*
|
||||
* @param userWalletId The unique identifier of the user wallet.
|
||||
* @param networks A set of network IDs which statuses are to be retrieved.
|
||||
* @param networks A set of network which statuses are to be retrieved.
|
||||
* @param refresh A boolean flag indicating whether the data should be refreshed.
|
||||
* @return A [Flow] emitting a set of [NetworkStatus] objects corresponding to the specified networks.
|
||||
*/
|
||||
suspend fun getNetworkStatusesSync(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
networks: Set<Network>,
|
||||
refresh: Boolean,
|
||||
): Set<NetworkStatus>
|
||||
}
|
||||
|
|
@ -164,6 +164,6 @@ internal class GetPrimaryCurrencyStatusUpdatesUseCaseTest {
|
|||
isSortedByBalance = flowOf(),
|
||||
),
|
||||
quotesRepository = MockQuotesRepository(quotes),
|
||||
networksRepository = MockNetworksRepository(MockNetworks.networks.right(), statuses),
|
||||
networksRepository = MockNetworksRepository(statuses),
|
||||
)
|
||||
}
|
||||
|
|
@ -314,6 +314,6 @@ internal class GetTokenListUseCaseTest {
|
|||
isSortedByBalance = isSortedByBalance,
|
||||
),
|
||||
quotesRepository = MockQuotesRepository(quotes),
|
||||
networksRepository = MockNetworksRepository(MockNetworks.networks.right(), statuses),
|
||||
networksRepository = MockNetworksRepository(statuses),
|
||||
)
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ internal object MockNetworks {
|
|||
name = "Network One",
|
||||
isTestnet = false,
|
||||
standardType = Network.StandardType.ERC20,
|
||||
derivationPath = Network.DerivationPath.None,
|
||||
)
|
||||
|
||||
val network2 = Network(
|
||||
|
|
@ -24,6 +25,7 @@ internal object MockNetworks {
|
|||
name = "Network Two",
|
||||
isTestnet = false,
|
||||
standardType = Network.StandardType.ERC20,
|
||||
derivationPath = Network.DerivationPath.None,
|
||||
)
|
||||
|
||||
val network3 = Network(
|
||||
|
|
@ -31,22 +33,21 @@ internal object MockNetworks {
|
|||
name = "Network Three",
|
||||
isTestnet = false,
|
||||
standardType = Network.StandardType.ERC20,
|
||||
derivationPath = Network.DerivationPath.None,
|
||||
)
|
||||
|
||||
val networks = nonEmptySetOf(network1, network2, network3)
|
||||
|
||||
val networkStatus1 = NetworkStatus(
|
||||
networkId = network1.id,
|
||||
network = network1,
|
||||
value = NetworkStatus.Unreachable,
|
||||
)
|
||||
|
||||
val networkStatus2 = NetworkStatus(
|
||||
networkId = network2.id,
|
||||
network = network2,
|
||||
value = NetworkStatus.MissedDerivation,
|
||||
)
|
||||
|
||||
val networkStatus3 = NetworkStatus(
|
||||
networkId = network3.id,
|
||||
network = network3,
|
||||
value = NetworkStatus.NoAccount(
|
||||
amountToCreateAccount = amountToCreateAccount,
|
||||
address = NetworkAddress.Single(defaultAddress = "mock"),
|
||||
|
|
|
|||
|
|
@ -7,17 +7,25 @@ internal object MockTokens {
|
|||
|
||||
val token1
|
||||
get() = CryptoCurrency.Coin(
|
||||
id = ID(ID.Prefix.COIN_PREFIX, MockNetworks.network1.id, ID.Suffix.RawID("token1")),
|
||||
id = ID(
|
||||
ID.Prefix.COIN_PREFIX,
|
||||
ID.Body.NetworkId(MockNetworks.network1.id.value),
|
||||
ID.Suffix.RawID("token1"),
|
||||
),
|
||||
network = MockNetworks.network1,
|
||||
name = "Token 1",
|
||||
symbol = "T1",
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
derivationPath = null,
|
||||
isCustom = false,
|
||||
)
|
||||
val token2
|
||||
get() = CryptoCurrency.Token(
|
||||
id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network1.id, ID.Suffix.RawID("token2")),
|
||||
id = ID(
|
||||
ID.Prefix.TOKEN_PREFIX,
|
||||
ID.Body.NetworkId(MockNetworks.network1.id.value),
|
||||
ID.Suffix.RawID("token2"),
|
||||
),
|
||||
network = MockNetworks.network1,
|
||||
name = "Token 2",
|
||||
symbol = "T2",
|
||||
|
|
@ -25,11 +33,14 @@ internal object MockTokens {
|
|||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token3
|
||||
get() = CryptoCurrency.Token(
|
||||
id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network1.id, ID.Suffix.RawID("token3")),
|
||||
id = ID(
|
||||
ID.Prefix.TOKEN_PREFIX,
|
||||
ID.Body.NetworkId(MockNetworks.network1.id.value),
|
||||
ID.Suffix.RawID("token3"),
|
||||
),
|
||||
network = MockNetworks.network1,
|
||||
name = "Token 3",
|
||||
symbol = "T3",
|
||||
|
|
@ -37,21 +48,28 @@ internal object MockTokens {
|
|||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token4
|
||||
get() = CryptoCurrency.Coin(
|
||||
id = ID(ID.Prefix.COIN_PREFIX, MockNetworks.network2.id, ID.Suffix.RawID("token4")),
|
||||
id = ID(
|
||||
ID.Prefix.COIN_PREFIX,
|
||||
ID.Body.NetworkId(MockNetworks.network2.id.value),
|
||||
ID.Suffix.RawID("token4"),
|
||||
),
|
||||
network = MockNetworks.network2,
|
||||
name = "Token 4",
|
||||
symbol = "T4",
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
derivationPath = null,
|
||||
isCustom = false,
|
||||
)
|
||||
val token5
|
||||
get() = CryptoCurrency.Token(
|
||||
id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network2.id, ID.Suffix.RawID("token5")),
|
||||
id = ID(
|
||||
ID.Prefix.TOKEN_PREFIX,
|
||||
ID.Body.NetworkId(MockNetworks.network2.id.value),
|
||||
ID.Suffix.RawID("token5"),
|
||||
),
|
||||
network = MockNetworks.network2,
|
||||
name = "Token 5",
|
||||
symbol = "T5",
|
||||
|
|
@ -59,11 +77,14 @@ internal object MockTokens {
|
|||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token6
|
||||
get() = CryptoCurrency.Token(
|
||||
id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network2.id, ID.Suffix.RawID("token6")),
|
||||
id = ID(
|
||||
ID.Prefix.TOKEN_PREFIX,
|
||||
ID.Body.NetworkId(MockNetworks.network2.id.value),
|
||||
ID.Suffix.RawID("token6"),
|
||||
),
|
||||
network = MockNetworks.network2,
|
||||
name = "Token 6",
|
||||
symbol = "T6",
|
||||
|
|
@ -71,21 +92,28 @@ internal object MockTokens {
|
|||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token7
|
||||
get() = CryptoCurrency.Coin(
|
||||
id = ID(ID.Prefix.COIN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token7")),
|
||||
id = ID(
|
||||
ID.Prefix.COIN_PREFIX,
|
||||
ID.Body.NetworkId(MockNetworks.network3.id.value),
|
||||
ID.Suffix.RawID("token7"),
|
||||
),
|
||||
network = MockNetworks.network3,
|
||||
name = "Token 7",
|
||||
symbol = "T7",
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
derivationPath = null,
|
||||
isCustom = false,
|
||||
)
|
||||
val token8
|
||||
get() = CryptoCurrency.Token(
|
||||
id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token8")),
|
||||
id = ID(
|
||||
ID.Prefix.TOKEN_PREFIX,
|
||||
ID.Body.NetworkId(MockNetworks.network3.id.value),
|
||||
ID.Suffix.RawID("token8"),
|
||||
),
|
||||
network = MockNetworks.network3,
|
||||
name = "Token 8",
|
||||
symbol = "T8",
|
||||
|
|
@ -93,11 +121,14 @@ internal object MockTokens {
|
|||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token9
|
||||
get() = CryptoCurrency.Token(
|
||||
id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token9")),
|
||||
id = ID(
|
||||
ID.Prefix.TOKEN_PREFIX,
|
||||
ID.Body.NetworkId(MockNetworks.network3.id.value),
|
||||
ID.Suffix.RawID("token9"),
|
||||
),
|
||||
network = MockNetworks.network3,
|
||||
name = "Token 9",
|
||||
symbol = "T9",
|
||||
|
|
@ -105,11 +136,14 @@ internal object MockTokens {
|
|||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token10
|
||||
get() = CryptoCurrency.Token(
|
||||
id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token10")),
|
||||
id = ID(
|
||||
ID.Prefix.TOKEN_PREFIX,
|
||||
ID.Body.NetworkId(MockNetworks.network3.id.value),
|
||||
ID.Suffix.RawID("token10"),
|
||||
),
|
||||
network = MockNetworks.network3,
|
||||
name = "Token 10",
|
||||
symbol = "T10",
|
||||
|
|
@ -117,7 +151,6 @@ internal object MockTokens {
|
|||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
|
||||
val tokens = listOf(token1, token2, token3, token4, token5, token6, token7, token8, token9, token10)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ internal object MockTokensStates {
|
|||
|
||||
val loadedTokensStates = failedTokenStates.map { status ->
|
||||
val networkStatus = MockNetworks.verifiedNetworksStatuses
|
||||
.first { it.networkId == status.currency.network.id }
|
||||
.first { it.network == status.currency.network }
|
||||
val amount = (networkStatus.value as NetworkStatus.Verified).amounts[status.currency.id]!!
|
||||
val quote = MockQuotes.quotes.first { it.rawCurrencyId == status.currency.id.rawCurrencyId }
|
||||
val fiatAmount = amount * quote.fiatRate
|
||||
|
|
@ -98,7 +98,7 @@ internal object MockTokensStates {
|
|||
hasCurrentNetworkTransactions = false,
|
||||
networkAddress = requireNotNull(
|
||||
value = MockNetworks.verifiedNetworksStatuses
|
||||
.first { it.networkId == status.currency.network.id }
|
||||
.first { it.network == status.currency.network }
|
||||
.value as? NetworkStatus.Verified,
|
||||
).address,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -11,24 +11,19 @@ import kotlinx.coroutines.flow.first
|
|||
import kotlinx.coroutines.flow.map
|
||||
|
||||
internal class MockNetworksRepository(
|
||||
private val networks: Either<DataError, Set<Network>>,
|
||||
private val statuses: Flow<Either<DataError, Set<NetworkStatus>>>,
|
||||
) : NetworksRepository {
|
||||
|
||||
override fun getNetworks(networksIds: Set<Network.ID>): Set<Network> {
|
||||
return networks.getOrElse { throw it }
|
||||
}
|
||||
|
||||
override fun getNetworkStatusesUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
networks: Set<Network>,
|
||||
): Flow<Set<NetworkStatus>> {
|
||||
return statuses.map { it.getOrElse { e -> throw e } }
|
||||
}
|
||||
|
||||
override suspend fun getNetworkStatusesSync(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
networks: Set<Network>,
|
||||
refresh: Boolean,
|
||||
): Set<NetworkStatus> {
|
||||
return getNetworkStatusesUpdates(userWalletId, networks).first()
|
||||
|
|
|
|||
|
|
@ -2,20 +2,16 @@ package com.tangem.domain.txhistory.repository
|
|||
|
||||
import androidx.paging.PagingData
|
||||
import com.tangem.domain.tokens.models.Network
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.domain.txhistory.models.TxHistoryStateError
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface TxHistoryRepository {
|
||||
|
||||
@Throws(TxHistoryStateError::class)
|
||||
suspend fun getTxHistoryItemsCount(networkId: Network.ID, derivationPath: String?): Int
|
||||
suspend fun getTxHistoryItemsCount(network: Network): Int
|
||||
|
||||
@Throws(TxHistoryListError::class)
|
||||
fun getTxHistoryItems(
|
||||
networkId: Network.ID,
|
||||
derivationPath: String?,
|
||||
pageSize: Int,
|
||||
): Flow<PagingData<TxHistoryItem>>
|
||||
fun getTxHistoryItems(network: Network, pageSize: Int): Flow<PagingData<TxHistoryItem>>
|
||||
}
|
||||
|
|
@ -9,10 +9,10 @@ import com.tangem.domain.txhistory.repository.TxHistoryRepository
|
|||
|
||||
class GetTxHistoryItemsCountUseCase(private val repository: TxHistoryRepository) {
|
||||
|
||||
suspend operator fun invoke(networkId: Network.ID, derivationPath: String?): Either<TxHistoryStateError, Int> {
|
||||
suspend operator fun invoke(network: Network): Either<TxHistoryStateError, Int> {
|
||||
return either {
|
||||
catch(
|
||||
block = { repository.getTxHistoryItemsCount(networkId, derivationPath) },
|
||||
block = { repository.getTxHistoryItemsCount(network) },
|
||||
catch = { throwable ->
|
||||
raise(
|
||||
when (throwable) {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import androidx.paging.PagingData
|
|||
import arrow.core.Either
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.domain.tokens.models.Network
|
||||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.domain.txhistory.repository.TxHistoryRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
|
|
@ -15,13 +15,12 @@ private const val DEFAULT_PAGE_SIZE = 20
|
|||
class GetTxHistoryItemsUseCase(private val repository: TxHistoryRepository) {
|
||||
|
||||
operator fun invoke(
|
||||
networkId: Network.ID,
|
||||
derivationPath: String?,
|
||||
network: Network,
|
||||
pageSize: Int = DEFAULT_PAGE_SIZE,
|
||||
): Either<TxHistoryListError, Flow<PagingData<TxHistoryItem>>> {
|
||||
return either {
|
||||
repository
|
||||
.getTxHistoryItems(networkId = networkId, derivationPath = derivationPath, pageSize = pageSize)
|
||||
.getTxHistoryItems(network = network, pageSize = pageSize)
|
||||
.catch { raise(TxHistoryListError.DataError(it)) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
package com.tangem.domain.wallets.usecase
|
||||
|
||||
import arrow.core.raise.catch
|
||||
import com.tangem.domain.tokens.models.Network
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
// TODO: Add tests
|
||||
class GetExploreUrlUseCase(private val walletsManagersFacade: WalletManagersFacade) {
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, networkId: Network.ID): String {
|
||||
return walletsManagersFacade.getExploreUrl(userWalletId, networkId)
|
||||
// FIXME: Handle error
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, network: Network): String {
|
||||
return catch({ walletsManagersFacade.getExploreUrl(userWalletId, network) }) {
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -65,6 +65,7 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
// TODO: [REDACTED_JIRA]
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
-> {
|
||||
|
|
@ -89,6 +90,7 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
is CryptoCurrencyStatus.Custom,
|
||||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
-> MarketPriceBlockState.Error(currencyName)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,8 +118,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private fun updateTxHistory(refresh: Boolean = false) {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(
|
||||
networkId = cryptoCurrency.network.id,
|
||||
derivationPath = cryptoCurrency.derivationPath,
|
||||
network = cryptoCurrency.network,
|
||||
)
|
||||
|
||||
if (!refresh) {
|
||||
|
|
@ -129,8 +128,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
txHistoryItemsCountEither.onRight {
|
||||
uiState = stateFactory.getLoadedTxHistoryState(
|
||||
txHistoryEither = txHistoryItemsUseCase(
|
||||
networkId = cryptoCurrency.network.id,
|
||||
derivationPath = cryptoCurrency.derivationPath,
|
||||
network = cryptoCurrency.network,
|
||||
).map {
|
||||
it.cachedIn(viewModelScope)
|
||||
},
|
||||
|
|
@ -254,7 +252,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
router.openUrl(
|
||||
url = getExploreUrlUseCase(
|
||||
userWalletId = wallet.walletId,
|
||||
networkId = cryptoCurrency.network.id,
|
||||
network = cryptoCurrency.network,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ internal object WalletPreviewData {
|
|||
val networkNumber = index + 1
|
||||
|
||||
val group = DraggableItem.GroupHeader(
|
||||
id = "group_$networkNumber",
|
||||
id = networkNumber,
|
||||
networkName = "$networkNumber",
|
||||
roundingMode = when (index) {
|
||||
0 -> DraggableItem.RoundingMode.Top()
|
||||
|
|
@ -328,7 +328,7 @@ internal object WalletPreviewData {
|
|||
walletsListConfig = walletListConfig,
|
||||
tokensListState = WalletTokensListState.Content(
|
||||
persistentListOf(
|
||||
TokensListItemState.NetworkGroupTitle(TextReference.Str("Bitcoin")),
|
||||
TokensListItemState.NetworkGroupTitle(id = 0, stringReference("Bitcoin")),
|
||||
TokensListItemState.Token(
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_1",
|
||||
|
|
@ -357,7 +357,7 @@ internal object WalletPreviewData {
|
|||
amount = "1,89340821 ETH",
|
||||
),
|
||||
),
|
||||
TokensListItemState.NetworkGroupTitle(TextReference.Str("Ethereum")),
|
||||
TokensListItemState.NetworkGroupTitle(id = 1, stringReference("Ethereum")),
|
||||
TokensListItemState.Token(
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_5",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
|||
* */
|
||||
@Immutable
|
||||
internal sealed class DraggableItem {
|
||||
abstract val id: String
|
||||
abstract val id: Any
|
||||
abstract val roundingMode: RoundingMode
|
||||
abstract val showShadow: Boolean
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ internal sealed class DraggableItem {
|
|||
* @property showShadow if true then item should be elevated
|
||||
* */
|
||||
data class GroupHeader(
|
||||
override val id: String,
|
||||
override val id: Int,
|
||||
val networkName: String,
|
||||
override val roundingMode: RoundingMode = RoundingMode.None,
|
||||
override val showShadow: Boolean = false,
|
||||
|
|
@ -43,7 +43,7 @@ internal sealed class DraggableItem {
|
|||
* */
|
||||
data class Token(
|
||||
val tokenItemState: TokenItemState.Draggable,
|
||||
val groupId: String,
|
||||
val groupId: Int,
|
||||
override val showShadow: Boolean = false,
|
||||
override val roundingMode: RoundingMode = RoundingMode.None,
|
||||
) : DraggableItem() {
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ import com.tangem.domain.tokens.models.Network
|
|||
|
||||
internal fun getTokenItemId(currencyId: CryptoCurrency.ID): String = currencyId.value
|
||||
|
||||
internal fun getGroupHeaderId(networkId: Network.ID): String = networkId.value
|
||||
internal fun getGroupHeaderId(network: Network): Int = network.hashCode()
|
||||
|
|
@ -36,7 +36,7 @@ internal class CryptoCurrencyToDraggableItemConverter(
|
|||
): DraggableItem.Token {
|
||||
return DraggableItem.Token(
|
||||
tokenItemState = createTokenItemState(currencyStatus, appCurrency),
|
||||
groupId = getGroupHeaderId(currencyStatus.currency.network.id),
|
||||
groupId = getGroupHeaderId(currencyStatus.currency.network),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ internal class NetworkGroupToDraggableItemsConverter(
|
|||
}
|
||||
|
||||
private fun createGroupHeader(group: NetworkGroup) = DraggableItem.GroupHeader(
|
||||
id = getGroupHeaderId(group.network.id),
|
||||
id = getGroupHeaderId(group.network),
|
||||
networkName = group.network.name,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.utils.common.uniteI
|
|||
|
||||
internal class DraggableGroupsOperations {
|
||||
|
||||
private var groupIdToTokens: Map<String, List<DraggableItem.Token>>? = null
|
||||
private var groupIdToTokens: Map<Int, List<DraggableItem.Token>>? = null
|
||||
|
||||
fun collapseGroup(items: List<DraggableItem>, movingGroup: DraggableItem.GroupHeader): List<DraggableItem> {
|
||||
if (!groupIdToTokens.isNullOrEmpty()) return items
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ internal sealed class WalletTokensListState {
|
|||
/** Locked content state */
|
||||
object Locked : ContentState(
|
||||
items = persistentListOf(
|
||||
TokensListItemState.NetworkGroupTitle(value = TextReference.Res(id = R.string.main_tokens)),
|
||||
TokensListItemState.NetworkGroupTitle(id = 42, name = TextReference.Res(id = R.string.main_tokens)),
|
||||
TokensListItemState.Token(state = TokenItemState.Locked(id = LOCKED_TOKEN_ID)),
|
||||
),
|
||||
organizeTokensButton = OrganizeTokensButtonState.Hidden,
|
||||
|
|
@ -84,19 +84,26 @@ internal sealed class WalletTokensListState {
|
|||
@Immutable
|
||||
sealed interface TokensListItemState {
|
||||
|
||||
val id: Any
|
||||
|
||||
/**
|
||||
* Network group title item
|
||||
*
|
||||
* @property value network name
|
||||
* @property name network name
|
||||
*/
|
||||
data class NetworkGroupTitle(val value: TextReference) : TokensListItemState
|
||||
data class NetworkGroupTitle(
|
||||
override val id: Int,
|
||||
val name: TextReference,
|
||||
) : TokensListItemState
|
||||
|
||||
/**
|
||||
* Token item
|
||||
*
|
||||
* @property state token item state
|
||||
*/
|
||||
data class Token(val state: TokenItemState) : TokensListItemState
|
||||
data class Token(val state: TokenItemState) : TokensListItemState {
|
||||
override val id: String = state.id
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
|
|||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
-> MarketPriceBlockState.Error(currencyName)
|
||||
}
|
||||
}
|
||||
|
|
@ -112,6 +113,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
|
|||
is CryptoCurrencyStatus.NoAccount,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
-> {
|
||||
WalletCardState.Error(
|
||||
id = selectedWallet.id,
|
||||
|
|
|
|||
|
|
@ -44,12 +44,7 @@ private fun LazyListScope.contentItems(
|
|||
) {
|
||||
itemsIndexed(
|
||||
items = items,
|
||||
key = { _, item ->
|
||||
when (item) {
|
||||
is WalletTokensListState.TokensListItemState.NetworkGroupTitle -> item.value.hashCode()
|
||||
is WalletTokensListState.TokensListItemState.Token -> item.state.id
|
||||
}
|
||||
},
|
||||
key = { _, item -> item.id },
|
||||
contentType = { _, item -> item::class.java },
|
||||
itemContent = { index, item ->
|
||||
MultiCurrencyContentItem(
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletToke
|
|||
internal fun MultiCurrencyContentItem(state: WalletTokensListState.TokensListItemState, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is WalletTokensListState.TokensListItemState.NetworkGroupTitle -> {
|
||||
NetworkGroupItem(networkName = state.value.resolveReference(), modifier = modifier)
|
||||
NetworkGroupItem(networkName = state.name.resolveReference(), modifier = modifier)
|
||||
}
|
||||
is WalletTokensListState.TokensListItemState.Token -> {
|
||||
TokenItem(state = state.state, modifier = modifier)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ internal class CryptoCurrencyStatusToTokenItemConverter(
|
|||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
-> value.mapToUnreachableTokenItemState()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.utils
|
||||
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkGroup
|
||||
|
|
@ -68,7 +68,12 @@ internal class TokenListToContentItemsConverter(
|
|||
}
|
||||
|
||||
private fun MutableList<TokensListItemState>.addGroup(group: NetworkGroup): List<TokensListItemState> {
|
||||
this.add(TokensListItemState.NetworkGroupTitle(TextReference.Str(group.network.name)))
|
||||
val groupTitle = TokensListItemState.NetworkGroupTitle(
|
||||
id = group.network.hashCode(),
|
||||
name = stringReference(group.network.name),
|
||||
)
|
||||
|
||||
this.add(groupTitle)
|
||||
|
||||
group.currencies.forEach { token ->
|
||||
this.addToken(token)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels
|
|||
import androidx.lifecycle.*
|
||||
import androidx.paging.cachedIn
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.derivation.DerivationStyle
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
|
|
@ -15,7 +13,6 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.card.*
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.CanUseBiometryUseCase
|
||||
|
|
@ -461,14 +458,18 @@ internal class WalletViewModel @Inject constructor(
|
|||
val wallet = getWallet(
|
||||
index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex,
|
||||
)
|
||||
router.openTxHistoryWebsite(
|
||||
url = getExploreUrlUseCase(
|
||||
userWalletId = wallet.walletId,
|
||||
networkId = Network.ID(
|
||||
value = wallet.scanResponse.cardTypesResolver.getBlockchain().id,
|
||||
val currencyStatus = getPrimaryCurrencyStatusUpdatesUseCase(wallet.walletId)
|
||||
.firstOrNull()
|
||||
?.getOrNull()
|
||||
|
||||
if (currencyStatus != null) {
|
||||
router.openTxHistoryWebsite(
|
||||
url = getExploreUrlUseCase(
|
||||
userWalletId = wallet.walletId,
|
||||
network = currencyStatus.currency.network,
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -582,31 +583,20 @@ internal class WalletViewModel @Inject constructor(
|
|||
|
||||
private fun getSingleCurrencyContent(index: Int) {
|
||||
val wallet = getWallet(index)
|
||||
val blockchain = getCardTypeResolver(index).getBlockchain()
|
||||
updateTxHistory(
|
||||
blockchain = blockchain,
|
||||
derivationStyle = wallet.scanResponse.derivationStyleProvider.getDerivationStyle(),
|
||||
)
|
||||
updateMarketPrice(userWalletId = wallet.walletId)
|
||||
updatePrimaryCurrencyStatus(userWalletId = wallet.walletId)
|
||||
updateNotifications(index)
|
||||
}
|
||||
|
||||
private fun updateTxHistory(blockchain: Blockchain, derivationStyle: DerivationStyle?) {
|
||||
private fun updateTxHistory(network: Network) {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
val derivationPath = blockchain.derivationPath(style = derivationStyle)?.rawPath
|
||||
|
||||
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(
|
||||
networkId = Network.ID(blockchain.id),
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(network)
|
||||
|
||||
uiState = stateFactory.getLoadingTxHistoryState(itemsCountEither = txHistoryItemsCountEither)
|
||||
|
||||
txHistoryItemsCountEither.onRight {
|
||||
uiState = stateFactory.getLoadedTxHistoryState(
|
||||
txHistoryEither = txHistoryItemsUseCase(
|
||||
networkId = Network.ID(blockchain.id),
|
||||
derivationPath = derivationPath,
|
||||
network,
|
||||
).map {
|
||||
it.cachedIn(viewModelScope)
|
||||
},
|
||||
|
|
@ -615,8 +605,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
// It also update wallet balance
|
||||
private fun updateMarketPrice(userWalletId: UserWalletId) {
|
||||
private fun updatePrimaryCurrencyStatus(userWalletId: UserWalletId) {
|
||||
getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = userWalletId)
|
||||
.distinctUntilChanged()
|
||||
.onEach { maybeCryptoCurrencyStatus ->
|
||||
|
|
@ -625,6 +614,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
maybeCryptoCurrencyStatus.onRight { status ->
|
||||
singleWalletCryptoCurrencyStatus = status
|
||||
updateButtons(userWalletId = userWalletId, currency = status.currency)
|
||||
updateTxHistory(status.currency.network)
|
||||
}
|
||||
}
|
||||
.flowOn(dispatchers.io)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue