Updated on 2026-08-14
This commit is contained in:
parent
473a936863
commit
df938519a2
13 changed files with 155 additions and 32 deletions
|
|
@ -1,37 +1,46 @@
|
|||
package com.tangem.data.managetokens
|
||||
|
||||
import com.tangem.blockchain.blockchains.cardano.CardanoTokenAddressConverter
|
||||
import com.tangem.blockchain.blockchains.hedera.HederaTokenAddressConverter
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.data.common.api.safeApiCall
|
||||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
import com.tangem.data.common.currency.getBlockchain
|
||||
import com.tangem.data.common.currency.UserTokensResponseFactory
|
||||
import com.tangem.data.common.currency.getNetwork
|
||||
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.supportedBlockchains
|
||||
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.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultCustomTokensRepository(
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val userWalletsStore: UserWalletsStore,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : CustomTokensRepository {
|
||||
|
||||
private val cryptoCurrencyFactory = CryptoCurrencyFactory()
|
||||
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) {
|
||||
|
|
@ -73,7 +82,7 @@ internal class DefaultCustomTokensRepository(
|
|||
val userWallet = userWalletsStore.getSyncOrNull(userWalletId)
|
||||
?: error("User wallet not found")
|
||||
val network = getNetwork(networkId, derivationPath)
|
||||
val tokenAddress = convertTokenAddress(
|
||||
val tokenAddress = tokenAddressConverter.convertTokenAddress(
|
||||
networkId,
|
||||
contractAddress,
|
||||
symbol = null,
|
||||
|
|
@ -112,10 +121,7 @@ internal class DefaultCustomTokensRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun createCoin(
|
||||
networkId: Network.ID,
|
||||
derivationPath: Network.DerivationPath,
|
||||
): CryptoCurrency.Coin {
|
||||
override fun createCoin(networkId: Network.ID, derivationPath: Network.DerivationPath): CryptoCurrency.Coin {
|
||||
val network = getNetwork(networkId, derivationPath)
|
||||
|
||||
return cryptoCurrencyFactory.createCoin(network)
|
||||
|
|
@ -127,7 +133,7 @@ internal class DefaultCustomTokensRepository(
|
|||
formValues: AddCustomTokenForm.Validated.All,
|
||||
): CryptoCurrency.Token {
|
||||
val network = getNetwork(networkId, derivationPath)
|
||||
val tokenAddress = convertTokenAddress(
|
||||
val tokenAddress = tokenAddressConverter.convertTokenAddress(
|
||||
networkId,
|
||||
formValues.contractAddress,
|
||||
formValues.symbol,
|
||||
|
|
@ -143,20 +149,54 @@ internal class DefaultCustomTokensRepository(
|
|||
)
|
||||
}
|
||||
|
||||
private fun convertTokenAddress(networkId: Network.ID, contractAddress: String, symbol: String?): String {
|
||||
val convertedAddress = when (getBlockchain(networkId)) {
|
||||
Blockchain.Hedera,
|
||||
Blockchain.HederaTestnet,
|
||||
-> HederaTokenAddressConverter().convertToTokenId(contractAddress)
|
||||
Blockchain.Cardano -> {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
CardanoTokenAddressConverter().convertToFingerprint(contractAddress, symbol)
|
||||
override suspend fun removeCurrency(userWalletId: UserWalletId, currency: ManagedCryptoCurrency.Custom) =
|
||||
withContext(dispatchers.io) {
|
||||
val cryptoCurrency = when (currency) {
|
||||
is ManagedCryptoCurrency.Custom.Coin -> createCoin(currency.network.id, currency.network.derivationPath)
|
||||
is ManagedCryptoCurrency.Custom.Token -> cryptoCurrencyFactory.createToken(
|
||||
network = currency.network,
|
||||
rawId = currency.currencyId.rawCurrencyId,
|
||||
name = currency.name,
|
||||
symbol = currency.symbol,
|
||||
decimals = currency.decimals,
|
||||
contractAddress = currency.contractAddress,
|
||||
)
|
||||
}
|
||||
|
||||
val savedCurrencies = requireNotNull(
|
||||
value = getSavedUserTokensResponseSync(key = userWalletId),
|
||||
lazyMessage = { "Saved tokens empty. Can not perform remove currency action" },
|
||||
)
|
||||
val token = userTokensResponseFactory.createResponseToken(cryptoCurrency)
|
||||
storeAndPushTokens(
|
||||
userWalletId = userWalletId,
|
||||
response = savedCurrencies.copy(tokens = savedCurrencies.tokens.filterNot { it == token }),
|
||||
)
|
||||
when (cryptoCurrency) {
|
||||
is CryptoCurrency.Coin -> walletManagersFacade.remove(userWalletId, setOf(cryptoCurrency.network))
|
||||
is CryptoCurrency.Token -> walletManagersFacade.removeTokens(userWalletId, setOf(cryptoCurrency))
|
||||
}
|
||||
else -> contractAddress
|
||||
}
|
||||
|
||||
return requireNotNull(convertedAddress) {
|
||||
"Token contract address is invalid"
|
||||
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 save user tokens for: ${userWalletId.stringValue}")
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getSavedUserTokensResponseSync(key: UserWalletId): UserTokensResponse? {
|
||||
return appPreferencesStore.getObjectSyncOrNull<UserTokensResponse>(
|
||||
key = PreferencesKeys.getUserTokensKey(key.stringValue),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -52,12 +52,14 @@ internal object ManageTokensDataModule {
|
|||
tangemTechApi: TangemTechApi,
|
||||
userWalletsStore: UserWalletsStore,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): CustomTokensRepository {
|
||||
return DefaultCustomTokensRepository(
|
||||
tangemTechApi,
|
||||
userWalletsStore,
|
||||
appPreferencesStore,
|
||||
walletManagersFacade,
|
||||
dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ internal class ManagedCryptoCurrencyFactory {
|
|||
iconUrl = token.id?.let { getIconUrl(it, imageHost) },
|
||||
contractAddress = contractAddress,
|
||||
network = network,
|
||||
decimals = token.decimals,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.data.managetokens.utils
|
||||
|
||||
import com.tangem.blockchain.blockchains.cardano.CardanoTokenAddressConverter
|
||||
import com.tangem.blockchain.blockchains.hedera.HederaTokenAddressConverter
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.data.common.currency.getBlockchain
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
||||
internal class TokenAddressesConverter {
|
||||
private val hederaTokenAddressConverter = HederaTokenAddressConverter()
|
||||
private val cardanoTokenAddressConverter = CardanoTokenAddressConverter()
|
||||
|
||||
fun convertTokenAddress(networkId: Network.ID, contractAddress: String, symbol: String?): String {
|
||||
val convertedAddress = when (getBlockchain(networkId)) {
|
||||
Blockchain.Hedera,
|
||||
Blockchain.HederaTestnet,
|
||||
-> hederaTokenAddressConverter.convertToTokenId(contractAddress)
|
||||
Blockchain.Cardano -> {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
cardanoTokenAddressConverter.convertToFingerprint(contractAddress, symbol)
|
||||
}
|
||||
else -> contractAddress
|
||||
}
|
||||
|
||||
return requireNotNull(convertedAddress) {
|
||||
"Token contract address is invalid"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -153,15 +153,7 @@ internal class DefaultCurrenciesRepository(
|
|||
val token = userTokensResponseFactory.createResponseToken(currency)
|
||||
storeAndPushTokens(
|
||||
userWalletId = userWalletId,
|
||||
response = savedCurrencies.copy(
|
||||
tokens = savedCurrencies.tokens.filterNot {
|
||||
// it's better to compare by fields, to support renaming and etc
|
||||
it.contractAddress == token.contractAddress &&
|
||||
it.networkId == token.networkId &&
|
||||
it.derivationPath == token.derivationPath &&
|
||||
it.decimals == token.decimals
|
||||
},
|
||||
),
|
||||
response = savedCurrencies.copy(tokens = savedCurrencies.tokens.filterNot { it == token }),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue