Update transactions and events modules
This commit is contained in:
19
ethereum-explorer/Cargo.lock
generated
19
ethereum-explorer/Cargo.lock
generated
@@ -508,6 +508,12 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.3"
|
||||
@@ -770,6 +776,17 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_qs"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.10.8"
|
||||
@@ -879,6 +896,8 @@ dependencies = [
|
||||
"hex-literal",
|
||||
"num-bigint",
|
||||
"prost",
|
||||
"serde",
|
||||
"serde_qs",
|
||||
"substreams",
|
||||
"substreams-ethereum",
|
||||
]
|
||||
|
||||
@@ -19,6 +19,8 @@ prost = "0.11"
|
||||
substreams = "0.5"
|
||||
# Use latest from https://crates.io/crates/substreams-ethereum
|
||||
substreams-ethereum = "0.9"
|
||||
serde_qs = "0.12.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
# Required so that ethabi > ethereum-types build correctly under wasm32-unknown-unknown
|
||||
[target.wasm32-unknown-unknown.dependencies]
|
||||
|
||||
@@ -6,6 +6,6 @@ build:
|
||||
protogen:
|
||||
substreams protogen ./substreams.yaml --exclude-paths="sf/substreams,google"
|
||||
|
||||
.PHONY: stream
|
||||
.PHONY: package
|
||||
package: build
|
||||
substreams package substreams.yaml
|
||||
substreams pack substreams.yaml
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
cargo build --target wasm32-unknown-unknown --release
|
||||
@@ -2,8 +2,8 @@ syntax = "proto3";
|
||||
|
||||
package eth.transaction.v1;
|
||||
|
||||
message TransactionOption {
|
||||
Transaction transaction = 1;
|
||||
message Transactions {
|
||||
repeated Transaction transactions = 1;
|
||||
}
|
||||
|
||||
message Transaction {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[toolchain]
|
||||
channel = "1.65"
|
||||
channel = "1.69"
|
||||
components = [ "rustfmt" ]
|
||||
targets = [ "wasm32-unknown-unknown" ]
|
||||
@@ -1,40 +1,7 @@
|
||||
mod pb;
|
||||
|
||||
#[path = "map_block_meta.rs"]
|
||||
mod block_meta;
|
||||
|
||||
#[path = "map_filter_transaction.rs"]
|
||||
mod filter_transaction;
|
||||
|
||||
#[path = "map_contract_events.rs"]
|
||||
mod contract_events;
|
||||
|
||||
|
||||
use pb::eth::transaction::v1::TransactionOption;
|
||||
use substreams_ethereum::pb::eth::v2::Block;
|
||||
use pb::eth::block_meta::v1::BlockMeta;
|
||||
use crate::pb::eth::event::v1::Events;
|
||||
mod map_block_meta;
|
||||
mod map_filter_transactions;
|
||||
mod map_contract_events;
|
||||
mod util;
|
||||
|
||||
substreams_ethereum::init!();
|
||||
|
||||
#[substreams::handlers::map]
|
||||
fn map_block_meta(blk: Block) -> Result<BlockMeta, substreams::errors::Error> {
|
||||
let block_meta = block_meta::map_block_meta(&blk);
|
||||
|
||||
Ok(block_meta)
|
||||
}
|
||||
|
||||
#[substreams::handlers::map]
|
||||
pub fn map_filter_transaction(transaction_hash: String, blk: Block) -> Result<TransactionOption, substreams::errors::Error> {
|
||||
let filtered_transaction = filter_transaction::filter_by_transaction_hash(transaction_hash, &blk);
|
||||
|
||||
Ok(filtered_transaction)
|
||||
}
|
||||
|
||||
#[substreams::handlers::map]
|
||||
fn map_contract_events(contract_address: String, blk: Block) -> Result<Events, substreams::errors::Error> {
|
||||
let events: Events = contract_events::map_contract_events(contract_address, &blk);
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,16 @@ use substreams_ethereum::pb::eth::v2::Block;
|
||||
use crate::pb::eth::block_meta::v1::BlockMeta;
|
||||
use substreams::Hex;
|
||||
|
||||
pub fn map_block_meta (blk: &Block) -> BlockMeta {
|
||||
#[substreams::handlers::map]
|
||||
fn map_block_meta(blk: Block) -> Result<BlockMeta, substreams::errors::Error> {
|
||||
let header = blk.header.as_ref().unwrap();
|
||||
|
||||
let hash_string = Hex(&blk.hash).to_string();
|
||||
let parent_hash_string = Hex(&header.parent_hash).to_string();
|
||||
|
||||
return BlockMeta {
|
||||
Ok(BlockMeta {
|
||||
number: blk.number,
|
||||
hash: hash_string,
|
||||
parent_hash: parent_hash_string
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,34 +1,52 @@
|
||||
mod pb;
|
||||
use crate::pb::eth::event::v1::Events;
|
||||
use crate::pb::eth::event::v1::Event;
|
||||
use substreams_ethereum::pb::eth::v2::Block;
|
||||
use substreams::Hex;
|
||||
use substreams_ethereum::pb::eth::v2::Log;
|
||||
use substreams_ethereum::pb::eth::v2::TransactionTrace;
|
||||
use substreams::errors::Error;
|
||||
use crate::util;
|
||||
|
||||
#[substreams::handlers::map]
|
||||
fn map_contract_events(contract_address: String, blk: Block) -> Result<Events, Error> {
|
||||
let error = verify_parameter(&contract_address);
|
||||
if error.is_some() {
|
||||
return Err(error.unwrap());
|
||||
}
|
||||
|
||||
pub fn map_contract_events(contract_address: String, blk: &Block) -> Events {
|
||||
let mut events: Vec<Event> = Vec::new();
|
||||
let contract_address_as_vec = match Hex::decode(&contract_address) {
|
||||
Ok(address) => address,
|
||||
Err(error) => return Err(Error::Unexpected(error.to_string())),
|
||||
};
|
||||
|
||||
for tr in &blk.transaction_traces {
|
||||
let to = Hex(&tr.to).to_string();
|
||||
|
||||
if to == contract_address {
|
||||
let transaction_events = &mut get_transaction_events(&tr);
|
||||
events.append(transaction_events);
|
||||
for transaction in &blk.transaction_traces {
|
||||
if transaction.to == contract_address_as_vec {
|
||||
let transaction_events = get_transaction_events(&transaction);
|
||||
events.extend(transaction_events);
|
||||
}
|
||||
}
|
||||
|
||||
return Events { events }
|
||||
Ok(Events { events })
|
||||
}
|
||||
|
||||
fn verify_parameter(contract_address: &String) -> Option<Error> {
|
||||
if !util::is_address_valid(contract_address) {
|
||||
return Some(Error::Unexpected(String::from("Contract address is not valid")))
|
||||
}
|
||||
|
||||
return None
|
||||
}
|
||||
|
||||
fn get_transaction_events(transaction: &TransactionTrace) -> Vec<Event> {
|
||||
let mut transaction_events: Vec<Event> = Vec::new();
|
||||
|
||||
for log in &transaction.receipt().receipt.logs {
|
||||
let address = Hex(&log.address).to_string();
|
||||
let address = util::hexadecimal_to_string(&log.address);
|
||||
let topics = get_log_topics(&log);
|
||||
|
||||
transaction_events.push(create_event_from(address, topics, &transaction.hash))
|
||||
let event = create_event_from(address, topics, util::hexadecimal_to_string(&transaction.hash));
|
||||
transaction_events.push(event)
|
||||
}
|
||||
|
||||
return transaction_events;
|
||||
@@ -38,19 +56,18 @@ fn get_log_topics(log: &Log) -> Vec<String> {
|
||||
let mut topics: Vec<String> = Vec::new();
|
||||
|
||||
for topic in &log.topics {
|
||||
let topic_string = Hex(topic).to_string();
|
||||
let topic_string = util::hexadecimal_to_string(topic);
|
||||
topics.push(topic_string)
|
||||
}
|
||||
|
||||
return topics;
|
||||
}
|
||||
|
||||
fn create_event_from(address: String, topics: Vec<String>, hash: &Vec<u8>) -> Event {
|
||||
let hash_as_string = Hex(hash).to_string();
|
||||
fn create_event_from(address: String, topics: Vec<String>, hash: String) -> Event {
|
||||
|
||||
return Event {
|
||||
address,
|
||||
topics,
|
||||
tx_hash: hash_as_string
|
||||
tx_hash: hash
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
mod pb;
|
||||
use substreams_ethereum::pb::eth::v2::Block;
|
||||
use substreams::Hex;
|
||||
use crate::pb::eth::transaction::v1::TransactionOption;
|
||||
use crate::pb::eth::transaction::v1::Transaction;
|
||||
|
||||
|
||||
pub fn filter_by_transaction_hash(transaction_hash: String, blk: &Block) -> TransactionOption {
|
||||
let transaction_traces = &blk.transaction_traces;
|
||||
|
||||
for transfer in transaction_traces {
|
||||
//let transferValue = transfer;
|
||||
let hash = &transfer.hash;
|
||||
let from = &transfer.from;
|
||||
let to = &transfer.to;
|
||||
|
||||
if Hex(hash).to_string() == transaction_hash {
|
||||
let trans = Transaction { from: Hex(from).to_string(), to: Hex(to).to_string(), hash: Hex(hash).to_string() };
|
||||
return transfer_option_of(Some(trans));
|
||||
}
|
||||
}
|
||||
|
||||
return empty_transfer_option()
|
||||
}
|
||||
|
||||
fn transfer_option_of (transaction: Option<Transaction>) -> TransactionOption {
|
||||
return TransactionOption { transaction }
|
||||
}
|
||||
|
||||
fn empty_transfer_option() -> TransactionOption {
|
||||
return transfer_option_of(None)
|
||||
}
|
||||
77
ethereum-explorer/src/map_filter_transactions.rs
Normal file
77
ethereum-explorer/src/map_filter_transactions.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use substreams_ethereum::pb::eth::v2::{Block, TransactionTraceStatus};
|
||||
use crate::pb::eth::transaction::v1::{Transaction, Transactions};
|
||||
use serde::Deserialize;
|
||||
use substreams::errors::Error;
|
||||
use crate::util;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct TransactionFilterParams {
|
||||
hash: Option<String>,
|
||||
to: Option<String>,
|
||||
from: Option<String>
|
||||
}
|
||||
|
||||
#[substreams::handlers::map]
|
||||
pub fn map_filter_transactions(params: String, blk: Block) -> Result<Transactions, Vec<substreams::errors::Error>> {
|
||||
let filters: TransactionFilterParams = serde_qs::from_str(¶ms).unwrap();
|
||||
let errors = verify_filter_params(&filters);
|
||||
if errors.len() > 0 {
|
||||
return Err(errors)
|
||||
}
|
||||
|
||||
let mut filtered_transactions: Vec<Transaction> = Vec::new();
|
||||
|
||||
for transaction in &blk.transaction_traces {
|
||||
let tx_hash = util::hexadecimal_to_string(&transaction.hash);
|
||||
let tx_from = util::hexadecimal_to_string(&transaction.from);
|
||||
let tx_to = util::hexadecimal_to_string(&transaction.to);
|
||||
let mut current_transaction_filtered = true;
|
||||
|
||||
if !filter_by_parameter(&filters.hash, &tx_hash) ||
|
||||
!filter_by_parameter(&filters.from, &tx_from) ||
|
||||
!filter_by_parameter(&filters.to, &tx_to) ||
|
||||
transaction.status != (TransactionTraceStatus::Succeeded as i32) {
|
||||
current_transaction_filtered = false
|
||||
}
|
||||
|
||||
if current_transaction_filtered {
|
||||
let trans = Transaction { from: tx_from, to: tx_to, hash: tx_hash };
|
||||
filtered_transactions.push(trans)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Transactions { transactions: filtered_transactions })
|
||||
}
|
||||
|
||||
fn verify_filter_params(params: &TransactionFilterParams) -> Vec<substreams::errors::Error> {
|
||||
let mut errors: Vec<substreams::errors::Error> = Vec::new();
|
||||
|
||||
if params.hash.is_some()
|
||||
&& !util::is_transaction_hash_valid(¶ms.hash.as_ref().unwrap()) {
|
||||
errors.push(Error::Unexpected(String::from("Transaction hash is not valid")));
|
||||
}
|
||||
|
||||
if params.from.is_some()
|
||||
&& !util::is_address_valid(¶ms.from.as_ref().unwrap()) {
|
||||
errors.push(Error::Unexpected(String::from("'from' address is not valid")));
|
||||
}
|
||||
|
||||
if params.to.is_some()
|
||||
&& !util::is_address_valid(¶ms.to.as_ref().unwrap()) {
|
||||
errors.push(Error::Unexpected(String::from("'to' address is not valid")));
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
fn filter_by_parameter(parameter: &Option<String>, transaction_field: &String) -> bool {
|
||||
if parameter.is_none() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if parameter.as_ref().unwrap() == transaction_field {
|
||||
return true
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
// @generated
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct TransactionOption {
|
||||
#[prost(message, optional, tag="1")]
|
||||
pub transaction: ::core::option::Option<Transaction>,
|
||||
pub struct Transactions {
|
||||
#[prost(message, repeated, tag="1")]
|
||||
pub transactions: ::prost::alloc::vec::Vec<Transaction>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
|
||||
23
ethereum-explorer/src/util.rs
Normal file
23
ethereum-explorer/src/util.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use substreams::Hex;
|
||||
|
||||
pub fn hexadecimal_to_string(hex: &Vec<u8>) -> String {
|
||||
return Hex::encode(hex);
|
||||
}
|
||||
|
||||
pub fn is_transaction_hash_valid(hash: &String) -> bool {
|
||||
// A transaction hash is always 64 hexadecimal characters
|
||||
if hash.len() != 64 {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn is_address_valid(address: &String) -> bool {
|
||||
// An address is always 40 hexadecimal characters
|
||||
if address.len() != 40 {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -23,13 +23,13 @@ modules:
|
||||
- source: sf.ethereum.type.v2.Block
|
||||
output:
|
||||
type: proto:eth.block_meta.v1.BlockMeta
|
||||
- name: map_filter_transaction
|
||||
- name: map_filter_transactions
|
||||
kind: map
|
||||
inputs:
|
||||
- params: string
|
||||
- source: sf.ethereum.type.v2.Block
|
||||
output:
|
||||
type: proto:eth.transaction.v1.TransactionOption
|
||||
type: proto:eth.transaction.v1.Transactions
|
||||
- name: map_contract_events
|
||||
kind: map
|
||||
inputs:
|
||||
@@ -39,5 +39,5 @@ modules:
|
||||
type: proto:eth.event.v1.Events
|
||||
|
||||
params:
|
||||
map_filter_transaction: "4faa877df84080a9d98b1e28294c4680bb141ec27a1a5dee009c3e02dfa65ab7"
|
||||
map_filter_transactions: "to=dac17f958d2ee523a2206206994597c13d831ec7&from=46340b20830761efd32832a74d7169b29feb9758"
|
||||
map_contract_events: "bc4ca0eda7647a8ab7c2061c2e118a18a936f13d"
|
||||
|
||||
Reference in New Issue
Block a user