Using stream-fashion structures
This commit is contained in:
@@ -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!();
|
||||
|
||||
@@ -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),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(());
|
||||
}
|
||||
@@ -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(¶ms).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(¶ms.from.as_ref().unwrap()) {
|
||||
errors.push(anyhow!("'from' address is not valid"));
|
||||
}
|
||||
|
||||
if params.to.is_some() && !util::is_address_valid(¶ms.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(¶ms.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(¶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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user