Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-16 17:51:24 +04:00
commit 1881ac067d
33 changed files with 541 additions and 46 deletions

View file

@ -104,6 +104,7 @@ dependencies {
implementation(projects.data.staking)
implementation(projects.data.walletConnect)
implementation(projects.data.markets)
implementation(projects.data.manageTokens)
/** Features */
implementation(projects.features.onboarding)

View file

@ -4,7 +4,7 @@ import arrow.core.getOrElse
import arrow.core.raise.result
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.domain.card.DerivePublicKeysUseCase
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.features.addCustomToken.CustomCurrency

View file

@ -5,7 +5,7 @@ import com.tangem.common.extensions.guard
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.Analytics
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.domain.common.extensions.makePrimaryWalletManager
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.derivationStyleProvider

View file

@ -7,7 +7,7 @@ import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.utils.popTo
import com.tangem.core.analytics.Analytics
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.domain.common.extensions.makePrimaryWalletManager
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.derivationStyleProvider

View file

@ -5,7 +5,7 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.utils.popTo
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.domain.card.DerivePublicKeysUseCase
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase

View file

@ -2,7 +2,7 @@ package com.tangem.tap.network.exchangeServices
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.tap.common.extensions.inject

View file

@ -2,7 +2,7 @@ package com.tangem.tap.domain.card
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency

View file

@ -10,13 +10,26 @@ android {
}
dependencies {
/* Core */
implementation(projects.core.datasource)
/* Domain */
implementation(projects.domain.models)
implementation(projects.domain.legacy)
implementation(projects.domain.tokens.models)
/* Libs - SDK */
implementation(deps.tangem.blockchain)
implementation(deps.tangem.card.core)
implementation(projects.libs.blockchainSdk)
/* DI */
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)
/* Libs - Other */
implementation(deps.kotlin.coroutines)
implementation(deps.jodatime)
implementation(deps.timber)
implementation(deps.arrow.core)
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)
}

View file

@ -1,4 +1,4 @@
package com.tangem.data.tokens.utils
package com.tangem.data.common.currency
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId

View file

@ -1,4 +1,4 @@
package com.tangem.data.tokens.utils
package com.tangem.data.common.currency
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.FeePaidCurrency
@ -7,14 +7,14 @@ import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.tokens.model.Network
import timber.log.Timber
internal fun getBlockchain(networkId: Network.ID): Blockchain {
fun getBlockchain(networkId: Network.ID): Blockchain {
return Blockchain.fromId(networkId.value)
}
internal fun getNetwork(
fun getNetwork(
blockchain: Blockchain,
extraDerivationPath: String?,
derivationStyleProvider: DerivationStyleProvider,
derivationStyleProvider: DerivationStyleProvider?,
): Network? {
if (blockchain == Blockchain.Unknown) {
Timber.e("Unable to convert Unknown blockchain to the domain network model")
@ -36,8 +36,12 @@ internal fun getNetwork(
private fun getNetworkDerivationPath(
blockchain: Blockchain,
extraDerivationPath: String?,
cardDerivationStyleProvider: DerivationStyleProvider,
cardDerivationStyleProvider: DerivationStyleProvider?,
): Network.DerivationPath {
if (cardDerivationStyleProvider == null) {
return Network.DerivationPath.None
}
val defaultDerivationPath = getDefaultDerivationPath(blockchain, cardDerivationStyleProvider)
return if (extraDerivationPath.isNullOrBlank()) {
@ -55,7 +59,7 @@ private fun getNetworkDerivationPath(
}
}
internal fun getNetworkStandardType(blockchain: Blockchain): Network.StandardType {
fun getNetworkStandardType(blockchain: Blockchain): Network.StandardType {
return when (blockchain) {
Blockchain.Ethereum, Blockchain.EthereumTestnet -> Network.StandardType.ERC20
Blockchain.BSC, Blockchain.BSCTestnet -> Network.StandardType.BEP20

View file

@ -1,4 +1,4 @@
package com.tangem.data.tokens.utils
package com.tangem.data.common.currency
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token

View file

@ -1,4 +1,4 @@
package com.tangem.data.tokens.utils
package com.tangem.data.common.currency
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.IconsUtil
@ -18,30 +18,35 @@ 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, network: Network): Boolean {
fun isCustomToken(tokenId: ID, network: Network): Boolean {
return network.derivationPath is Network.DerivationPath.Custom || tokenId.rawCurrencyId == null
}
internal fun isCustomCoin(network: Network): Boolean {
fun isCustomCoin(network: Network): Boolean {
return network.derivationPath is Network.DerivationPath.Custom
}
internal fun getCoinId(network: Network, coinId: String): ID {
fun getCoinId(network: Network, coinId: String): ID {
return ID(COIN_ID_PREFIX, getCurrencyIdBody(network), CurrencyIdSuffix(rawId = coinId))
}
internal fun getTokenId(network: Network, sdkToken: SdkToken): ID {
fun getTokenId(network: Network, sdkToken: SdkToken): ID {
val sdkTokenId = sdkToken.id
val suffix = if (sdkTokenId == null) {
CustomCurrencyIdSuffix(contractAddress = sdkToken.contractAddress)
return getTokenId(network, sdkTokenId, sdkToken.contractAddress)
}
fun getTokenId(network: Network, rawTokenId: String?, contractAddress: String): ID {
val suffix = if (rawTokenId == null) {
CustomCurrencyIdSuffix(contractAddress)
} else {
CurrencyIdSuffix(rawId = sdkTokenId, contractAddress = sdkToken.contractAddress)
CurrencyIdSuffix(rawTokenId, contractAddress)
}
return ID(TOKEN_ID_PREFIX, getCurrencyIdBody(network), suffix)
}
internal fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? {
fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? {
val tokenId = token.id
return if (tokenId == null) {
@ -51,7 +56,7 @@ internal fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? {
}
}
internal fun getCoinIconUrl(blockchain: Blockchain): String? {
fun getCoinIconUrl(blockchain: Blockchain): String? {
val coinId = when (blockchain) {
Blockchain.Unknown -> null
else -> blockchain.toCoinId()
@ -60,7 +65,7 @@ internal fun getCoinIconUrl(blockchain: Blockchain): String? {
return coinId?.let(::getTokenIconUrlFromDefaultHost)
}
internal fun List<UserTokensResponse.Token>.hasCoinForToken(token: CryptoCurrency.Token): Boolean {
fun List<UserTokensResponse.Token>.hasCoinForToken(token: CryptoCurrency.Token): Boolean {
return any {
val blockchain = getBlockchain(networkId = token.network.id)
val tokenDerivation = token.network.derivationPath.value

View file

@ -1,4 +1,4 @@
package com.tangem.data.tokens.utils
package com.tangem.data.common.currency
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse

View file

@ -1,4 +1,4 @@
package com.tangem.data.markets.utils
package com.tangem.data.common.utils
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
@ -8,7 +8,7 @@ import timber.log.Timber
import kotlin.coroutines.cancellation.CancellationException
@Suppress("UnconditionalJumpStatementInLoop")
internal suspend fun <T> retryOnError(priority: Boolean = false, call: suspend () -> T): T {
suspend fun <T> retryOnError(priority: Boolean = false, call: suspend () -> T): T {
while (true) {
return try {
call()
@ -16,11 +16,14 @@ internal suspend fun <T> retryOnError(priority: Boolean = false, call: suspend (
if (e is CancellationException) {
currentCoroutineContext().ensureActive()
}
Timber.e(e)
Timber.e(e, "Error occurred during retryOnError block")
if (priority.not()) {
yield()
delay(timeMillis = 500)
}
continue
}
}

1
data/manage-tokens/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,44 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.kapt)
id("configuration")
}
android {
namespace = "com.example.data.managetokens"
}
dependencies {
/** Project - Domain */
implementation(projects.domain.models)
implementation(projects.domain.manageTokens)
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets.models)
/** Project - Data */
implementation(projects.core.datasource)
implementation(projects.data.common)
/** Project - Utils */
implementation(projects.core.utils)
implementation(projects.core.pagination)
implementation(projects.domain.legacy)
implementation(projects.libs.blockchainSdk)
/** Tangem SDKs */
implementation(deps.tangem.blockchain)
implementation(deps.tangem.card.core)
/** AndroidX */
implementation(deps.androidx.datastore)
/** DI */
implementation(deps.hilt.core)
kapt(deps.hilt.kapt)
/** Other */
implementation(deps.moshi.kotlin)
implementation(deps.timber)
}

View file

@ -0,0 +1,138 @@
package com.tangem.data.managetokens
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.isSupportedInApp
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.data.common.utils.retryOnError
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
import com.tangem.data.managetokens.utils.ManagedCryptoCurrencyFactory
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.common.extensions.supportedBlockchains
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.managetokens.model.ManageTokensListBatchFlow
import com.tangem.domain.managetokens.model.ManageTokensListBatchingContext
import com.tangem.domain.managetokens.model.ManageTokensListConfig
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
import com.tangem.domain.managetokens.repository.ManageTokensRepository
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.pagination.BatchFetchResult
import com.tangem.pagination.BatchListSource
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
import com.tangem.pagination.toBatchFlow
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
internal class DefaultManageTokensRepository(
private val tangemTechApi: TangemTechApi,
private val userWalletsStore: UserWalletsStore,
private val managedCryptoCurrencyFactory: ManagedCryptoCurrencyFactory,
private val manageTokensUpdateFetcher: ManageTokensUpdateFetcher,
private val appPreferencesStore: AppPreferencesStore,
private val dispatchers: CoroutineDispatcherProvider,
) : ManageTokensRepository {
override fun getTokenListBatchFlow(
context: ManageTokensListBatchingContext,
batchSize: Int,
): ManageTokensListBatchFlow {
return BatchListSource(
fetchDispatcher = dispatchers.io,
context = context,
generateNewKey = { it.size.inc() },
batchFetcher = createFetcher(batchSize),
updateFetcher = manageTokensUpdateFetcher,
).toBatchFlow()
}
@Suppress("ComplexCondition")
private fun createFetcher(
batchSize: Int,
): LimitOffsetBatchFetcher<ManageTokensListConfig, List<ManagedCryptoCurrency>> = LimitOffsetBatchFetcher(
prefetchDistance = batchSize,
batchSize = batchSize,
subFetcher = { request, _, isFirstBatchFetching ->
val userWallet = getUserWallet(request.params.userWalletId)
val supportedBlockchains = getSupportedBlockchains(userWallet)
val searchText = request.params.searchText?.takeIf { it.isNotBlank() }
val call = suspend {
tangemTechApi.getCoins(
networkIds = supportedBlockchains.joinToString(
separator = ",",
transform = Blockchain::toNetworkId,
),
active = true,
searchText = searchText,
offset = request.offset * request.limit,
limit = request.limit,
).getOrThrow()
}
val coinsResponse = if (isFirstBatchFetching) {
call()
} else {
retryOnError(call = call)
}
val tokensResponse = getStoredUserTokens(request.params.userWalletId)
val items = if (isFirstBatchFetching &&
tokensResponse != null &&
userWallet != null &&
request.params.searchText.isNullOrBlank()
) {
managedCryptoCurrencyFactory.createWithCustomTokens(
coinsResponse = coinsResponse,
tokensResponse = tokensResponse,
derivationStyleProvider = userWallet.scanResponse.derivationStyleProvider,
)
} else {
managedCryptoCurrencyFactory.create(
coinsResponse = coinsResponse,
tokensResponse = tokensResponse,
derivationStyleProvider = userWallet?.scanResponse?.derivationStyleProvider,
)
}
BatchFetchResult.Success(
data = items,
empty = items.isEmpty(),
last = items.size < request.limit,
)
},
)
private suspend fun getStoredUserTokens(userWalletId: UserWalletId?): UserTokensResponse? {
return if (userWalletId != null) {
appPreferencesStore.getObjectSyncOrNull(
key = PreferencesKeys.getUserTokensKey(userWalletId.stringValue),
)
} else {
null
}
}
private suspend fun getUserWallet(userWalletId: UserWalletId?): UserWallet? {
if (userWalletId == null) {
return null
}
return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
"User wallet not found"
}
}
private fun getSupportedBlockchains(userWallet: UserWallet?): List<Blockchain> {
return userWallet?.scanResponse?.let {
it.card.supportedBlockchains(it.cardTypesResolver)
} ?: Blockchain.entries.filter {
!it.isTestnet() && it.isSupportedInApp()
}
}
}

View file

@ -0,0 +1,40 @@
package com.tangem.data.managetokens.di
import com.tangem.data.managetokens.DefaultManageTokensRepository
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
import com.tangem.data.managetokens.utils.ManagedCryptoCurrencyFactory
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.managetokens.repository.ManageTokensRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object ManageTokensDataModule {
@Provides
@Singleton
fun provideManageTokensRepository(
tangemTechApi: TangemTechApi,
userWalletsStore: UserWalletsStore,
managedCryptoCurrencyFactory: ManagedCryptoCurrencyFactory,
manageTokensUpdateFetcher: ManageTokensUpdateFetcher,
appPreferencesStore: AppPreferencesStore,
dispatchers: CoroutineDispatcherProvider,
): ManageTokensRepository {
return DefaultManageTokensRepository(
tangemTechApi,
userWalletsStore,
managedCryptoCurrencyFactory,
manageTokensUpdateFetcher,
appPreferencesStore,
dispatchers,
)
}
}

View file

@ -0,0 +1,68 @@
package com.tangem.data.managetokens.utils
import com.tangem.domain.managetokens.model.ManageTokensUpdateAction
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
import com.tangem.pagination.Batch
import com.tangem.pagination.BatchUpdateFetcher
import com.tangem.pagination.BatchUpdateResult
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
internal class ManageTokensUpdateFetcher @Inject constructor() :
BatchUpdateFetcher<Int, List<ManagedCryptoCurrency>, ManageTokensUpdateAction> {
override suspend fun BatchUpdateFetcher.UpdateContext<Int, List<ManagedCryptoCurrency>>.fetchUpdateAsync(
toUpdate: List<Batch<Int, List<ManagedCryptoCurrency>>>,
updateRequest: ManageTokensUpdateAction,
) {
when (updateRequest) {
is ManageTokensUpdateAction.AddCurrency -> coroutineScope {
val tasks = toUpdate.map { (key, data) ->
async {
val currencyIndex = data.indexOfFirst { it.id == updateRequest.currencyId }
.takeIf { it != -1 }
?: error("Currency '${updateRequest.currencyId}' not found in batch #$key")
val updatedCurrency = when (val currency = data[currencyIndex]) {
is ManagedCryptoCurrency.Custom -> error("Can't add custom currency '${currency.id}'")
is ManagedCryptoCurrency.Token -> {
currency.copy(
addedIn = if (updateRequest.isSelected) {
currency.addedIn + updateRequest.networkId
} else {
currency.addedIn - updateRequest.networkId
},
)
}
}
data.toMutableList().apply {
set(currencyIndex, updatedCurrency)
}
}
}
tasks.forEachIndexed { index, task ->
launch {
val updatedItems = task.await()
update {
BatchUpdateResult.Success(
data = mapNotNull {
if (it.key == toUpdate[index].key) {
Batch(it.key, updatedItems)
} else {
null
}
},
)
}
}
}
}
}
}
}

View file

@ -0,0 +1,168 @@
package com.tangem.data.managetokens.utils
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.blockchainsdk.utils.isSupportedInApp
import com.tangem.blockchainsdk.utils.toCoinId
import com.tangem.data.common.currency.getCoinId
import com.tangem.data.common.currency.getNetwork
import com.tangem.data.common.currency.getTokenId
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.domain.common.DerivationStyleProvider
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
import com.tangem.domain.tokens.model.Network
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
internal class ManagedCryptoCurrencyFactory @Inject constructor() {
fun create(
coinsResponse: CoinsResponse,
tokensResponse: UserTokensResponse?,
derivationStyleProvider: DerivationStyleProvider?,
): List<ManagedCryptoCurrency> {
return coinsResponse.coins.mapNotNull { coin ->
createToken(coin, tokensResponse, coinsResponse.imageHost, derivationStyleProvider)
}
}
fun createWithCustomTokens(
coinsResponse: CoinsResponse,
tokensResponse: UserTokensResponse,
derivationStyleProvider: DerivationStyleProvider,
): List<ManagedCryptoCurrency> {
val customTokens = tokensResponse.tokens
.mapNotNull { token ->
maybeCreateCustomToken(token, coinsResponse.imageHost, derivationStyleProvider)
}
val tokens = create(coinsResponse, tokensResponse, derivationStyleProvider)
return customTokens + tokens
}
private fun maybeCreateCustomToken(
token: UserTokensResponse.Token,
imageHost: String?,
derivationStyleProvider: DerivationStyleProvider,
): ManagedCryptoCurrency? {
val blockchain = Blockchain.fromNetworkId(token.networkId)
?.takeIf { it.isSupportedInApp() }
?: return null
if (!checkIsCustomToken(token, blockchain, derivationStyleProvider)) {
return null
}
val network = getNetwork(
blockchain = blockchain,
extraDerivationPath = token.derivationPath,
derivationStyleProvider = derivationStyleProvider,
) ?: return null
val contractAddress = token.contractAddress
return if (contractAddress.isNullOrBlank()) {
ManagedCryptoCurrency.Custom.Coin(
currencyId = getCoinId(network, blockchain.toCoinId()),
name = token.name,
symbol = token.symbol,
iconUrl = getIconUrl(blockchain.id, imageHost),
network = network,
)
} else {
ManagedCryptoCurrency.Custom.Token(
currencyId = getTokenId(network, token.id, contractAddress),
name = token.name,
symbol = token.symbol,
iconUrl = token.id?.let { getIconUrl(it, imageHost) },
contractAddress = contractAddress,
network = network,
)
}
}
private fun createToken(
coinResponse: CoinsResponse.Coin,
tokensResponse: UserTokensResponse?,
imageHost: String?,
derivationStyleProvider: DerivationStyleProvider?,
): ManagedCryptoCurrency? {
if (coinResponse.networks.isEmpty() || !coinResponse.active) return null
return ManagedCryptoCurrency.Token(
id = ManagedCryptoCurrency.ID(coinResponse.id),
name = coinResponse.name,
symbol = coinResponse.symbol,
iconUrl = getIconUrl(coinResponse.id, imageHost),
availableNetworks = coinResponse.networks.mapNotNull { network ->
createSource(network, derivationStyleProvider)
},
addedIn = findAddedInNetworksIds(coinResponse.id, tokensResponse),
)
}
private fun createSource(
networkResponse: CoinsResponse.Coin.Network,
derivationStyleProvider: DerivationStyleProvider?,
extraDerivationPath: String? = null,
): SourceNetwork? {
val blockchain = Blockchain.fromNetworkId(networkResponse.networkId)
?.takeIf { it.isSupportedInApp() }
?: return null
val network = getNetwork(blockchain, extraDerivationPath, derivationStyleProvider) ?: return null
val contractAddress = networkResponse.contractAddress
return if (contractAddress.isNullOrBlank()) {
SourceNetwork.Main(
network = network,
)
} else {
SourceNetwork.Default(
network = network,
contractAddress = contractAddress,
)
}
}
private fun findAddedInNetworksIds(currencyId: String, tokensResponse: UserTokensResponse?): Set<Network.ID> {
if (tokensResponse == null) return emptySet()
return tokensResponse.tokens
.filter { it.id == currencyId }
.map { it.networkId }
.mapNotNullTo(mutableSetOf()) { networkId ->
val blockchain = Blockchain.fromNetworkId(networkId)
if (blockchain != null && blockchain.isSupportedInApp()) {
Network.ID(blockchain.id)
} else {
null
}
}
}
private fun getIconUrl(id: String, imageHost: String?): String {
return "${imageHost ?: DEFAULT_IMAGE_HOST}large/$id.png"
}
private fun checkIsCustomToken(
token: UserTokensResponse.Token,
blockchain: Blockchain,
derivationStyleProvider: DerivationStyleProvider,
): Boolean = token.id.isNullOrBlank() ||
checkIsCustomDerivationPath(token.derivationPath, blockchain, derivationStyleProvider)
private fun checkIsCustomDerivationPath(
derivationPath: String?,
blockchain: Blockchain,
derivationStyleProvider: DerivationStyleProvider,
): Boolean = derivationPath != blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath
private companion object {
const val DEFAULT_IMAGE_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/"
}
}

View file

@ -1,7 +1,10 @@
package com.tangem.data.markets
import com.tangem.data.markets.converters.*
import com.tangem.data.markets.utils.retryOnError
import com.tangem.data.common.utils.retryOnError
import com.tangem.data.markets.converters.TokenChartConverter
import com.tangem.data.markets.converters.TokenMarketInfoConverter
import com.tangem.data.markets.converters.TokenMarketListConverter
import com.tangem.data.markets.converters.toRequestParam
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.markets.TangemTechMarketsApi
import com.tangem.datasource.api.tangemTech.TangemTechApi

View file

@ -1,9 +1,9 @@
package com.tangem.data.markets
import com.tangem.data.common.utils.retryOnError
import com.tangem.data.markets.converters.TokenMarketChartsConverter
import com.tangem.data.markets.converters.TokenQuotesShortConverter
import com.tangem.data.markets.converters.toRequestParam
import com.tangem.data.markets.utils.retryOnError
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.markets.TangemTechMarketsApi
import com.tangem.datasource.api.markets.models.response.TokenMarketChartListResponse
@ -13,7 +13,9 @@ import com.tangem.domain.markets.TokenMarketUpdateRequest
import com.tangem.pagination.Batch
import com.tangem.pagination.BatchUpdateFetcher
import com.tangem.pagination.BatchUpdateResult
import kotlinx.coroutines.*
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
internal class MarketsBatchUpdateFetcher(
private val marketsApi: TangemTechMarketsApi,

View file

@ -2,7 +2,7 @@ package com.tangem.data.tokens.paging
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.data.tokens.utils.getNetworkStandardType
import com.tangem.data.common.currency.getNetworkStandardType
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Token

View file

@ -6,7 +6,10 @@ import com.tangem.blockchainsdk.utils.toCoinId
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.data.common.api.safeApiCall
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.tokens.utils.*
import com.tangem.data.common.currency.*
import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory
import com.tangem.data.tokens.utils.CustomTokensMerger
import com.tangem.data.tokens.utils.UserTokensBackwardCompatibility
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.express.TangemExpressApi

View file

@ -3,7 +3,7 @@ package com.tangem.data.tokens.repository
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.common.card.EllipticCurve
import com.tangem.data.tokens.utils.getNetwork
import com.tangem.data.common.currency.getNetwork
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.common.configs.CardConfig
import com.tangem.domain.common.extensions.*

View file

@ -5,9 +5,9 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.address.AddressType
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.data.common.cache.CacheRegistry
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory
import com.tangem.data.tokens.utils.NetworkStatusFactory
import com.tangem.data.tokens.utils.ResponseCryptoCurrenciesFactory
import com.tangem.datasource.local.network.NetworksStatusesStore
import com.tangem.datasource.local.token.UserTokensStore
import com.tangem.datasource.local.userwallet.UserWalletsStore

View file

@ -1,6 +1,7 @@
package com.tangem.data.tokens.utils
import com.tangem.blockchain.common.Blockchain
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.derivationStyleProvider

View file

@ -17,7 +17,7 @@ dependencies {
implementation(projects.core.utils)
/** Data modules */
implementation(projects.data.tokens)
implementation(projects.data.common)
/** Domain modules */
implementation(projects.domain.legacy)

View file

@ -3,7 +3,7 @@ package com.tangem.feature.referral.data
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.datasource.api.common.AuthProvider
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.api.tangemTech.models.StartReferralBody

View file

@ -37,7 +37,7 @@ dependencies {
implementation(projects.domain.wallets.models)
/** Data */
implementation(projects.data.tokens)
implementation(projects.data.common)
/** Tangem SDKs */
implementation(deps.tangem.blockchain)

View file

@ -8,7 +8,7 @@ import arrow.core.right
import com.squareup.moshi.Moshi
import com.tangem.blockchain.common.*
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.data.tokens.utils.CryptoCurrencyFactory
import com.tangem.data.common.currency.CryptoCurrencyFactory
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.common.response.ApiResponseError
import com.tangem.datasource.api.common.response.getOrThrow

View file

@ -1,7 +1,7 @@
package com.tangem.feature.swap.converters
import com.tangem.data.tokens.utils.ResponseCryptoCurrenciesFactory
import com.tangem.data.tokens.utils.UserTokensResponseFactory
import com.tangem.data.common.currency.ResponseCryptoCurrenciesFactory
import com.tangem.data.common.currency.UserTokensResponseFactory
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId

View file

@ -250,4 +250,5 @@ include(":data:qr-scanning")
include(":data:staking")
include(":data:wallet-connect")
include(":data:markets")
include(":data:manage-tokens")
// endregion Data modules