Skip to content

Commit

Permalink
ETH blockchain additional methods added, like broadcast, getGasLimit/…
Browse files Browse the repository at this point in the history
…GasPrice etc.
  • Loading branch information
physcom committed Oct 14, 2024
1 parent 1c88250 commit 835e9f5
Show file tree
Hide file tree
Showing 17 changed files with 368 additions and 36 deletions.
11 changes: 5 additions & 6 deletions src/main/kotlin/io/openfuture/state/blockchain/Blockchain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@ import java.math.BigInteger
abstract class Blockchain {

abstract suspend fun getLastBlockNumber(): Int

abstract suspend fun getNonce(address: String): BigInteger
abstract suspend fun broadcastTransaction(signedTransaction: String): String
abstract suspend fun getTransactionStatus(transactionHash: String): Boolean
abstract suspend fun getBlock(blockNumber: Int): UnifiedBlock

abstract suspend fun getGasPrice(): BigInteger
abstract suspend fun getGasLimit(): BigInteger
abstract suspend fun getBalance(address: String): BigDecimal

abstract suspend fun getContractBalance(address: String, contractAddress: String): BigDecimal

open fun getName(): String = javaClass.simpleName

abstract suspend fun getCurrencyCode(): CurrencyCode

override fun toString(): String {
return getName()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import io.openfuture.state.blockchain.dto.UnifiedBlock
import io.openfuture.state.blockchain.dto.UnifiedTransaction
import io.openfuture.state.domain.CurrencyCode
import io.openfuture.state.util.toLocalDateTime
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.future.await
import kotlinx.coroutines.withContext
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Component
import org.web3j.abi.FunctionEncoder
import org.web3j.abi.FunctionReturnDecoder
import org.web3j.abi.TypeReference
import org.web3j.abi.datatypes.Address
import org.web3j.abi.datatypes.generated.Uint256
Expand All @@ -21,6 +22,7 @@ import org.web3j.protocol.core.methods.request.Transaction
import org.web3j.protocol.core.methods.response.EthBlock
import org.web3j.protocol.core.methods.response.EthCall
import org.web3j.utils.Convert
import java.lang.Exception
import java.math.BigDecimal
import java.math.BigInteger

Expand All @@ -33,6 +35,30 @@ class BinanceBlockchain(@Qualifier("web3jBinance") private val web3jBinance: Web
.sendAsync().await()
.blockNumber.toInt()

override suspend fun getNonce(address: String): BigInteger = web3jBinance.ethGetTransactionCount(address,
DefaultBlockParameterName.LATEST
).send().transactionCount

override suspend fun broadcastTransaction(signedTransaction: String): String {
val result = web3jBinance.ethSendRawTransaction(signedTransaction).send()

if (result.hasError()) {
throw Exception(result.error.message)
}

while (!web3jBinance.ethGetTransactionReceipt(result.transactionHash).send().transactionReceipt.isPresent) {
withContext(Dispatchers.IO) {
Thread.sleep(1000)
}
}

return web3jBinance.ethGetTransactionReceipt(result.transactionHash).send().transactionReceipt.get().transactionHash
}

override suspend fun getTransactionStatus(transactionHash: String): Boolean {
TODO("Not yet implemented")
}

override suspend fun getBlock(blockNumber: Int): UnifiedBlock {
val parameter = DefaultBlockParameterNumber(blockNumber.toLong())
val block = web3jBinance.ethGetBlockByNumber(parameter, true)
Expand Down Expand Up @@ -76,6 +102,16 @@ class BinanceBlockchain(@Qualifier("web3jBinance") private val web3jBinance: Web
return Convert.fromWei(contractBalance.toString(), Convert.Unit.ETHER)
}

override suspend fun getGasPrice(): BigInteger {
return web3jBinance.ethGasPrice().sendAsync().await().gasPrice
}

override suspend fun getGasLimit(): BigInteger {
return web3jBinance
.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false)
.sendAsync().await()
.block.gasLimit
}
private suspend fun obtainTransactions(ethBlock: EthBlock.Block): List<UnifiedTransaction> = ethBlock.transactions
.map { it.get() as EthBlock.TransactionObject }
.map { tx ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import io.openfuture.state.blockchain.dto.UnifiedBlock
import io.openfuture.state.blockchain.dto.UnifiedTransaction
import io.openfuture.state.domain.CurrencyCode
import io.openfuture.state.util.toLocalDateTime
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.future.await
import kotlinx.coroutines.withContext
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Component
import org.web3j.abi.FunctionEncoder
Expand All @@ -21,6 +23,7 @@ import org.web3j.protocol.core.methods.request.Transaction
import org.web3j.protocol.core.methods.response.EthBlock
import org.web3j.protocol.core.methods.response.EthCall
import org.web3j.utils.Convert
import java.lang.Exception
import java.math.BigDecimal
import java.math.BigInteger

Expand All @@ -32,6 +35,30 @@ class BinanceTestnetBlockchain(@Qualifier("web3jBinanceTestnet") private val web
.sendAsync().await()
.blockNumber.toInt()

override suspend fun getNonce(address: String): BigInteger = web3jBinanceTestnet.ethGetTransactionCount(address,
DefaultBlockParameterName.LATEST
).send().transactionCount

override suspend fun broadcastTransaction(signedTransaction: String): String {
val result = web3jBinanceTestnet.ethSendRawTransaction(signedTransaction).send()

if (result.hasError()) {
throw Exception(result.error.message)
}

while (!web3jBinanceTestnet.ethGetTransactionReceipt(result.transactionHash).send().transactionReceipt.isPresent) {
withContext(Dispatchers.IO) {
Thread.sleep(1000)
}
}

return web3jBinanceTestnet.ethGetTransactionReceipt(result.transactionHash).send().transactionReceipt.get().transactionHash
}

override suspend fun getTransactionStatus(transactionHash: String): Boolean {
TODO("Not yet implemented")
}

override suspend fun getBlock(blockNumber: Int): UnifiedBlock {
val parameter = DefaultBlockParameterNumber(blockNumber.toLong())
val block = web3jBinanceTestnet.ethGetBlockByNumber(parameter, true)
Expand Down Expand Up @@ -78,6 +105,17 @@ class BinanceTestnetBlockchain(@Qualifier("web3jBinanceTestnet") private val web
return CurrencyCode.BINANCE
}

override suspend fun getGasPrice(): BigInteger {
return web3jBinanceTestnet.ethGasPrice().sendAsync().await().gasPrice
}

override suspend fun getGasLimit(): BigInteger {
return web3jBinanceTestnet
.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false)
.sendAsync().await()
.block.gasLimit
}

private suspend fun obtainTransactions(ethBlock: EthBlock.Block): List<UnifiedTransaction> = ethBlock.transactions
.map { it.get() as EthBlock.TransactionObject }
.map { tx ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,36 @@ class BitcoinBlockchain(private val client: BitcoinClient) : Blockchain() {
return client.getBlockHeight(latestBlockHash)
}

override suspend fun getNonce(address: String): BigInteger {
TODO("Not yet implemented")
}

override suspend fun broadcastTransaction(signedTransaction: String): String {
TODO("Not yet implemented")
}

override suspend fun getTransactionStatus(transactionHash: String): Boolean {
TODO("Not yet implemented")
}

override suspend fun getBlock(blockNumber: Int): UnifiedBlock {
val blockHash = client.getBlockHash(blockNumber)
val block = client.getBlock(blockHash)

return toUnifiedBlock(block)
}

//todo - implement
override suspend fun getGasPrice(): BigInteger {
TODO("Not yet implemented")
}

override suspend fun getGasLimit(): BigInteger {
TODO("Not yet implemented")
}

override suspend fun getBalance(address: String): BigDecimal {
val balance = client.getAddressBalance(address)
return BigDecimal.ZERO
return balance.toBigDecimal()
}

override suspend fun getContractBalance(address: String, contractAddress: String): BigDecimal {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ class BitcoinClient(
return response.result.height
}

suspend fun broadcastRawTransaction(signature: String): Int {
val command = BitcoinCommand("send")
val response = client.post()
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(command))
.retrieve()
.awaitBody<BitcoinResponse<BlockHeightBitcoinResponse>>()
return response.result.height
}

suspend fun getBlockHash(blockHeight: Int): String {
val command = BitcoinCommand("getblockhash", listOf(blockHeight))
val response = client.post()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import io.openfuture.state.blockchain.dto.UnifiedBlock
import io.openfuture.state.blockchain.dto.UnifiedTransaction
import io.openfuture.state.domain.CurrencyCode
import io.openfuture.state.util.toLocalDateTime
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.future.await
import kotlinx.coroutines.withContext
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Component
import org.web3j.abi.FunctionEncoder
import org.web3j.abi.FunctionReturnDecoder
import org.web3j.protocol.core.DefaultBlockParameterName.LATEST
import org.web3j.abi.TypeReference
import org.web3j.abi.datatypes.Address
import org.web3j.abi.datatypes.generated.Uint256
Expand All @@ -20,6 +23,7 @@ import org.web3j.protocol.core.methods.request.Transaction
import org.web3j.protocol.core.methods.response.EthBlock
import org.web3j.protocol.core.methods.response.EthCall
import org.web3j.utils.Convert
import java.lang.Exception
import java.math.BigDecimal
import java.math.BigInteger

Expand All @@ -31,6 +35,27 @@ class EthereumBlockchain(private val web3j: Web3j) : Blockchain() {
.sendAsync().await()
.blockNumber.toInt()

override suspend fun getNonce(address: String): BigInteger = web3j.ethGetTransactionCount(address, LATEST).send().transactionCount
override suspend fun broadcastTransaction(signedTransaction: String): String {
val result = web3j.ethSendRawTransaction(signedTransaction).send()

if (result.hasError()) {
throw Exception(result.error.message)
}

while (!web3j.ethGetTransactionReceipt(result.transactionHash).send().transactionReceipt.isPresent) {
withContext(Dispatchers.IO) {
Thread.sleep(1000)
}
}

return web3j.ethGetTransactionReceipt(result.transactionHash).send().transactionReceipt.get().transactionHash
}

override suspend fun getTransactionStatus(transactionHash: String): Boolean {
TODO("Not yet implemented")
}

override suspend fun getBlock(blockNumber: Int): UnifiedBlock {
val parameter = DefaultBlockParameterNumber(blockNumber.toLong())
val block = web3j.ethGetBlockByNumber(parameter, true)
Expand All @@ -42,8 +67,7 @@ class EthereumBlockchain(private val web3j: Web3j) : Blockchain() {
}

override suspend fun getBalance(address: String): BigDecimal {
val parameter = DefaultBlockParameterName.LATEST
val balanceWei = web3j.ethGetBalance(address, parameter)
val balanceWei = web3j.ethGetBalance(address, LATEST)
.sendAsync().await()
.balance
return Convert.fromWei(balanceWei.toString(), Convert.Unit.ETHER)
Expand All @@ -59,7 +83,7 @@ class EthereumBlockchain(private val web3j: Web3j) : Blockchain() {
val encodedFunction = FunctionEncoder.encode(functionBalance)
val ethCall: EthCall = web3j.ethCall(
Transaction.createEthCallTransaction(address, contractAddress, encodedFunction),
DefaultBlockParameterName.LATEST
LATEST
).sendAsync().await()

val value = ethCall.value
Expand All @@ -68,6 +92,17 @@ class EthereumBlockchain(private val web3j: Web3j) : Blockchain() {
return Convert.fromWei(contractBalance.toString(), Convert.Unit.ETHER)
}

override suspend fun getGasPrice(): BigInteger {
return web3j.ethGasPrice().sendAsync().await().gasPrice
}

override suspend fun getGasLimit(): BigInteger {
return web3j
.ethGetBlockByNumber(LATEST, false)
.sendAsync().await()
.block.gasLimit
}

override suspend fun getCurrencyCode(): CurrencyCode {
return CurrencyCode.ETHEREUM
}
Expand Down
Loading

0 comments on commit 835e9f5

Please sign in to comment.