diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt index db25b8597e..9d48157eef 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultCustomTokensRepository.kt @@ -7,6 +7,7 @@ import com.tangem.blockchainsdk.utils.toNetworkId import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.data.common.currency.CryptoCurrencyFactory import com.tangem.data.common.network.NetworkFactory +import com.tangem.data.managetokens.utils.HederaTokenAddressResolver import com.tangem.data.managetokens.utils.TokenAddressesConverter import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.tangemTech.TangemTechApi @@ -32,6 +33,7 @@ internal class DefaultCustomTokensRepository( private val excludedBlockchains: ExcludedBlockchains, private val dispatchers: CoroutineDispatcherProvider, private val networkFactory: NetworkFactory, + hederaTokenAddressResolver: HederaTokenAddressResolver, ) : CustomTokensRepository { private val excludedBlockchainsForCustom = setOf( @@ -40,7 +42,7 @@ internal class DefaultCustomTokensRepository( ) private val cryptoCurrencyFactory = CryptoCurrencyFactory(excludedBlockchains) - private val tokenAddressConverter = TokenAddressesConverter() + private val tokenAddressConverter = TokenAddressesConverter(hederaTokenAddressResolver) override suspend fun validateContractAddress(contractAddress: String, networkId: Network.ID): Boolean = withContext(dispatchers.io) { @@ -53,6 +55,8 @@ internal class DefaultCustomTokensRepository( Blockchain.TerraV2, -> true Blockchain.Cardano, + Blockchain.Hedera, + Blockchain.HederaTestnet, Blockchain.Sui, Blockchain.Stellar, Blockchain.XRP, @@ -89,30 +93,39 @@ internal class DefaultCustomTokensRepository( .filter(Blockchain::canHandleTokens) .map(Blockchain::toNetworkId) - val response = tangemTechApi.getCoins( - contractAddress = contractAddress, - networkIds = network.rawId, - active = true, - ).getOrThrow() - - response.coins.firstNotNullOfOrNull { coin -> - val coinNetwork = coin.networks.firstOrNull { network -> - (network.contractAddress != null || network.decimalCount != null) && - network.contractAddress.equals(tokenAddress, ignoreCase = true) && - network.networkId in supportedTokenNetworkIds + val addressesToTry = buildList { + add(tokenAddress) + if (!contractAddress.equals(tokenAddress, ignoreCase = true)) { + add(contractAddress) } + } - if (coinNetwork != null) { - cryptoCurrencyFactory.createToken( - network = network, - rawId = CryptoCurrency.RawID(coin.id), - name = coin.name, - symbol = coin.symbol, - decimals = requireNotNull(coinNetwork.decimalCount).toInt(), - contractAddress = tokenAddress, - ) - } else { - null + addressesToTry.firstNotNullOfOrNull { searchAddress -> + val response = tangemTechApi.getCoins( + contractAddress = searchAddress, + networkIds = network.rawId, + active = true, + ).getOrThrow() + + response.coins.firstNotNullOfOrNull { coin -> + val coinNetwork = coin.networks.firstOrNull { network -> + (network.contractAddress != null || network.decimalCount != null) && + network.contractAddress.equals(tokenAddress, ignoreCase = true) && + network.networkId in supportedTokenNetworkIds + } + + if (coinNetwork != null) { + cryptoCurrencyFactory.createToken( + network = network, + rawId = CryptoCurrency.RawID(coin.id), + name = coin.name, + symbol = coin.symbol, + decimals = requireNotNull(coinNetwork.decimalCount).toInt(), + contractAddress = tokenAddress, + ) + } else { + null + } } } } diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt index 2df7ecce2c..7c1a1c7dd8 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/di/ManageTokensDataModule.kt @@ -1,10 +1,12 @@ package com.tangem.data.managetokens.di +import com.tangem.blockchainsdk.providers.BlockchainProviderTypesStore import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.data.common.account.WalletAccountsFetcher 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.HederaTokenAddressResolver import com.tangem.data.managetokens.utils.ManageTokensUpdateFetcher import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.local.config.testnet.TestnetTokensStorage @@ -54,6 +56,7 @@ internal object ManageTokensDataModule { dispatchers: CoroutineDispatcherProvider, excludedBlockchains: ExcludedBlockchains, networkFactory: NetworkFactory, + blockchainProviderTypesStore: BlockchainProviderTypesStore, ): CustomTokensRepository { return DefaultCustomTokensRepository( tangemTechApi = tangemTechApi, @@ -61,6 +64,7 @@ internal object ManageTokensDataModule { excludedBlockchains = excludedBlockchains, dispatchers = dispatchers, networkFactory = networkFactory, + hederaTokenAddressResolver = HederaTokenAddressResolver(blockchainProviderTypesStore), ) } } \ No newline at end of file diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/HederaTokenAddressResolver.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/HederaTokenAddressResolver.kt new file mode 100644 index 0000000000..f48ecbe0f0 --- /dev/null +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/HederaTokenAddressResolver.kt @@ -0,0 +1,41 @@ +package com.tangem.data.managetokens.utils + +import com.tangem.blockchain.blockchains.hedera.HederaContractIdResolver +import com.tangem.blockchain.blockchains.hedera.HederaTokenAddressConverter +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.network.providers.ProviderType +import com.tangem.blockchainsdk.providers.BlockchainProviderTypesStore +import java.util.concurrent.ConcurrentHashMap + +internal class HederaTokenAddressResolver( + private val blockchainProviderTypesStore: BlockchainProviderTypesStore, +) { + private val tokenAddressConverter = HederaTokenAddressConverter() + private val cache = ConcurrentHashMap() + + suspend fun resolveAddress(blockchain: Blockchain, contractAddress: String): String { + cache[contractAddress.lowercase()]?.let { return it } + + val resolver = HederaContractIdResolver(baseUrl = getBaseUrl(blockchain)) + val resolved = tokenAddressConverter.resolveTokenId(contractAddress) { resolver.resolve(it) } + + require(!resolved.startsWith("0x", ignoreCase = true)) { + "Failed to resolve Hedera contract ID for EVM address: $contractAddress" + } + + cache[contractAddress.lowercase()] = resolved + return resolved + } + + private fun getBaseUrl(blockchain: Blockchain): String { + val providerTypes = blockchainProviderTypesStore.get().value + return requireNotNull( + providerTypes[blockchain] + ?.filterIsInstance() + ?.firstOrNull() + ?.url, + ) { + "Hedera provider URL not found for $blockchain" + } + } +} \ No newline at end of file diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/TokenAddressesConverter.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/TokenAddressesConverter.kt index bb7e90cba1..e663936303 100644 --- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/TokenAddressesConverter.kt +++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/utils/TokenAddressesConverter.kt @@ -1,7 +1,6 @@ package com.tangem.data.managetokens.utils import com.tangem.blockchain.blockchains.cardano.CardanoTokenAddressConverter -import com.tangem.blockchain.blockchains.hedera.HederaTokenAddressConverter import com.tangem.blockchain.blockchains.stellar.StellarTokenAddressConverter import com.tangem.blockchain.blockchains.sui.SuiTokenAddressConverter import com.tangem.blockchain.blockchains.xrp.XrpTokenAddressConverter @@ -9,18 +8,19 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.utils.toBlockchain import com.tangem.domain.models.network.Network -internal class TokenAddressesConverter { - private val hederaTokenAddressConverter = HederaTokenAddressConverter() +internal class TokenAddressesConverter( + private val hederaTokenAddressResolver: HederaTokenAddressResolver, +) { private val cardanoTokenAddressConverter = CardanoTokenAddressConverter() private val xrpTokenAddressConverter = XrpTokenAddressConverter() private val stellarTokenAddressConverter = StellarTokenAddressConverter() private val suiTokenAddressConverter = SuiTokenAddressConverter() - fun convertTokenAddress(networkId: Network.ID, contractAddress: String, symbol: String?): String { - val convertedAddress = when (networkId.toBlockchain()) { + suspend fun convertTokenAddress(networkId: Network.ID, contractAddress: String, symbol: String?): String { + val convertedAddress = when (val blockchain = networkId.toBlockchain()) { Blockchain.Hedera, Blockchain.HederaTestnet, - -> hederaTokenAddressConverter.convertToTokenId(contractAddress) + -> hederaTokenAddressResolver.resolveAddress(blockchain, contractAddress) Blockchain.Sui, Blockchain.SuiTestnet, -> suiTokenAddressConverter.normalizeAddress(contractAddress) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index f191596235..2e558b3c64 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,7 +5,7 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "develop-1498" +tangemBlockchainSdk = "develop-1502" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-614" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^