Updated on 2026-08-14
This commit is contained in:
commit
1bac8466b1
990 changed files with 22183 additions and 7489 deletions
|
|
@ -2,21 +2,20 @@ package com.tangem.data.managetokens
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.data.common.api.safeApiCall
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||
import com.tangem.data.common.currency.getNetwork
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.managetokens.utils.TokenAddressesConverter
|
||||
import com.tangem.data.tokens.utils.UserTokensBackwardCompatibility
|
||||
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.preferences.utils.storeObject
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.common.extensions.canHandleBlockchain
|
||||
import com.tangem.domain.common.extensions.supportedBlockchains
|
||||
|
|
@ -24,14 +23,14 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.managetokens.model.AddCustomTokenForm
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||
import com.tangem.domain.managetokens.repository.CustomTokensRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultCustomTokensRepository(
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
|
|
@ -39,16 +38,17 @@ internal class DefaultCustomTokensRepository(
|
|||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val userTokensSaver: UserTokensSaver,
|
||||
private val networkFactory: NetworkFactory,
|
||||
) : CustomTokensRepository {
|
||||
|
||||
private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains)
|
||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||
private val tokenAddressConverter = TokenAddressesConverter()
|
||||
private val userTokensBackwardCompatibility = UserTokensBackwardCompatibility()
|
||||
|
||||
override suspend fun validateContractAddress(contractAddress: String, networkId: Network.ID): Boolean =
|
||||
withContext(dispatchers.io) {
|
||||
when (val blockchain = Blockchain.fromId(networkId.value)) {
|
||||
when (val blockchain = networkId.toBlockchain()) {
|
||||
Blockchain.Unknown,
|
||||
Blockchain.Binance,
|
||||
Blockchain.BinanceTestnet,
|
||||
|
|
@ -76,7 +76,7 @@ internal class DefaultCustomTokensRepository(
|
|||
}
|
||||
|
||||
storedCurrencies.tokens.none { token ->
|
||||
Blockchain.fromId(networkId.value).toNetworkId() == token.networkId &&
|
||||
networkId.toBlockchain().toNetworkId() == token.networkId &&
|
||||
derivationPath.value == token.derivationPath &&
|
||||
contractAddress.equals(token.contractAddress, ignoreCase = true)
|
||||
}
|
||||
|
|
@ -93,7 +93,11 @@ internal class DefaultCustomTokensRepository(
|
|||
"User wallet [$userWalletId] not found while finding token"
|
||||
}
|
||||
val network = requireNotNull(
|
||||
getNetwork(networkId, derivationPath, userWallet.scanResponse, excludedBlockchains),
|
||||
networkFactory.create(
|
||||
networkId = networkId,
|
||||
derivationPath = derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
),
|
||||
) {
|
||||
"Network [$networkId] not found while finding token"
|
||||
}
|
||||
|
|
@ -145,7 +149,11 @@ internal class DefaultCustomTokensRepository(
|
|||
"User wallet [$userWalletId] not found while creating coin"
|
||||
}
|
||||
val network = requireNotNull(
|
||||
getNetwork(networkId, derivationPath, userWallet.scanResponse, excludedBlockchains),
|
||||
networkFactory.create(
|
||||
networkId = networkId,
|
||||
derivationPath = derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
),
|
||||
) {
|
||||
"Network [$networkId] not found while creating coin"
|
||||
}
|
||||
|
|
@ -178,7 +186,11 @@ internal class DefaultCustomTokensRepository(
|
|||
"User wallet [$userWalletId] not found while creating custom token"
|
||||
}
|
||||
val network = requireNotNull(
|
||||
getNetwork(networkId, derivationPath, userWallet.scanResponse, excludedBlockchains),
|
||||
networkFactory.create(
|
||||
networkId = networkId,
|
||||
derivationPath = derivationPath,
|
||||
scanResponse = userWallet.scanResponse,
|
||||
),
|
||||
) {
|
||||
"Network [$networkId] not found while creating custom token"
|
||||
}
|
||||
|
|
@ -223,7 +235,7 @@ internal class DefaultCustomTokensRepository(
|
|||
}
|
||||
|
||||
val token = userTokensResponseFactory.createResponseToken(cryptoCurrency)
|
||||
storeAndPushTokens(
|
||||
userTokensSaver.storeAndPush(
|
||||
userWalletId = userWalletId,
|
||||
response = storedCurrencies.copy(tokens = storedCurrencies.tokens.filterNot { it == token }),
|
||||
)
|
||||
|
|
@ -248,11 +260,10 @@ internal class DefaultCustomTokensRepository(
|
|||
)
|
||||
|
||||
if (canHandleBlockchain) {
|
||||
getNetwork(
|
||||
networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = null,
|
||||
scanResponse = scanResponse,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
@ -267,20 +278,4 @@ internal class DefaultCustomTokensRepository(
|
|||
value = sdkPath.rawPath,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun storeAndPushTokens(userWalletId: UserWalletId, response: UserTokensResponse) {
|
||||
val compatibleUserTokensResponse = userTokensBackwardCompatibility.applyCompatibilityAndGetUpdated(response)
|
||||
appPreferencesStore.storeObject(
|
||||
key = PreferencesKeys.getUserTokensKey(userWalletId = userWalletId.stringValue),
|
||||
value = compatibleUserTokensResponse,
|
||||
)
|
||||
|
||||
pushTokens(userWalletId, response)
|
||||
}
|
||||
|
||||
private suspend fun pushTokens(userWalletId: UserWalletId, response: UserTokensResponse) {
|
||||
safeApiCall({ tangemTechApi.saveUserTokens(userWalletId.stringValue, response).bind() }) {
|
||||
Timber.e(it, "Unable to push user tokens for: ${userWalletId.stringValue}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,14 +4,15 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchainsdk.compatibility.l2BlockchainsCoinIds
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.data.common.api.safeApiCall
|
||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||
import com.tangem.data.common.currency.getBlockchain
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.common.utils.retryOnError
|
||||
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
||||
import com.tangem.data.managetokens.utils.ManagedCryptoCurrencyFactory
|
||||
import com.tangem.data.tokens.utils.CardCryptoCurrenciesFactory
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
|
|
@ -26,11 +27,10 @@ import com.tangem.domain.common.extensions.canHandleToken
|
|||
import com.tangem.domain.common.extensions.supportedBlockchains
|
||||
import com.tangem.domain.common.extensions.supportedTokens
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.managetokens.model.*
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||
import com.tangem.domain.managetokens.repository.ManageTokensRepository
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.pagination.BatchFetchResult
|
||||
|
|
@ -48,12 +48,13 @@ internal class DefaultManageTokensRepository(
|
|||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val testnetTokensStorage: TestnetTokensStorage,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
networkFactory: NetworkFactory,
|
||||
) : ManageTokensRepository {
|
||||
|
||||
private val managedCryptoCurrencyFactory = ManagedCryptoCurrencyFactory(excludedBlockchains)
|
||||
private val managedCryptoCurrencyFactory = ManagedCryptoCurrencyFactory(networkFactory, excludedBlockchains)
|
||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||
private val cardCurrenciesFactory = CardCryptoCurrenciesFactory(DemoConfig(), excludedBlockchains)
|
||||
|
||||
// region getTokenListBatchFlow
|
||||
override fun getTokenListBatchFlow(
|
||||
|
|
@ -77,7 +78,7 @@ internal class DefaultManageTokensRepository(
|
|||
prefetchDistance = batchSize,
|
||||
batchSize = batchSize,
|
||||
subFetcher = { request, _, isFirstBatchFetching ->
|
||||
val userWallet = request.params.userWalletId?.let { getUserWallet(it) }
|
||||
val userWallet = request.params.userWalletId?.let(userWalletsStore::getSyncStrict)
|
||||
|
||||
if (userWallet?.scanResponse?.card?.isTestCard == true) {
|
||||
fetchTestnetCurrencies(userWallet, request)
|
||||
|
|
@ -190,17 +191,11 @@ internal class DefaultManageTokensRepository(
|
|||
|
||||
private fun createDefaultUserTokensResponse(userWallet: UserWallet) =
|
||||
userTokensResponseFactory.createUserTokensResponse(
|
||||
currencies = cardCurrenciesFactory.createDefaultCoinsForMultiCurrencyCard(userWallet.scanResponse),
|
||||
currencies = cardCryptoCurrencyFactory.createDefaultCoinsForMultiCurrencyCard(userWallet.scanResponse),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
||||
private suspend fun getUserWallet(userWalletId: UserWalletId): UserWallet {
|
||||
return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"Unable to find a user wallet with provided ID: $userWalletId"
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSupportedBlockchains(userWallet: UserWallet?): List<Blockchain> {
|
||||
return userWallet?.scanResponse?.let {
|
||||
it.card.supportedBlockchains(it.cardTypesResolver, excludedBlockchains)
|
||||
|
|
@ -237,10 +232,11 @@ internal class DefaultManageTokensRepository(
|
|||
token.availableNetworks
|
||||
.filter { sourceNetwork -> networks.contains(sourceNetwork.network) }
|
||||
.map { sourceNetwork ->
|
||||
val blockchain = getBlockchain(sourceNetwork.network.id)
|
||||
val networkId = sourceNetwork.network.toBlockchain().toNetworkId()
|
||||
|
||||
UserTokensResponse.Token(
|
||||
id = token.id.value,
|
||||
networkId = blockchain.toNetworkId(),
|
||||
networkId = networkId,
|
||||
derivationPath = sourceNetwork.network.derivationPath.value,
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
|
|
@ -261,8 +257,9 @@ internal class DefaultManageTokensRepository(
|
|||
userWalletId: UserWalletId,
|
||||
sourceNetwork: SourceNetwork,
|
||||
): CurrencyUnsupportedState? {
|
||||
val userWallet = getUserWallet(userWalletId = userWalletId)
|
||||
val blockchain = getBlockchain(sourceNetwork.id)
|
||||
val userWallet = userWalletsStore.getSyncStrict(key = userWalletId)
|
||||
val blockchain = sourceNetwork.id.toBlockchain()
|
||||
|
||||
return when (sourceNetwork) {
|
||||
is SourceNetwork.Default -> checkTokenUnsupportedState(userWallet = userWallet, blockchain = blockchain)
|
||||
is SourceNetwork.Main -> checkBlockchainUnsupportedState(userWallet = userWallet, blockchain = blockchain)
|
||||
|
|
@ -274,7 +271,7 @@ internal class DefaultManageTokensRepository(
|
|||
rawNetworkId: String,
|
||||
isMainNetwork: Boolean,
|
||||
): CurrencyUnsupportedState? {
|
||||
val userWallet = getUserWallet(userWalletId = userWalletId)
|
||||
val userWallet = userWalletsStore.getSyncStrict(key = userWalletId)
|
||||
val blockchain = Blockchain.fromNetworkId(networkId = rawNetworkId)
|
||||
?: error("Can not create blockchain with given networkId -> $rawNetworkId")
|
||||
return if (isMainNetwork) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.data.managetokens.di
|
||||
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.UserTokensSaver
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.data.managetokens.DefaultCustomTokensRepository
|
||||
import com.tangem.data.managetokens.DefaultManageTokensRepository
|
||||
import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher
|
||||
|
|
@ -32,15 +35,19 @@ internal object ManageTokensDataModule {
|
|||
testnetTokensStorage: TestnetTokensStorage,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
|
||||
networkFactory: NetworkFactory,
|
||||
): ManageTokensRepository {
|
||||
return DefaultManageTokensRepository(
|
||||
tangemTechApi,
|
||||
userWalletsStore,
|
||||
manageTokensUpdateFetcher,
|
||||
appPreferencesStore,
|
||||
testnetTokensStorage,
|
||||
excludedBlockchains,
|
||||
dispatchers,
|
||||
tangemTechApi = tangemTechApi,
|
||||
userWalletsStore = userWalletsStore,
|
||||
manageTokensUpdateFetcher = manageTokensUpdateFetcher,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
testnetTokensStorage = testnetTokensStorage,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
|
||||
networkFactory = networkFactory,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -53,14 +60,18 @@ internal object ManageTokensDataModule {
|
|||
walletManagersFacade: WalletManagersFacade,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
userTokensSaver: UserTokensSaver,
|
||||
networkFactory: NetworkFactory,
|
||||
): CustomTokensRepository {
|
||||
return DefaultCustomTokensRepository(
|
||||
tangemTechApi,
|
||||
userWalletsStore,
|
||||
appPreferencesStore,
|
||||
walletManagersFacade,
|
||||
excludedBlockchains,
|
||||
dispatchers,
|
||||
tangemTechApi = tangemTechApi,
|
||||
userWalletsStore = userWalletsStore,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
dispatchers = dispatchers,
|
||||
userTokensSaver = userTokensSaver,
|
||||
networkFactory = networkFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,8 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
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.data.common.network.NetworkFactory
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.config.testnet.models.TestnetTokensConfig
|
||||
|
|
@ -19,12 +19,13 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import timber.log.Timber
|
||||
|
||||
internal class ManagedCryptoCurrencyFactory(
|
||||
private val networkFactory: NetworkFactory,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
) {
|
||||
|
||||
|
|
@ -98,11 +99,10 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
return null
|
||||
}
|
||||
|
||||
val network = getNetwork(
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = token.derivationPath,
|
||||
scanResponse = scanResponse,
|
||||
excludedBlockchains = excludedBlockchains,
|
||||
) ?: return null
|
||||
val contractAddress = token.contractAddress
|
||||
|
||||
|
|
@ -172,11 +172,10 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
?.takeUnless { it in excludedBlockchains }
|
||||
?: return null
|
||||
|
||||
val network = getNetwork(
|
||||
blockchain,
|
||||
extraDerivationPath,
|
||||
scanResponse?.derivationStyleProvider,
|
||||
excludedBlockchains,
|
||||
val network = networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = extraDerivationPath,
|
||||
derivationStyleProvider = scanResponse?.derivationStyleProvider,
|
||||
canHandleTokens = scanResponse?.let {
|
||||
it.card.canHandleToken(blockchain, it.cardTypesResolver, excludedBlockchains)
|
||||
} ?: true,
|
||||
|
|
@ -217,11 +216,10 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
val blockchain = Blockchain.fromNetworkId(token.networkId)
|
||||
|
||||
if (blockchain != null && blockchain !in excludedBlockchains) {
|
||||
getNetwork(
|
||||
blockchain,
|
||||
token.derivationPath,
|
||||
scanResponse?.derivationStyleProvider,
|
||||
excludedBlockchains,
|
||||
networkFactory.create(
|
||||
blockchain = blockchain,
|
||||
extraDerivationPath = token.derivationPath,
|
||||
derivationStyleProvider = scanResponse?.derivationStyleProvider,
|
||||
canHandleTokens = scanResponse?.let {
|
||||
it.card.canHandleToken(blockchain, it.cardTypesResolver, excludedBlockchains)
|
||||
} ?: true,
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import com.tangem.blockchain.blockchains.cardano.CardanoTokenAddressConverter
|
|||
import com.tangem.blockchain.blockchains.hedera.HederaTokenAddressConverter
|
||||
import com.tangem.blockchain.blockchains.sui.SuiTokenAddressConverter
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.data.common.currency.getBlockchain
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.blockchainsdk.utils.toBlockchain
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
||||
internal class TokenAddressesConverter {
|
||||
private val hederaTokenAddressConverter = HederaTokenAddressConverter()
|
||||
|
|
@ -13,7 +13,7 @@ internal class TokenAddressesConverter {
|
|||
private val suiTokenAddressConverter = SuiTokenAddressConverter()
|
||||
|
||||
fun convertTokenAddress(networkId: Network.ID, contractAddress: String, symbol: String?): String {
|
||||
val convertedAddress = when (getBlockchain(networkId)) {
|
||||
val convertedAddress = when (networkId.toBlockchain()) {
|
||||
Blockchain.Hedera,
|
||||
Blockchain.HederaTestnet,
|
||||
-> hederaTokenAddressConverter.convertToTokenId(contractAddress)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue