From 7aadf9d670e271849153bad6c6eda233d476d4c2 Mon Sep 17 00:00:00 2001 From: abdrasulov Date: Mon, 23 Jan 2023 14:28:28 +0600 Subject: [PATCH] Implement fetching token info from node via rpc call --- .../erc20kit/contract/DecimalsMethod.kt | 8 ++ .../erc20kit/contract/NameMethod.kt | 8 ++ .../erc20kit/contract/SymbolMethod.kt | 8 ++ .../erc20kit/core/Eip20Provider.kt | 95 +++++++++++++++++++ .../erc20kit/events/TransferEventInstance.kt | 2 +- .../ethereumkit/core/EthereumKit.kt | 4 + 6 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/DecimalsMethod.kt create mode 100644 erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/NameMethod.kt create mode 100644 erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/SymbolMethod.kt create mode 100644 erc20kit/src/main/java/io/horizontalsystems/erc20kit/core/Eip20Provider.kt diff --git a/erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/DecimalsMethod.kt b/erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/DecimalsMethod.kt new file mode 100644 index 00000000..5981f685 --- /dev/null +++ b/erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/DecimalsMethod.kt @@ -0,0 +1,8 @@ +package io.horizontalsystems.erc20kit.contract + +import io.horizontalsystems.ethereumkit.contracts.ContractMethod + +class DecimalsMethod: ContractMethod() { + override var methodSignature = "decimals()" + override fun getArguments() = listOf() +} diff --git a/erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/NameMethod.kt b/erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/NameMethod.kt new file mode 100644 index 00000000..498d4039 --- /dev/null +++ b/erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/NameMethod.kt @@ -0,0 +1,8 @@ +package io.horizontalsystems.erc20kit.contract + +import io.horizontalsystems.ethereumkit.contracts.ContractMethod + +class NameMethod: ContractMethod() { + override var methodSignature = "name()" + override fun getArguments() = listOf() +} diff --git a/erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/SymbolMethod.kt b/erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/SymbolMethod.kt new file mode 100644 index 00000000..e946a692 --- /dev/null +++ b/erc20kit/src/main/java/io/horizontalsystems/erc20kit/contract/SymbolMethod.kt @@ -0,0 +1,8 @@ +package io.horizontalsystems.erc20kit.contract + +import io.horizontalsystems.ethereumkit.contracts.ContractMethod + +class SymbolMethod: ContractMethod() { + override var methodSignature = "symbol()" + override fun getArguments() = listOf() +} diff --git a/erc20kit/src/main/java/io/horizontalsystems/erc20kit/core/Eip20Provider.kt b/erc20kit/src/main/java/io/horizontalsystems/erc20kit/core/Eip20Provider.kt new file mode 100644 index 00000000..65a621fc --- /dev/null +++ b/erc20kit/src/main/java/io/horizontalsystems/erc20kit/core/Eip20Provider.kt @@ -0,0 +1,95 @@ +package io.horizontalsystems.erc20kit.core + +import io.horizontalsystems.erc20kit.contract.DecimalsMethod +import io.horizontalsystems.erc20kit.contract.NameMethod +import io.horizontalsystems.erc20kit.contract.SymbolMethod +import io.horizontalsystems.erc20kit.events.TokenInfo +import io.horizontalsystems.ethereumkit.api.core.IRpcApiProvider +import io.horizontalsystems.ethereumkit.api.core.RpcBlockchain +import io.horizontalsystems.ethereumkit.contracts.ContractMethodHelper +import io.horizontalsystems.ethereumkit.core.EthereumKit +import io.horizontalsystems.ethereumkit.models.Address +import io.horizontalsystems.ethereumkit.models.DefaultBlockParameter +import io.horizontalsystems.ethereumkit.models.RpcSource +import io.horizontalsystems.ethereumkit.spv.core.toBigInteger +import io.reactivex.Single + +class Eip20Provider(private val provider: IRpcApiProvider) { + + class TokenNotFoundException : Throwable() + + fun getTokenInfo(contractAddress: Address): Single { + val nameSingle = getTokenName(contractAddress) + val symbolSingle = getTokenSymbol(contractAddress) + val decimalsSingle = getDecimals(contractAddress) + + return Single + .zip(nameSingle, symbolSingle, decimalsSingle) { name, symbol, decimals -> + TokenInfo(name, symbol, decimals) + } + } + + private fun getDecimals(contractAddress: Address): Single { + val callRpc = RpcBlockchain.callRpc( + contractAddress, + DecimalsMethod().encodedABI(), + DefaultBlockParameter.Latest + ) + + return provider.single(callRpc) + .map { + if (it.isEmpty()) throw TokenNotFoundException() + + it.sliceArray(IntRange(0, 31)).toBigInteger().toInt() + } + } + + private fun getTokenSymbol(contractAddress: Address): Single { + val callRpc = RpcBlockchain.callRpc( + contractAddress, + SymbolMethod().encodedABI(), + DefaultBlockParameter.Latest + ) + + return provider.single(callRpc) + .map { + if (it.isEmpty()) throw TokenNotFoundException() + + val argumentTypes = listOf(ByteArray::class) + + val parsedArguments = ContractMethodHelper.decodeABI(it, argumentTypes) + val stringBytes = parsedArguments[0] as? ByteArray ?: throw TokenNotFoundException() + + String(stringBytes) + } + } + + private fun getTokenName(contractAddress: Address): Single { + val callRpc = RpcBlockchain.callRpc( + contractAddress, + NameMethod().encodedABI(), + DefaultBlockParameter.Latest + ) + + return provider.single(callRpc) + .map { + if (it.isEmpty()) throw TokenNotFoundException() + + val argumentTypes = listOf(ByteArray::class) + + val parsedArguments = ContractMethodHelper.decodeABI(it, argumentTypes) + val stringBytes = parsedArguments[0] as? ByteArray ?: throw TokenNotFoundException() + + String(stringBytes) + } + } + + companion object { + + fun instance(rpcSource: RpcSource.Http): Eip20Provider { + return Eip20Provider(EthereumKit.getNodeApiProvider(rpcSource)) + } + + } + +} \ No newline at end of file diff --git a/erc20kit/src/main/java/io/horizontalsystems/erc20kit/events/TransferEventInstance.kt b/erc20kit/src/main/java/io/horizontalsystems/erc20kit/events/TransferEventInstance.kt index 8b8ac1e7..7ed14bb4 100644 --- a/erc20kit/src/main/java/io/horizontalsystems/erc20kit/events/TransferEventInstance.kt +++ b/erc20kit/src/main/java/io/horizontalsystems/erc20kit/events/TransferEventInstance.kt @@ -39,5 +39,5 @@ class TransferEventInstance( } } -class TokenInfo(val tokenName: String, val tokenSymbol: String, val tokenDecimal: Int) +data class TokenInfo(val tokenName: String, val tokenSymbol: String, val tokenDecimal: Int) diff --git a/ethereumkit/src/main/java/io/horizontalsystems/ethereumkit/core/EthereumKit.kt b/ethereumkit/src/main/java/io/horizontalsystems/ethereumkit/core/EthereumKit.kt index 7bbc344d..07ed087b 100644 --- a/ethereumkit/src/main/java/io/horizontalsystems/ethereumkit/core/EthereumKit.kt +++ b/ethereumkit/src/main/java/io/horizontalsystems/ethereumkit/core/EthereumKit.kt @@ -438,6 +438,10 @@ class EthereumKit( EthereumDatabaseManager.clear(context, chain, walletId) } + fun getNodeApiProvider(rpcSource: RpcSource.Http): NodeApiProvider { + return NodeApiProvider(rpcSource.urls, gson, rpcSource.auth) + } + private fun transactionProvider(transactionSource: TransactionSource, address: Address): ITransactionProvider { when (transactionSource.type) { is TransactionSource.SourceType.Etherscan -> {