Update transactions and events modules

This commit is contained in:
Enol Álvarez
2023-07-21 18:33:05 +02:00
parent 58e2534e9b
commit 3678208dad
14 changed files with 172 additions and 101 deletions

View File

@@ -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",
]

View File

@@ -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]

View File

@@ -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

View File

@@ -1,3 +0,0 @@
#!/bin/bash
cargo build --target wasm32-unknown-unknown --release

View File

@@ -2,8 +2,8 @@ syntax = "proto3";
package eth.transaction.v1;
message TransactionOption {
Transaction transaction = 1;
message Transactions {
repeated Transaction transactions = 1;
}
message Transaction {

View File

@@ -1,4 +1,4 @@
[toolchain]
channel = "1.65"
channel = "1.69"
components = [ "rustfmt" ]
targets = [ "wasm32-unknown-unknown" ]

View File

@@ -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)
}

View File

@@ -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
}
})
}

View File

@@ -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
}
}

View File

@@ -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)
}

View 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(&params).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(&params.hash.as_ref().unwrap()) {
errors.push(Error::Unexpected(String::from("Transaction hash is not valid")));
}
if params.from.is_some()
&& !util::is_address_valid(&params.from.as_ref().unwrap()) {
errors.push(Error::Unexpected(String::from("'from' address is not valid")));
}
if params.to.is_some()
&& !util::is_address_valid(&params.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;
}

View File

@@ -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)]

View 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;
}

View File

@@ -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"