Using stream-fashion structures

This commit is contained in:
Enol Álvarez
2023-07-26 11:22:44 +02:00
parent 3678208dad
commit 73a819ad71
10 changed files with 132 additions and 225 deletions

View File

@@ -1,7 +1,7 @@
mod pb;
mod map_block_meta;
mod map_filter_transactions;
mod map_contract_events;
mod map_filter_transactions;
mod pb;
mod util;
substreams_ethereum::init!();

View File

@@ -1,17 +1,14 @@
use substreams_ethereum::pb::eth::v2::Block;
use crate::pb::eth::block_meta::v1::BlockMeta;
use substreams::Hex;
use substreams_ethereum::pb::eth::v2::Block;
#[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();
Ok(BlockMeta {
number: blk.number,
hash: hash_string,
parent_hash: parent_hash_string
number: blk.number,
hash: Hex::encode(&blk.hash),
parent_hash: Hex::encode(&header.parent_hash),
})
}
}

View File

@@ -1,73 +1,32 @@
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::pb::eth::event::v1::Events;
use crate::util;
use anyhow::Ok;
use substreams::errors::Error;
use substreams::Hex;
use substreams_ethereum::pb::eth::v2::Block;
use anyhow::anyhow;
#[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());
}
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 transaction in &blk.transaction_traces {
if transaction.to == contract_address_as_vec {
let transaction_events = get_transaction_events(&transaction);
events.extend(transaction_events);
}
}
verify_parameter(&contract_address)?;
let events: Vec<Event> = blk.logs()
.filter(| log| log.address().to_vec() == Hex::decode(&contract_address).expect("already validated"))
.map(|log| Event {
address: Hex::encode(log.address()),
topics: log.topics().into_iter().map(| topic| Hex::encode(topic)).collect(),
tx_hash: Hex::encode(&log.receipt.transaction.hash)
})
.collect();
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")))
fn verify_parameter(address: &String) -> Result<(), Error> {
if !util::is_address_valid(&address) {
return Err(anyhow!("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 = util::hexadecimal_to_string(&log.address);
let topics = get_log_topics(&log);
let event = create_event_from(address, topics, util::hexadecimal_to_string(&transaction.hash));
transaction_events.push(event)
}
return transaction_events;
}
fn get_log_topics(log: &Log) -> Vec<String> {
let mut topics: Vec<String> = Vec::new();
for topic in &log.topics {
let topic_string = util::hexadecimal_to_string(topic);
topics.push(topic_string)
}
return topics;
}
fn create_event_from(address: String, topics: Vec<String>, hash: String) -> Event {
return Event {
address,
topics,
tx_hash: hash
}
return Ok(());
}

View File

@@ -1,77 +1,72 @@
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;
use serde::Deserialize;
use substreams::Hex;
use substreams_ethereum::pb::eth::v2::{Block, TransactionTraceStatus, TransactionTrace};
use anyhow::anyhow;
#[derive(Deserialize)]
struct TransactionFilterParams {
hash: Option<String>,
to: Option<String>,
from: Option<String>
from: Option<String>,
}
#[substreams::handlers::map]
pub fn map_filter_transactions(params: String, blk: Block) -> Result<Transactions, Vec<substreams::errors::Error>> {
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);
verify_filter_params(&filters)?;
let transactions: Vec<Transaction> = blk.transactions()
.filter(|trans| apply_filter(&trans, &filters))
.map(|trans| Transaction {
from: Hex::encode(&trans.from),
to: Hex::encode(&trans.to),
hash: Hex::encode(&trans.hash),
}).collect();
Ok(Transactions {
transactions,
})
}
fn verify_filter_params(params: &TransactionFilterParams) -> Result<(), Vec<substreams::errors::Error>> {
let mut errors: Vec<substreams::errors::Error> = Vec::new();
if params.from.is_some() && !util::is_address_valid(&params.from.as_ref().unwrap()) {
errors.push(anyhow!("'from' address is not valid"));
}
if params.to.is_some() && !util::is_address_valid(&params.to.as_ref().unwrap()) {
errors.push(anyhow!("'to' address is not valid"));
}
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 })
return Ok(())
}
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")));
fn apply_filter(transaction: &TransactionTrace, filters: &TransactionFilterParams) -> bool {
if !filter_by_parameter(&filters.from, &transaction.from)
|| !filter_by_parameter(&filters.to, &transaction.to)
|| transaction.status != (TransactionTraceStatus::Succeeded as i32) {
return false;
}
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;
return true;
}
fn filter_by_parameter(parameter: &Option<String>, transaction_field: &String) -> bool {
fn filter_by_parameter(parameter: &Option<String>, transaction_field: &Vec<u8>) -> bool {
if parameter.is_none() {
return true;
}
if parameter.as_ref().unwrap() == transaction_field {
return true
let parameter_as_vec = &Hex::decode(parameter.as_ref().unwrap()).expect("already verified");
if parameter_as_vec == transaction_field {
return true;
}
return false;
}
}

View File

@@ -1,23 +1,8 @@
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 {
pub fn is_address_valid(address: &String) -> bool {
// An address is always 40 hexadecimal characters (or 2 more character with 0x prefix)
if address.len() != 40 && address.len() != 42 {
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;
}