Skip to content

Commit

Permalink
Add static estimateGas methods
Browse files Browse the repository at this point in the history
  • Loading branch information
omurovch committed Jan 15, 2024
1 parent 7adf26e commit 32cd40c
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,46 @@
package io.horizontalsystems.ethereumkit.api.core

import io.horizontalsystems.ethereumkit.api.jsonrpc.*
import io.horizontalsystems.ethereumkit.api.jsonrpc.BlockNumberJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.CallJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.DataJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.EstimateGasJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.GetBalanceJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.GetBlockByNumberJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.GetLogsJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.GetStorageAtJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.GetTransactionByHashJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.GetTransactionCountJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.GetTransactionReceiptJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.JsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.SendRawTransactionJsonRpc
import io.horizontalsystems.ethereumkit.api.jsonrpc.models.RpcBlock
import io.horizontalsystems.ethereumkit.api.jsonrpc.models.RpcTransaction
import io.horizontalsystems.ethereumkit.api.jsonrpc.models.RpcTransactionReceipt
import io.horizontalsystems.ethereumkit.api.models.AccountState
import io.horizontalsystems.ethereumkit.core.*
import io.horizontalsystems.ethereumkit.core.EthereumKit
import io.horizontalsystems.ethereumkit.core.EthereumKit.SyncState
import io.horizontalsystems.ethereumkit.models.*
import io.horizontalsystems.ethereumkit.core.IApiStorage
import io.horizontalsystems.ethereumkit.core.IBlockchain
import io.horizontalsystems.ethereumkit.core.IBlockchainListener
import io.horizontalsystems.ethereumkit.core.TransactionBuilder
import io.horizontalsystems.ethereumkit.models.Address
import io.horizontalsystems.ethereumkit.models.DefaultBlockParameter
import io.horizontalsystems.ethereumkit.models.GasPrice
import io.horizontalsystems.ethereumkit.models.RawTransaction
import io.horizontalsystems.ethereumkit.models.RpcSource
import io.horizontalsystems.ethereumkit.models.Signature
import io.horizontalsystems.ethereumkit.models.Transaction
import io.horizontalsystems.ethereumkit.models.TransactionLog
import io.reactivex.Single
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import java.math.BigInteger

