Skip to content

Commit

Permalink
Add method to return FullTransaction constructed from RPC node entities
Browse files Browse the repository at this point in the history
  • Loading branch information
esen committed Apr 14, 2022
1 parent dfb66ec commit ac4b615
Show file tree
Hide file tree
Showing 20 changed files with 90 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class AddressWatchViewModel : ViewModel() {
val erc20Adapter = Erc20BaseAdapter(App.instance, Configuration.erc20Tokens.first(), evmKit)

Erc20Kit.addTransactionSyncer(evmKit)
Erc20Kit.addDecorator(evmKit)
Erc20Kit.addDecorators(evmKit)

evmAdapter.lastBlockHeightFlowable.subscribe {
lastBlockHeight.postValue(evmKit.lastBlockHeight)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ class MainViewModel : ViewModel() {
uniswapKit = UniswapKit.getInstance(ethereumKit)

Erc20Kit.addTransactionSyncer(ethereumKit)
Erc20Kit.addDecorator(ethereumKit)
UniswapKit.addDecorator(ethereumKit)
OneInchKit.addDecorator(ethereumKit)
Erc20Kit.addDecorators(ethereumKit)
UniswapKit.addDecorators(ethereumKit)
OneInchKit.addDecorators(ethereumKit)

updateBalance()
updateErc20Balance()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class Eip20EventDecorator(
val map: MutableMap<String, List<ContractEventInstance>> = mutableMapOf()

for (event in erc20Events) {
val eventInstance = TransferEventInstance(event.contractAddress, event.from, event.to, event.value, event.tokenName, event.tokenSymbol, event.tokenDecimal)
val tokenInfo = TransferEventInstance.TokenInfo(event.tokenName, event.tokenSymbol, event.tokenDecimal)
val eventInstance = TransferEventInstance(event.contractAddress, event.from, event.to, event.value, tokenInfo)

map[event.hashString] = (map[event.hashString] ?: listOf()) + listOf(eventInstance)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class Erc20Kit(
ethereumKit.addTransactionSyncer(Erc20TransactionSyncer(ethereumKit.transactionProvider, ethereumKit.eip20Storage))
}

fun addDecorator(ethereumKit: EthereumKit) {
fun addDecorators(ethereumKit: EthereumKit) {
ethereumKit.addMethodDecorator(Eip20MethodDecorator(Eip20ContractMethodFactories))
ethereumKit.addEventDecorator(Eip20EventDecorator(ethereumKit.receiveAddress, ethereumKit.eip20Storage))
ethereumKit.addTransactionDecorator(Eip20TransactionDecorator(ethereumKit.receiveAddress))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fun TransactionLog.getErc20EventInstance(): ContractEventInstance? {

when {
signature.contentEquals(TransferEventInstance.signature) ->
TransferEventInstance(address, firstParam, secondParam, BigInteger(data.toRawHexString(), 16), "", "", 18) // TODO: Here we should have token details from smart contract read
TransferEventInstance(address, firstParam, secondParam, BigInteger(data.toRawHexString(), 16), null)
signature.contentEquals(ApproveEip20Decoration.signature) ->
ApproveEventInstance(address, firstParam, secondParam, BigInteger(data.toRawHexString(), 16))
else ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import java.math.BigInteger

class TransferEventInstance(
contractAddress: Address, val from: Address, val to: Address, val value: BigInteger,
val tokenName: String, val tokenSymbol: String, val tokenDecimal: Int
val tokenInfo: TokenInfo?
) : ContractEventInstance(contractAddress) {

class TokenInfo(val tokenName: String, val tokenSymbol: String, val tokenDecimal: Int)

override fun tags(userAddress: Address): List<String> {
val tags = mutableListOf(contractAddress.hex, TransactionTag.EIP20_TRANSFER)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import io.reactivex.Single
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import java.math.BigInteger
import java.util.*

class RpcBlockchain(
private val address: Address,
Expand Down Expand Up @@ -123,15 +122,15 @@ class RpcBlockchain(
return syncer.single(EstimateGasJsonRpc(address, to, amount, gasLimit, gasPrice, data))
}

override fun getTransactionReceipt(transactionHash: ByteArray): Single<Optional<RpcTransactionReceipt>> {
override fun getTransactionReceipt(transactionHash: ByteArray): Single<RpcTransactionReceipt> {
return syncer.single(GetTransactionReceiptJsonRpc(transactionHash))
}

override fun getTransaction(transactionHash: ByteArray): Single<Optional<RpcTransaction>> {
override fun getTransaction(transactionHash: ByteArray): Single<RpcTransaction> {
return syncer.single(GetTransactionByHashJsonRpc(transactionHash))
}

override fun getBlock(blockNumber: Long): Single<Optional<RpcBlock>> {
override fun getBlock(blockNumber: Long): Single<RpcBlock> {
return syncer.single(GetBlockByNumberJsonRpc(blockNumber))
}

Expand All @@ -156,7 +155,7 @@ class RpcBlockchain(
logsByBlockNumber[log.blockNumber] = logs
}

val requestSingles: MutableList<Single<Optional<RpcBlock>>> = mutableListOf()
val requestSingles: MutableList<Single<RpcBlock>> = mutableListOf()

for ((blockNumber, _) in logsByBlockNumber) {
requestSingles.add(syncer.single(GetBlockByNumberJsonRpc(blockNumber)))
Expand All @@ -165,8 +164,7 @@ class RpcBlockchain(
return Single.merge(requestSingles).toList().map { blocks ->
val resultLogs: MutableList<TransactionLog> = mutableListOf()

for (blockOptional in blocks) {
val block = blockOptional.orElse(null) ?: continue
for (block in blocks) {
val logsOfBlock = logsByBlockNumber[block.number] ?: continue

for (log in logsOfBlock) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ package io.horizontalsystems.ethereumkit.api.jsonrpc
import com.google.gson.reflect.TypeToken
import io.horizontalsystems.ethereumkit.api.jsonrpc.models.RpcBlock
import java.lang.reflect.Type
import java.util.*

class GetBlockByNumberJsonRpc(
@Transient val blockNumber: Long
) : JsonRpc<Optional<RpcBlock>>(
) : JsonRpc<RpcBlock>(
method = "eth_getBlockByNumber",
params = listOf(blockNumber, false)
) {
@Transient
override val typeOfResult: Type = object : TypeToken<Optional<RpcBlock>>() {}.type
override val typeOfResult: Type = object : TypeToken<RpcBlock>() {}.type
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ package io.horizontalsystems.ethereumkit.api.jsonrpc
import com.google.gson.reflect.TypeToken
import io.horizontalsystems.ethereumkit.api.jsonrpc.models.RpcTransaction
import java.lang.reflect.Type
import java.util.*

class GetTransactionByHashJsonRpc(
@Transient val transactionHash: ByteArray
) : JsonRpc<Optional<RpcTransaction>>(
) : JsonRpc<RpcTransaction>(
method = "eth_getTransactionByHash",
params = listOf(transactionHash)
) {
@Transient
override val typeOfResult: Type = object : TypeToken<Optional<RpcTransaction>>() {}.type
override val typeOfResult: Type = object : TypeToken<RpcTransaction>() {}.type
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package io.horizontalsystems.ethereumkit.api.jsonrpc

import com.google.gson.reflect.TypeToken
import io.horizontalsystems.ethereumkit.api.jsonrpc.models.RpcTransaction
import io.horizontalsystems.ethereumkit.api.jsonrpc.models.RpcTransactionReceipt
import java.lang.reflect.Type
import java.util.*

class GetTransactionReceiptJsonRpc(
@Transient val transactionHash: ByteArray
) : JsonRpc<Optional<RpcTransactionReceipt>>(
) : JsonRpc<RpcTransactionReceipt>(
method = "eth_getTransactionReceipt",
params = listOf(transactionHash)
) {
@Transient
override val typeOfResult: Type = object : TypeToken<Optional<RpcTransactionReceipt>>() {}.type
override val typeOfResult: Type = object : TypeToken<RpcTransactionReceipt>() {}.type
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ class EthereumKit(
return transactionManager.getFullTransactions(hashes)
}

fun getFullTransactionSingle(hash: ByteArray): Single<FullTransaction> {
return transactionManager.getFullTransactionSingle(hash)
}

fun estimateGas(to: Address?, value: BigInteger, gasPrice: GasPrice): Single<Long> {
// without address - provide default gas limit
if (to == null) {
Expand Down Expand Up @@ -409,7 +413,7 @@ class EthereumKit(
val internalTransactionsSyncer = InternalTransactionSyncer(transactionProvider, transactionStorage)

val decorationManager = DecorationManager(address, transactionStorage)
val transactionManager = TransactionManager(transactionStorage, decorationManager)
val transactionManager = TransactionManager(transactionStorage, decorationManager, blockchain, transactionProvider)
val transactionSyncManager = TransactionSyncManager(transactionManager)

transactionSyncManager.add(internalTransactionsSyncer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import io.horizontalsystems.ethereumkit.spv.models.AccountStateSpv
import io.horizontalsystems.ethereumkit.spv.models.BlockHeader
import io.reactivex.Single
import java.math.BigInteger
import java.util.*


interface IApiStorage {
Expand Down Expand Up @@ -49,9 +48,9 @@ interface IBlockchain {
fun send(rawTransaction: RawTransaction, signature: Signature): Single<Transaction>
fun getNonce(): Single<Long>
fun estimateGas(to: Address?, amount: BigInteger?, gasLimit: Long?, gasPrice: GasPrice, data: ByteArray?): Single<Long>
fun getTransactionReceipt(transactionHash: ByteArray): Single<Optional<RpcTransactionReceipt>>
fun getTransaction(transactionHash: ByteArray): Single<Optional<RpcTransaction>>
fun getBlock(blockNumber: Long): Single<Optional<RpcBlock>>
fun getTransactionReceipt(transactionHash: ByteArray): Single<RpcTransactionReceipt>
fun getTransaction(transactionHash: ByteArray): Single<RpcTransaction>
fun getBlock(blockNumber: Long): Single<RpcBlock>

fun getLogs(address: Address?, topics: List<ByteArray?>, fromBlock: Long, toBlock: Long, pullTimestamps: Boolean): Single<List<TransactionLog>>
fun getStorageAt(contractAddress: Address, position: ByteArray, defaultBlockParameter: DefaultBlockParameter): Single<ByteArray>
Expand All @@ -69,6 +68,7 @@ interface IBlockchainListener {
interface ITransactionStorage {
fun getLastTransaction(): Transaction?
fun getTransactions(hashes: List<ByteArray>): List<Transaction>
fun getTransaction(hash: ByteArray): Transaction?
fun getTransactionsBeforeAsync(tags: List<List<String>>, hash: ByteArray?, limit: Int?): Single<List<Transaction>>
fun save(transactions: List<Transaction>)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import java.util.logging.Logger
class TransactionManager(
private val storage: ITransactionStorage,
private val decorationManager: DecorationManager,
private val blockchain: IBlockchain,
private val provider: ITransactionProvider
) {
val lastTransaction: Transaction?
get() = storage.getLastTransaction()
Expand Down Expand Up @@ -77,6 +79,25 @@ class TransactionManager(
return TransactionData(address, value, byteArrayOf())
}

fun getFullTransactionSingle(hash: ByteArray): Single<FullTransaction> {
val fullRpcTransactionSingle = blockchain.getTransaction(hash)
.flatMap { transaction ->
if (transaction.blockNumber != null) {
return@flatMap Single.zip(
blockchain.getTransactionReceipt(hash),
blockchain.getBlock(transaction.blockNumber),
provider.getInternalTransactionsAsync(hash)
) { receipt, block, internalTransactions ->
FullRpcTransaction(transaction, receipt, block, internalTransactions.map { it.internalTransaction() }.toMutableList())
}
} else {
return@flatMap Single.just(FullRpcTransaction(transaction, null, null, mutableListOf()))
}
}

return fullRpcTransactionSingle.map { decorationManager.decorateFullRpcTransaction(it) }
}

private fun failPendingTransactions(): List<Transaction> {
val pendingTransactions = storage.getPendingTransactions()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ class TransactionStorage(database: TransactionDatabase) : ITransactionStorage {
override fun getTransactions(hashes: List<ByteArray>): List<Transaction> =
transactionDao.getTransactions(hashes)

override fun getTransaction(hash: ByteArray): Transaction? =
transactionDao.getTransaction(hash)

override fun getTransactionsBeforeAsync(tags: List<List<String>>, hash: ByteArray?, limit: Int?): Single<List<Transaction>> {
val whereConditions = mutableListOf<String>()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,23 @@ class DecorationManager(private val userAddress: Address, private val storage: I
}
}

fun decorateRpcTransactions(fullRpcTransaction: FullRpcTransaction): FullTransaction {
val transaction = fullRpcTransaction.transaction
fun decorateFullRpcTransaction(fullRpcTransaction: FullRpcTransaction): FullTransaction {
val timestamp = if (fullRpcTransaction.rpcBlock != null) {
fullRpcTransaction.rpcBlock.timestamp
} else {
val transaction = storage.getTransaction(fullRpcTransaction.rpcTransaction.hash)
transaction?.timestamp ?: throw Exception("Transaction not in DB")
}

val transaction = fullRpcTransaction.transaction(timestamp)

val decoration = decoration(
transaction.from,
transaction.to,
transaction.value,
contractMethod(transaction.input),
fullRpcTransaction.internalTransactions,
eventInstances(fullRpcTransaction.rpcReceipt.logs)
fullRpcTransaction.rpcReceipt?.logs?.let { eventInstances(it) } ?: listOf()
)

return FullTransaction(transaction, decoration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,25 @@ import io.horizontalsystems.ethereumkit.api.jsonrpc.models.RpcTransactionReceipt

data class FullRpcTransaction(
val rpcTransaction: RpcTransaction,
val rpcReceipt: RpcTransactionReceipt,
var internalTransactions: MutableList<InternalTransaction> = mutableListOf(),
val rpcBlock: RpcBlock
val rpcReceipt: RpcTransactionReceipt?,
val rpcBlock: RpcBlock?,
var internalTransactions: MutableList<InternalTransaction> = mutableListOf()
) {

val isFailed: Boolean =
if (rpcReceipt.status == null) rpcTransaction.gasLimit == rpcReceipt.cumulativeGasUsed else rpcReceipt.status == 0
when {
rpcReceipt == null -> false
rpcReceipt.status == null -> rpcTransaction.gasLimit == rpcReceipt.cumulativeGasUsed
else -> rpcReceipt.status == 0
}

val transaction: Transaction =
fun transaction(timestamp: Long) =
Transaction(
rpcTransaction.hash,
rpcBlock.timestamp,
timestamp,
isFailed,
rpcBlock.number,
rpcReceipt.transactionIndex,
rpcBlock?.number,
rpcReceipt?.transactionIndex,
rpcTransaction.from,
rpcTransaction.to,
rpcTransaction.value,
Expand All @@ -30,7 +34,7 @@ data class FullRpcTransaction(
rpcTransaction.maxFeePerGas,
rpcTransaction.maxPriorityFeePerGas,
rpcTransaction.gasLimit,
rpcReceipt.gasUsed
)
rpcReceipt?.gasUsed
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,8 @@ data class ProviderInternalTransaction(
val to: Address,
val value: BigInteger,
val traceId: String
)
) {

fun internalTransaction() = InternalTransaction(hash, from, to, value)

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import io.horizontalsystems.ethereumkit.spv.net.tasks.HandshakeTask
import io.reactivex.Single
import io.reactivex.subjects.PublishSubject
import java.math.BigInteger
import java.util.*
import java.util.logging.Logger

class SpvBlockchain(
Expand Down Expand Up @@ -99,15 +98,15 @@ class SpvBlockchain(
TODO("not implemented")
}

override fun getTransactionReceipt(transactionHash: ByteArray): Single<Optional<RpcTransactionReceipt>> {
override fun getTransactionReceipt(transactionHash: ByteArray): Single<RpcTransactionReceipt> {
TODO("not implemented")
}

override fun getTransaction(transactionHash: ByteArray): Single<Optional<RpcTransaction>> {
override fun getTransaction(transactionHash: ByteArray): Single<RpcTransaction> {
TODO("not implemented")
}

override fun getBlock(blockNumber: Long): Single<Optional<RpcBlock>> {
override fun getBlock(blockNumber: Long): Single<RpcBlock> {
TODO("not implemented")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class OneInchKit(
return OneInchKit(evmKit, service)
}

fun addDecorator(evmKit: EthereumKit) {
fun addDecorators(evmKit: EthereumKit) {
evmKit.addMethodDecorator(OneInchMethodDecorator(OneInchContractMethodFactories))
evmKit.addTransactionDecorator(OneInchTransactionDecorator(evmKit.receiveAddress))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class UniswapKit(
return UniswapKit(tradeManager, pairSelector, tokenFactory)
}

fun addDecorator(ethereumKit: EthereumKit) {
fun addDecorators(ethereumKit: EthereumKit) {
ethereumKit.addMethodDecorator(SwapMethodDecorator(SwapContractMethodFactories))
ethereumKit.addTransactionDecorator(SwapTransactionDecorator())
}
Expand Down

0 comments on commit ac4b615

Please sign in to comment.