Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement fetching token info from node via rpc call #269

Merged
merged 1 commit into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<Any>()
}
Original file line number Diff line number Diff line change
@@ -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<Any>()
}
Original file line number Diff line number Diff line change
@@ -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<Any>()
}
Original file line number Diff line number Diff line change
@@ -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<TokenInfo> {
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<Int> {
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<String> {
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<String> {
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))
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Original file line number Diff line number Diff line change
Expand Up @@ -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 -> {
Expand Down