class RpcBlockchain(
private val address: Address,
private val storage: IApiStorage,
private val syncer: IRpcSyncer,
private val transactionBuilder: TransactionBuilder
private val address: Address,
private val storage: IApiStorage,
private val syncer: IRpcSyncer,
private val transactionBuilder: TransactionBuilder
) : IBlockchain, IRpcSyncerListener {

private val disposables = CompositeDisposable()
Expand All @@ -34,32 +57,32 @@ class RpcBlockchain(

private fun syncLastBlockHeight() {
syncer.single(BlockNumberJsonRpc())
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe({ lastBlockNumber ->
onUpdateLastBlockHeight(lastBlockNumber)
}, {
syncState = SyncState.NotSynced(it)
}).let {
disposables.add(it)
}
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe({ lastBlockNumber ->
onUpdateLastBlockHeight(lastBlockNumber)
}, {
syncState = SyncState.NotSynced(it)
}).let {
disposables.add(it)
}
}

override fun syncAccountState() {
Single.zip(
syncer.single(GetBalanceJsonRpc(address, DefaultBlockParameter.Latest)),
syncer.single(GetTransactionCountJsonRpc(address, DefaultBlockParameter.Latest))
syncer.single(GetBalanceJsonRpc(address, DefaultBlockParameter.Latest)),
syncer.single(GetTransactionCountJsonRpc(address, DefaultBlockParameter.Latest))
) { t1, t2 -> Pair(t1, t2) }
.subscribeOn(Schedulers.io())
.subscribe({ (balance, nonce) ->
onUpdateAccountState(AccountState(balance, nonce))
syncState = SyncState.Synced()
}, {
it?.printStackTrace()
syncState = SyncState.NotSynced(it)
}).let {
disposables.add(it)
}
.subscribeOn(Schedulers.io())
.subscribe({ (balance, nonce) ->
onUpdateAccountState(AccountState(balance, nonce))
syncState = SyncState.Synced()
}, {
it?.printStackTrace()
syncState = SyncState.NotSynced(it)
}).let {
disposables.add(it)
}
}


Expand Down Expand Up @@ -92,10 +115,12 @@ class RpcBlockchain(
when (syncer.state) {
SyncerState.Preparing -> {
}

SyncerState.Ready -> {
syncAccountState()
syncLastBlockHeight()
}

is SyncerState.NotReady -> {
syncer.start()
}
Expand All @@ -111,7 +136,7 @@ class RpcBlockchain(
val encoded = transactionBuilder.encode(rawTransaction, signature)

return syncer.single(SendRawTransactionJsonRpc(encoded))
.map { transaction }
.map { transaction }
}

override fun getNonce(defaultBlockParameter: DefaultBlockParameter): Single<Long> {
Expand All @@ -134,23 +159,36 @@ class RpcBlockchain(
return syncer.single(GetBlockByNumberJsonRpc(blockNumber))
}

override fun getLogs(address: Address?, topics: List<ByteArray?>, fromBlock: Long, toBlock: Long, pullTimestamps: Boolean): Single<List<TransactionLog>> {
return syncer.single(GetLogsJsonRpc(address, DefaultBlockParameter.BlockNumber(fromBlock), DefaultBlockParameter.BlockNumber(toBlock), topics))
.flatMap { logs ->
if (pullTimestamps) {
pullTransactionTimestamps(logs)
} else {
Single.just(logs)
}
override fun getLogs(
address: Address?,
topics: List<ByteArray?>,
fromBlock: Long,
toBlock: Long,
pullTimestamps: Boolean
): Single<List<TransactionLog>> {
return syncer.single(
GetLogsJsonRpc(
address,
DefaultBlockParameter.BlockNumber(fromBlock),
DefaultBlockParameter.BlockNumber(toBlock),
topics
)
)
.flatMap { logs ->
if (pullTimestamps) {
pullTransactionTimestamps(logs)
} else {
Single.just(logs)
}
}
}

private fun pullTransactionTimestamps(logs: List<TransactionLog>): Single<List<TransactionLog>> {
val logsByBlockNumber: MutableMap<Long, MutableList<TransactionLog>> = mutableMapOf()

for (log in logs) {
val logs: MutableList<TransactionLog> = logsByBlockNumber[log.blockNumber]
?: mutableListOf()
?: mutableListOf()
logs.add(log)
logsByBlockNumber[log.blockNumber] = logs
}
Expand Down Expand Up @@ -200,11 +238,13 @@ class RpcBlockchain(
SyncerState.Preparing -> {
syncState = SyncState.Syncing()
}

SyncerState.Ready -> {
syncState = SyncState.Syncing()
syncAccountState()
syncLastBlockHeight()
}

is SyncerState.NotReady -> {
syncState = SyncState.NotSynced(state.error)
disposables.clear()
Expand All @@ -215,10 +255,12 @@ class RpcBlockchain(
//endregion

companion object {
fun instance(address: Address,
storage: IApiStorage,
syncer: IRpcSyncer,
transactionBuilder: TransactionBuilder): RpcBlockchain {
fun instance(
address: Address,
storage: IApiStorage,
syncer: IRpcSyncer,
transactionBuilder: TransactionBuilder
): RpcBlockchain {

val rpcBlockchain = RpcBlockchain(address, storage, syncer, transactionBuilder)
syncer.listener = rpcBlockchain
Expand All @@ -228,5 +270,25 @@ class RpcBlockchain(

fun callRpc(contractAddress: Address, data: ByteArray, defaultBlockParameter: DefaultBlockParameter): DataJsonRpc =
CallJsonRpc(contractAddress, data, defaultBlockParameter)

fun estimateGas(
rpcSource: RpcSource,
from: Address,
to: Address?,
amount: BigInteger?,
gasLimit: Long?,
gasPrice: GasPrice,
data: ByteArray?
): Single<Long> {
val rpcApiProvider: IRpcApiProvider = when (rpcSource) {
is RpcSource.Http -> {
NodeApiProvider(rpcSource.uris, EthereumKit.gson, rpcSource.auth)
}

is RpcSource.WebSocket -> throw IllegalStateException("Websocket not supported")
}

return rpcApiProvider.single(EstimateGasJsonRpc(from, to, amount, gasLimit, gasPrice, data))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,11 @@ class EthereumKit(
return blockchain.getStorageAt(contractAddress, position, defaultBlockParameter)
}

fun call(contractAddress: Address, data: ByteArray, defaultBlockParameter: DefaultBlockParameter = DefaultBlockParameter.Latest): Single<ByteArray> {
fun call(
contractAddress: Address,
data: ByteArray,
defaultBlockParameter: DefaultBlockParameter = DefaultBlockParameter.Latest
): Single<ByteArray> {
return blockchain.call(contractAddress, data, defaultBlockParameter)
}

Expand Down Expand Up @@ -394,6 +398,28 @@ class EthereumKit(
return rpcApiProvider.single(rpc)
}

fun estimateGas(
rpcSource: RpcSource,
chain: Chain,
from: Address,
to: Address?,
value: BigInteger?,
gasPrice: GasPrice,
data: ByteArray?
): Single<Long> {
return RpcBlockchain.estimateGas(rpcSource, from, to, value, chain.gasLimit, gasPrice, data)
}

fun estimateGas(
rpcSource: RpcSource,
chain: Chain,
from: Address,
transactionData: TransactionData,
gasPrice: GasPrice
): Single<Long> {
return estimateGas(rpcSource, chain, from, transactionData.to, transactionData.value, gasPrice, transactionData.input)
}

fun init() {
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME)
Security.addProvider(InternalBouncyCastleProvider.getInstance())
Expand Down

0 comments on commit 32cd40c

Please sign in to comment.