diff --git a/README.md b/README.md index 61e2320f..f83f0d6b 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,7 @@ cryo datasets - erc20_metadata - erc20_supplies - erc20_transfers +- erc20_approvals - erc721_metadata - erc721_transfers - eth_calls diff --git a/crates/freeze/src/datasets/erc20_approvals.rs b/crates/freeze/src/datasets/erc20_approvals.rs new file mode 100644 index 00000000..fbc77bc4 --- /dev/null +++ b/crates/freeze/src/datasets/erc20_approvals.rs @@ -0,0 +1,111 @@ +use crate::*; +use ethers::prelude::*; +use polars::prelude::*; + +#[cryo_to_df::to_df(Datatype::Erc20Approvals)] +#[derive(Default)] +pub struct Erc20Approvals { + n_rows: u64, + block_number: Vec, + block_hash: Vec>>, + transaction_index: Vec, + log_index: Vec, + transaction_hash: Vec>, + erc20: Vec>, + from_address: Vec>, + to_address: Vec>, + value: Vec, + chain_id: Vec, +} + +#[async_trait::async_trait] +impl Dataset for Erc20Approvals { + fn default_columns() -> Option> { + Some(vec![ + "block_number", + // "block_hash", + "transaction_index", + "log_index", + "transaction_hash", + "erc20", + "from_address", + "to_address", + "value", + "chain_id", + ]) + } + + fn optional_parameters() -> Vec { + vec![Dim::Address, Dim::Topic0, Dim::Topic1, Dim::Topic2, Dim::FromAddress, Dim::ToAddress] + } + + fn use_block_ranges() -> bool { + true + } + + fn arg_aliases() -> Option> { + Some([(Dim::Contract, Dim::Address)].into_iter().collect()) + } +} + +#[async_trait::async_trait] +impl CollectByBlock for Erc20Approvals { + type Response = Vec; + + async fn extract(request: Params, source: Arc, _: Arc) -> R { + let mut topics = [Some(ValueOrArray::Value(Some(*EVENT_ERC20_TRANSFER))), None, None, None]; + if let Some(from_address) = &request.from_address { + let mut v = vec![0u8; 12]; + v.append(&mut from_address.to_owned()); + topics[1] = Some(ValueOrArray::Value(Some(H256::from_slice(&v[..])))); + } + if let Some(to_address) = &request.to_address { + let mut v = vec![0u8; 12]; + v.append(&mut to_address.to_owned()); + topics[2] = Some(ValueOrArray::Value(Some(H256::from_slice(&v[..])))); + } + let filter = Filter { topics, ..request.ethers_log_filter()? }; + let logs = source.get_logs(&filter).await?; + + Ok(logs.into_iter().filter(|x| x.topics.len() == 3 && x.data.len() == 32).collect()) + } + + fn transform(response: Self::Response, columns: &mut Self, query: &Arc) -> R<()> { + let schema = query.schemas.get_schema(&Datatype::Erc20Approvals)?; + process_erc20_approval(response, columns, schema) + } +} + +#[async_trait::async_trait] +impl CollectByTransaction for Erc20Approvals { + type Response = Vec; + + async fn extract(request: Params, source: Arc, _: Arc) -> R { + let logs = source.get_transaction_logs(request.transaction_hash()?).await?; + Ok(logs.into_iter().filter(is_erc20_approval).collect()) + } +} + +fn is_erc20_approval(log: &Log) -> bool { + log.topics.len() == 3 && log.data.len() == 32 && log.topics[0] == *EVENT_ERC20_APPROVAL +} + +fn process_erc20_approval(logs: Vec, columns: &mut Erc20Approvals, schema: &Table) -> R<()> { + for log in logs.iter() { + if let (Some(bn), Some(tx), Some(ti), Some(li)) = + (log.block_number, log.transaction_hash, log.transaction_index, log.log_index) + { + columns.n_rows += 1; + store!(schema, columns, block_number, bn.as_u32()); + store!(schema, columns, block_hash, log.block_hash.map(|bh| bh.as_bytes().to_vec())); + store!(schema, columns, transaction_index, ti.as_u32()); + store!(schema, columns, log_index, li.as_u32()); + store!(schema, columns, transaction_hash, tx.as_bytes().to_vec()); + store!(schema, columns, erc20, log.address.as_bytes().to_vec()); + store!(schema, columns, from_address, log.topics[1].as_bytes()[12..].to_vec()); + store!(schema, columns, to_address, log.topics[2].as_bytes()[12..].to_vec()); + store!(schema, columns, value, log.data.to_vec().as_slice().into()); + } + } + Ok(()) +} diff --git a/crates/freeze/src/datasets/mod.rs b/crates/freeze/src/datasets/mod.rs index 4c168aea..ad61e34b 100644 --- a/crates/freeze/src/datasets/mod.rs +++ b/crates/freeze/src/datasets/mod.rs @@ -24,6 +24,8 @@ pub mod erc20_metadata; pub mod erc20_supplies; /// erc20 transfers pub mod erc20_transfers; +// erc20 approval +pub mod erc20_approvals; /// erc721 metadata pub mod erc721_metadata; /// erc721 transfers @@ -84,6 +86,7 @@ pub use erc20_balances::*; pub use erc20_metadata::*; pub use erc20_supplies::*; pub use erc20_transfers::*; +pub use erc20_approvals::*; pub use erc721_metadata::*; pub use erc721_transfers::*; pub use eth_calls::*; diff --git a/crates/freeze/src/types/datatypes/scalar.rs b/crates/freeze/src/types/datatypes/scalar.rs index c4078c0a..6f86bd8e 100644 --- a/crates/freeze/src/types/datatypes/scalar.rs +++ b/crates/freeze/src/types/datatypes/scalar.rs @@ -16,6 +16,7 @@ define_datatypes!( Erc20Metadata, Erc20Supplies, Erc20Transfers, + Erc20Approvals, Erc721Metadata, Erc721Transfers, EthCalls, diff --git a/crates/freeze/src/types/signatures.rs b/crates/freeze/src/types/signatures.rs index d5f9dfba..6e9bd3fe 100644 --- a/crates/freeze/src/types/signatures.rs +++ b/crates/freeze/src/types/signatures.rs @@ -23,6 +23,11 @@ lazy_static::lazy_static! { .expect("Decoding failed"), ); + pub static ref EVENT_ERC20_APPROVAL: H256 = H256( + prefix_hex::decode("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925") + .expect("Decoding failed"), + ); + /// event hash of EVENT_ERC721_TRANSFER pub static ref EVENT_ERC721_TRANSFER: H256 = H256( prefix_hex::decode("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")