Updated on 2026-08-14
This commit is contained in:
parent
4b5adcd08a
commit
5ed381c263
56 changed files with 631 additions and 455 deletions
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue