Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-13 14:19:25 +03:00
parent 4b5adcd08a
commit 5ed381c263
56 changed files with 631 additions and 455 deletions

View file

@ -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,
)

View file

@ -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}"
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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),
)
}
}

View file

@ -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)
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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),
)
}
}

View file

@ -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 {

View file

@ -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,

View file

@ -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,
)