From 73d48236ba698c984701bc24798d6bbfbe7c4237 Mon Sep 17 00:00:00 2001 From: zizou <111426680+flopell@users.noreply.github.com> Date: Fri, 11 Oct 2024 12:57:34 +0200 Subject: [PATCH 1/8] feat(substreams): add substreams for Uniswap v2 and v3 --- substreams/Cargo.lock | 96 + substreams/Cargo.toml | 12 +- .../crates/substreams-helper/.gitignore | 1 + .../crates/substreams-helper/Cargo.toml | 29 + substreams/crates/substreams-helper/Makefile | 7 + .../substreams-helper/rust-toolchain.toml | 4 + .../crates/substreams-helper/src/common.rs | 69 + .../substreams-helper/src/event_handler.rs | 103 + .../crates/substreams-helper/src/hex.rs | 20 + .../crates/substreams-helper/src/lib.rs | 5 + .../substreams-helper/src/storage_change.rs | 14 + substreams/ethereum-uniswap-v2/Cargo.toml | 28 + substreams/ethereum-uniswap-v2/Makefile | 2 + .../ethereum-uniswap-v2/abi/Factory.json | 125 + substreams/ethereum-uniswap-v2/abi/Pool.json | 713 +++ .../arbitrum-uniswap-v2.yaml | 49 + substreams/ethereum-uniswap-v2/build.rs | 12 + .../ethereum-pancakeswap.yaml | 49 + .../ethereum-sushiswap.yaml | 49 + .../ethereum-uniswap-v2.yaml | 49 + .../proto/v1/uniswap.proto | 61 + .../ethereum-uniswap-v2/src/abi/factory.rs | 904 +++ substreams/ethereum-uniswap-v2/src/abi/mod.rs | 4 + .../ethereum-uniswap-v2/src/abi/pool.rs | 3554 ++++++++++++ substreams/ethereum-uniswap-v2/src/lib.rs | 10 + .../src/modules/1_map_pool_created.rs | 99 + .../src/modules/2_store_pools.rs | 25 + .../src/modules/3_map_pool_events.rs | 232 + .../ethereum-uniswap-v2/src/modules/mod.rs | 11 + substreams/ethereum-uniswap-v2/src/pb/mod.rs | 17 + .../src/pb/tycho.evm.uniswap.v2.rs | 99 + .../src/pb/tycho.evm.v1.rs | 307 + .../ethereum-uniswap-v2/src/store_key.rs | 16 + substreams/ethereum-uniswap-v2/src/traits.rs | 51 + substreams/ethereum-uniswap-v3/Cargo.toml | 28 + substreams/ethereum-uniswap-v3/Makefile | 2 + .../ethereum-uniswap-v3/abi/Factory.json | 198 + substreams/ethereum-uniswap-v3/abi/Pool.json | 988 ++++ .../arbitrum-uniswap-v3.yaml | 67 + substreams/ethereum-uniswap-v3/build.rs | 12 + .../ethereum-uniswap-v3.yaml | 67 + .../proto/v1/uniswap.proto | 29 + .../ethereum-uniswap-v3/src/abi/factory.rs | 1040 ++++ substreams/ethereum-uniswap-v3/src/abi/mod.rs | 4 + .../ethereum-uniswap-v3/src/abi/pool.rs | 5153 +++++++++++++++++ .../ethereum-uniswap-v3/src/events/burn.rs | 45 + .../ethereum-uniswap-v3/src/events/collect.rs | 58 + .../src/events/collect_fee_protocol.rs | 50 + .../ethereum-uniswap-v3/src/events/flash.rs | 50 + .../src/events/initialize.rs | 34 + .../ethereum-uniswap-v3/src/events/mint.rs | 58 + .../ethereum-uniswap-v3/src/events/mod.rs | 150 + .../src/events/set_fee_protocol.rs | 34 + .../ethereum-uniswap-v3/src/events/swap.rs | 50 + substreams/ethereum-uniswap-v3/src/lib.rs | 11 + .../src/modules/1_map_pool_created.rs | 103 + .../src/modules/2_store_pools.rs | 23 + .../src/modules/3_map_balance_changes.rs | 40 + .../src/modules/4_store_pools_balances.rs | 28 + .../src/modules/5_map_pool_events.rs | 235 + .../ethereum-uniswap-v3/src/modules/mod.rs | 18 + substreams/ethereum-uniswap-v3/src/pb/mod.rs | 17 + .../src/pb/tycho.evm.v1.rs | 307 + .../src/pb/uniswap.types.v1.rs | 541 ++ .../ethereum-uniswap-v3/src/pb/uniswap.v3.rs | 41 + .../src/storage/constants.rs | 60 + .../ethereum-uniswap-v3/src/storage/mod.rs | 4 + .../src/storage/pool_storage.rs | 132 + .../ethereum-uniswap-v3/src/storage/utils.rs | 165 + substreams/ethereum-uniswap-v3/src/traits.rs | 30 + 70 files changed, 16697 insertions(+), 1 deletion(-) create mode 100644 substreams/crates/substreams-helper/.gitignore create mode 100644 substreams/crates/substreams-helper/Cargo.toml create mode 100644 substreams/crates/substreams-helper/Makefile create mode 100644 substreams/crates/substreams-helper/rust-toolchain.toml create mode 100644 substreams/crates/substreams-helper/src/common.rs create mode 100644 substreams/crates/substreams-helper/src/event_handler.rs create mode 100644 substreams/crates/substreams-helper/src/hex.rs create mode 100644 substreams/crates/substreams-helper/src/lib.rs create mode 100644 substreams/crates/substreams-helper/src/storage_change.rs create mode 100644 substreams/ethereum-uniswap-v2/Cargo.toml create mode 100644 substreams/ethereum-uniswap-v2/Makefile create mode 100644 substreams/ethereum-uniswap-v2/abi/Factory.json create mode 100644 substreams/ethereum-uniswap-v2/abi/Pool.json create mode 100644 substreams/ethereum-uniswap-v2/arbitrum-uniswap-v2.yaml create mode 100644 substreams/ethereum-uniswap-v2/build.rs create mode 100644 substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml create mode 100644 substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml create mode 100644 substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml create mode 100644 substreams/ethereum-uniswap-v2/proto/v1/uniswap.proto create mode 100644 substreams/ethereum-uniswap-v2/src/abi/factory.rs create mode 100644 substreams/ethereum-uniswap-v2/src/abi/mod.rs create mode 100644 substreams/ethereum-uniswap-v2/src/abi/pool.rs create mode 100644 substreams/ethereum-uniswap-v2/src/lib.rs create mode 100644 substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs create mode 100644 substreams/ethereum-uniswap-v2/src/modules/2_store_pools.rs create mode 100644 substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs create mode 100644 substreams/ethereum-uniswap-v2/src/modules/mod.rs create mode 100644 substreams/ethereum-uniswap-v2/src/pb/mod.rs create mode 100644 substreams/ethereum-uniswap-v2/src/pb/tycho.evm.uniswap.v2.rs create mode 100644 substreams/ethereum-uniswap-v2/src/pb/tycho.evm.v1.rs create mode 100644 substreams/ethereum-uniswap-v2/src/store_key.rs create mode 100644 substreams/ethereum-uniswap-v2/src/traits.rs create mode 100644 substreams/ethereum-uniswap-v3/Cargo.toml create mode 100644 substreams/ethereum-uniswap-v3/Makefile create mode 100644 substreams/ethereum-uniswap-v3/abi/Factory.json create mode 100644 substreams/ethereum-uniswap-v3/abi/Pool.json create mode 100644 substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml create mode 100644 substreams/ethereum-uniswap-v3/build.rs create mode 100644 substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml create mode 100644 substreams/ethereum-uniswap-v3/proto/v1/uniswap.proto create mode 100644 substreams/ethereum-uniswap-v3/src/abi/factory.rs create mode 100644 substreams/ethereum-uniswap-v3/src/abi/mod.rs create mode 100644 substreams/ethereum-uniswap-v3/src/abi/pool.rs create mode 100644 substreams/ethereum-uniswap-v3/src/events/burn.rs create mode 100644 substreams/ethereum-uniswap-v3/src/events/collect.rs create mode 100644 substreams/ethereum-uniswap-v3/src/events/collect_fee_protocol.rs create mode 100644 substreams/ethereum-uniswap-v3/src/events/flash.rs create mode 100644 substreams/ethereum-uniswap-v3/src/events/initialize.rs create mode 100644 substreams/ethereum-uniswap-v3/src/events/mint.rs create mode 100644 substreams/ethereum-uniswap-v3/src/events/mod.rs create mode 100644 substreams/ethereum-uniswap-v3/src/events/set_fee_protocol.rs create mode 100644 substreams/ethereum-uniswap-v3/src/events/swap.rs create mode 100644 substreams/ethereum-uniswap-v3/src/lib.rs create mode 100644 substreams/ethereum-uniswap-v3/src/modules/1_map_pool_created.rs create mode 100644 substreams/ethereum-uniswap-v3/src/modules/2_store_pools.rs create mode 100644 substreams/ethereum-uniswap-v3/src/modules/3_map_balance_changes.rs create mode 100644 substreams/ethereum-uniswap-v3/src/modules/4_store_pools_balances.rs create mode 100644 substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs create mode 100644 substreams/ethereum-uniswap-v3/src/modules/mod.rs create mode 100644 substreams/ethereum-uniswap-v3/src/pb/mod.rs create mode 100644 substreams/ethereum-uniswap-v3/src/pb/tycho.evm.v1.rs create mode 100644 substreams/ethereum-uniswap-v3/src/pb/uniswap.types.v1.rs create mode 100644 substreams/ethereum-uniswap-v3/src/pb/uniswap.v3.rs create mode 100644 substreams/ethereum-uniswap-v3/src/storage/constants.rs create mode 100644 substreams/ethereum-uniswap-v3/src/storage/mod.rs create mode 100644 substreams/ethereum-uniswap-v3/src/storage/pool_storage.rs create mode 100644 substreams/ethereum-uniswap-v3/src/storage/utils.rs create mode 100644 substreams/ethereum-uniswap-v3/src/traits.rs diff --git a/substreams/Cargo.lock b/substreams/Cargo.lock index c3164d5..0654435 100644 --- a/substreams/Cargo.lock +++ b/substreams/Cargo.lock @@ -29,6 +29,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bigdecimal" version = "0.3.1" @@ -126,6 +132,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "either" version = "1.10.0" @@ -934,6 +946,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "substreams-entity-change" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2c7fca123abff659d15ed30da5b605fa954a29e912c94260c488d0d18f9107d" +dependencies = [ + "base64", + "prost 0.11.9", + "prost-types 0.11.9", + "substreams", +] + [[package]] name = "substreams-ethereum" version = "0.9.9" @@ -965,6 +989,20 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "substreams-ethereum-ambient" +version = "0.3.0" +dependencies = [ + "anyhow", + "bytes", + "ethabi 18.0.0", + "hex", + "hex-literal 0.4.1", + "prost 0.11.9", + "substreams", + "substreams-ethereum", +] + [[package]] name = "substreams-ethereum-core" version = "0.9.9" @@ -997,6 +1035,64 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "substreams-ethereum-uniswap-v2" +version = "0.2.0" +dependencies = [ + "anyhow", + "ethabi 18.0.0", + "getrandom", + "hex-literal 0.4.1", + "itertools 0.12.1", + "num-bigint", + "prost 0.11.9", + "serde", + "serde_qs", + "substreams", + "substreams-ethereum", + "substreams-helper", +] + +[[package]] +name = "substreams-ethereum-uniswap-v3" +version = "0.2.0" +dependencies = [ + "anyhow", + "ethabi 18.0.0", + "getrandom", + "hex", + "hex-literal 0.4.1", + "num-bigint", + "prost 0.11.9", + "substreams", + "substreams-entity-change", + "substreams-ethereum", + "substreams-helper", + "tiny-keccak", +] + +[[package]] +name = "substreams-helper" +version = "0.0.2" +dependencies = [ + "anyhow", + "base64", + "bigdecimal", + "downcast-rs", + "ethabi 18.0.0", + "hex", + "hex-literal 0.4.1", + "num-bigint", + "pad", + "prost 0.11.9", + "prost-types 0.12.3", + "substreams", + "substreams-entity-change", + "substreams-ethereum", + "thiserror", + "tiny-keccak", +] + [[package]] name = "substreams-macro" version = "0.5.13" diff --git a/substreams/Cargo.toml b/substreams/Cargo.toml index 904aedb..720b54e 100644 --- a/substreams/Cargo.toml +++ b/substreams/Cargo.toml @@ -1,5 +1,13 @@ [workspace] -members = ["ethereum-balancer", "ethereum-curve", "crates/tycho-substreams"] +members = [ + "ethereum-balancer", + "ethereum-curve", + "crates/tycho-substreams", + "crates/substreams-helper", + "ethereum-ambient", + "ethereum-uniswap-v2", + "ethereum-uniswap-v3", +] resolver = "2" @@ -9,9 +17,11 @@ substreams = "0.5" prost = "0.11" prost-types = "0.12.3" hex-literal = "0.4.1" +anyhow = "1.0.75" hex = "0.4.3" ethabi = "18.0.0" tycho-substreams = { path = "crates/tycho-substreams" } +substreams-helper = { path = "crates/substreams-helper" } serde = "1.0.204" serde_json = "1.0.120" diff --git a/substreams/crates/substreams-helper/.gitignore b/substreams/crates/substreams-helper/.gitignore new file mode 100644 index 0000000..03314f7 --- /dev/null +++ b/substreams/crates/substreams-helper/.gitignore @@ -0,0 +1 @@ +Cargo.lock diff --git a/substreams/crates/substreams-helper/Cargo.toml b/substreams/crates/substreams-helper/Cargo.toml new file mode 100644 index 0000000..41624d6 --- /dev/null +++ b/substreams/crates/substreams-helper/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "substreams-helper" +description = "Substreams Helper Crate - by Messari" +version = "0.0.2" +edition = "2021" +license = "MIT" +homepage = "https://messari.io/" +repository = "https://github.com/messari/substreams/substreams-helper" + +[dependencies] +ethabi.workspace = true +hex.workspace = true +hex-literal.workspace = true +prost.workspace = true +prost-types.workspace = true +num-bigint = "0.4" +bigdecimal = "0.3" +pad = "0.1" +tiny-keccak = { version = "2.0", features = ["keccak"] } +substreams = { workspace = true } +substreams-ethereum = { workspace = true } +thiserror = "1.0.37" +downcast-rs = "1.2.0" +substreams-entity-change = "1.1.0" +base64 = "0.13.0" + + +[build-dependencies] +anyhow.workspace = true diff --git a/substreams/crates/substreams-helper/Makefile b/substreams/crates/substreams-helper/Makefile new file mode 100644 index 0000000..f991fe2 --- /dev/null +++ b/substreams/crates/substreams-helper/Makefile @@ -0,0 +1,7 @@ +.PHONY: build +build: + cargo build --target wasm32-unknown-unknown --release + +.PHONY: publish +publish: + cargo publish --target wasm32-unknown-unknown diff --git a/substreams/crates/substreams-helper/rust-toolchain.toml b/substreams/crates/substreams-helper/rust-toolchain.toml new file mode 100644 index 0000000..98d17b4 --- /dev/null +++ b/substreams/crates/substreams-helper/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.75.0" +components = [ "rustfmt" ] +targets = [ "wasm32-unknown-unknown" ] diff --git a/substreams/crates/substreams-helper/src/common.rs b/substreams/crates/substreams-helper/src/common.rs new file mode 100644 index 0000000..cdc12d8 --- /dev/null +++ b/substreams/crates/substreams-helper/src/common.rs @@ -0,0 +1,69 @@ +use ethabi::ethereum_types::Address; + +use substreams::store::{ + StoreGet, StoreGetBigDecimal, StoreGetBigInt, StoreGetInt64, StoreGetProto, StoreGetRaw, + StoreGetString, +}; + +use crate::hex::Hexable; + +/// HasAddresser is a trait that a few functionalities in this crate depend on. +/// Every time we need to filter something by address (events emmited by a set of addresses, +/// storage changes occurring on certain contracts, etc) you'll likely need +/// to provide a HasAddresser. +/// +/// HasAddresser has been implemented already for all substreams::store's for convenience. +/// So if you know a given store module contains the list of addresses you want to filter by +/// you can pass it directly as a HasAddresser. In this case, the addresses need to be the store key +/// hex encoded as a string including the leading 0x. The value of the store is ignored. +pub trait HasAddresser { + fn has_address(&self, key: Address) -> bool; +} + +impl HasAddresser for Vec
{ + fn has_address(&self, key: Address) -> bool { + self.contains(&key) + } +} + +impl HasAddresser for StoreGetString { + fn has_address(&self, key: Address) -> bool { + self.get_last(key.to_hex()).is_some() + } +} + +impl HasAddresser for StoreGetProto { + fn has_address(&self, key: Address) -> bool { + self.get_last(key.to_hex()).is_some() + } +} + +impl HasAddresser for StoreGetRaw { + fn has_address(&self, key: Address) -> bool { + self.get_last(key.to_hex()).is_some() + } +} + +impl HasAddresser for StoreGetBigInt { + fn has_address(&self, key: Address) -> bool { + self.get_last(key.to_hex()).is_some() + } +} + +impl HasAddresser for StoreGetBigDecimal { + fn has_address(&self, key: Address) -> bool { + self.get_last(key.to_hex()).is_some() + } +} + +impl HasAddresser for StoreGetInt64 { + fn has_address(&self, key: Address) -> bool { + self.get_last(key.to_hex()).is_some() + } +} + +impl HasAddresser for Address { + fn has_address(&self, key: Address) -> bool { + key == *self + } +} diff --git a/substreams/crates/substreams-helper/src/event_handler.rs b/substreams/crates/substreams-helper/src/event_handler.rs new file mode 100644 index 0000000..8a91342 --- /dev/null +++ b/substreams/crates/substreams-helper/src/event_handler.rs @@ -0,0 +1,103 @@ +use std::collections::HashMap; + +use ethabi::ethereum_types::Address; +use substreams_ethereum::{ + pb::eth::v2::{self as eth}, + Event, +}; + +use crate::common::HasAddresser; + +/// Utility struct to easily filter events and assign them handlers. +/// +/// Usage: +/// ``` +/// let eh = EventHandler::new(&block); +/// eh.filter_by_address(store); // This is optional, if omitted it will handle all events that match the type, independently of the emitting contract. +/// eh.on::(&mut on_transfer); +/// eh.on::(&mut on_approval); +/// eh.on::(&mut on_mint); +/// eh.on::(&mut on_burn); +/// eh.handle_events(); // this will run all handlers +/// ``` +/// +/// You'll likely want to mutate some value from the handlers that is in the current scope. +/// For that, make your handlers be closures, that close over the variable you want to mutate, and +/// have the whole EventHandler block of code in its own scope (either by wrapping it in an aux +/// function or by wrapping it in {...}) +/// +/// Like so: +/// ``` +/// let mut balances : Vec = vec![]; +/// { +/// let mut on_transfer = |/*...*/| { +/// // this handler modifies `balances` +/// balances.push(some_balance); +/// }; +/// let eh = EventHandler::new(&block); +/// eh.on::(&mut on_transfer); +/// eh.handle_events(); +/// } +/// +/// // do whatever else with `balances` here. +/// ``` +pub struct EventHandler<'a> { + block: &'a eth::Block, + #[allow(clippy::type_complexity)] + handlers: HashMap<&'static str, Box>, + addresses: Option>, +} + +impl<'a> EventHandler<'a> { + pub fn new(block: &'a eth::Block) -> Self { + Self { block, handlers: HashMap::new(), addresses: None } + } + + /// Sets the HasAddresser as a filter for which events to handle. + /// Only one at a time can be set. Setting it twice will remove the first one. + /// Addresses found in the `HasAddresser` will be the ones we'll handle events from. + pub fn filter_by_address(&mut self, addresser: impl HasAddresser + 'a) { + self.addresses = Some(Box::new(addresser)); + } + + /// Registers a handler to be run on a given event. The handler should have the signature: + /// `|ev: SomeEvent, tx: &pbeth::v2::TransactionTrace, log: &pbeth::v2::Log|`. + /// You can only assign one handler to each Event type. + /// Handlers are keyed by the name of the event they are handling, so be careful to not assign + /// handlers for 2 different events named equal. + pub fn on(&mut self, mut handler: F) + where + F: FnMut(E, ð::TransactionTrace, ð::Log) + 'a, + { + self.handlers.insert( + E::NAME, + Box::new(move |log: ð::Log, tx: ð::TransactionTrace| { + if let Some(event) = E::match_and_decode(log) { + handler(event, tx, log); + } + }), + ); + } + + /// Will run all registered handlers for all events present on the block that match the given + /// filters. You'll likely want to run this just once. + pub fn handle_events(&mut self) { + // Here we don't need to filter out failed transactions because logs only exist for + // successful ones. + for log in self.block.logs() { + if self.addresses.is_some() && + !&self + .addresses + .as_ref() + .unwrap() + .has_address(Address::from_slice(log.log.address.as_slice())) + { + continue; + } + + for handler in self.handlers.values_mut() { + handler(log.log, log.receipt.transaction); + } + } + } +} \ No newline at end of file diff --git a/substreams/crates/substreams-helper/src/hex.rs b/substreams/crates/substreams-helper/src/hex.rs new file mode 100644 index 0000000..c056ba7 --- /dev/null +++ b/substreams/crates/substreams-helper/src/hex.rs @@ -0,0 +1,20 @@ +use ethabi::ethereum_types::Address; +use substreams::Hex; + +pub trait Hexable { + fn to_hex(&self) -> String; +} + +impl Hexable for Vec { + fn to_hex(&self) -> String { + let mut str = Hex::encode(self); + str.insert_str(0, "0x"); + str + } +} + +impl Hexable for Address { + fn to_hex(&self) -> String { + self.as_bytes().to_vec().to_hex() + } +} diff --git a/substreams/crates/substreams-helper/src/lib.rs b/substreams/crates/substreams-helper/src/lib.rs new file mode 100644 index 0000000..8e66ac5 --- /dev/null +++ b/substreams/crates/substreams-helper/src/lib.rs @@ -0,0 +1,5 @@ +pub mod common; + +pub mod event_handler; +pub mod hex; +pub mod storage_change; diff --git a/substreams/crates/substreams-helper/src/storage_change.rs b/substreams/crates/substreams-helper/src/storage_change.rs new file mode 100644 index 0000000..0fe484e --- /dev/null +++ b/substreams/crates/substreams-helper/src/storage_change.rs @@ -0,0 +1,14 @@ +use substreams_ethereum::pb::eth::v2::StorageChange; + +pub trait StorageChangesFilter { + fn filter_by_address(&self, contract_addr: &[u8; 20]) -> Vec<&StorageChange>; +} + +impl StorageChangesFilter for Vec { + fn filter_by_address(&self, contract_addr: &[u8; 20]) -> Vec<&StorageChange> { + return self + .iter() + .filter(|change| change.address == contract_addr) + .collect(); + } +} diff --git a/substreams/ethereum-uniswap-v2/Cargo.toml b/substreams/ethereum-uniswap-v2/Cargo.toml new file mode 100644 index 0000000..ab2e76a --- /dev/null +++ b/substreams/ethereum-uniswap-v2/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "substreams-ethereum-uniswap-v2" +version = "0.2.0" +edition = "2021" + +[lib] +name = "ethereum_uniswap_v2" +crate-type = ["cdylib"] + +[dependencies] +substreams.workspace = true +substreams-ethereum.workspace = true +prost.workspace = true +ethabi.workspace = true +anyhow = { workspace = true, features = [] } +hex-literal.workspace = true +substreams-helper.workspace = true +num-bigint = "0.4.4" +itertools = "0.12.1" +serde_qs = "0.13.0" +serde.workspace = true + +[target.wasm32-unknown-unknown.dependencies] +getrandom = { version = "0.2", features = ["custom"] } + +[build-dependencies] +anyhow.workspace = true +substreams-ethereum.workspace = true diff --git a/substreams/ethereum-uniswap-v2/Makefile b/substreams/ethereum-uniswap-v2/Makefile new file mode 100644 index 0000000..d9be557 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/Makefile @@ -0,0 +1,2 @@ +build: + cargo build --target wasm32-unknown-unknown --release \ No newline at end of file diff --git a/substreams/ethereum-uniswap-v2/abi/Factory.json b/substreams/ethereum-uniswap-v2/abi/Factory.json new file mode 100644 index 0000000..1b14599 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/abi/Factory.json @@ -0,0 +1,125 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "_feeToSetter", "type": "address" } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "pair", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "PairCreated", + "type": "event" + }, + { + "constant": true, + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "allPairs", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "allPairsLength", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "internalType": "address", "name": "tokenA", "type": "address" }, + { "internalType": "address", "name": "tokenB", "type": "address" } + ], + "name": "createPair", + "outputs": [ + { "internalType": "address", "name": "pair", "type": "address" } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeTo", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "feeToSetter", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "address", "name": "", "type": "address" } + ], + "name": "getPair", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "internalType": "address", "name": "_feeTo", "type": "address" } + ], + "name": "setFeeTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "internalType": "address", "name": "_feeToSetter", "type": "address" } + ], + "name": "setFeeToSetter", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/substreams/ethereum-uniswap-v2/abi/Pool.json b/substreams/ethereum-uniswap-v2/abi/Pool.json new file mode 100644 index 0000000..53582c1 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/abi/Pool.json @@ -0,0 +1,713 @@ +[ + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint112", + "name": "reserve0", + "type": "uint112" + }, + { + "indexed": false, + "internalType": "uint112", + "name": "reserve1", + "type": "uint112" + } + ], + "name": "Sync", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MINIMUM_LIQUIDITY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "PERMIT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getReserves", + "outputs": [ + { + "internalType": "uint112", + "name": "_reserve0", + "type": "uint112" + }, + { + "internalType": "uint112", + "name": "_reserve1", + "type": "uint112" + }, + { + "internalType": "uint32", + "name": "_blockTimestampLast", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_token0", + "type": "address" + }, + { + "internalType": "address", + "name": "_token1", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "kLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "price0CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "price1CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "skim", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "sync", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "token0", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "token1", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/substreams/ethereum-uniswap-v2/arbitrum-uniswap-v2.yaml b/substreams/ethereum-uniswap-v2/arbitrum-uniswap-v2.yaml new file mode 100644 index 0000000..7f287bc --- /dev/null +++ b/substreams/ethereum-uniswap-v2/arbitrum-uniswap-v2.yaml @@ -0,0 +1,49 @@ +specVersion: v0.1.0 +package: + name: "arbitrum_uniswap_v2" + version: v0.2.0 + +protobuf: + files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/entity.proto + - uniswap.proto + importPaths: + - ./proto/v1 + - ../../proto/ + +binaries: + default: + type: wasm/rust-v1 + file: ../../target/wasm32-unknown-unknown/substreams/ethereum_uniswap_v2.wasm + +modules: + - name: map_pools_created + kind: map + initialBlock: 150442611 + inputs: + - params: string + - source: sf.ethereum.type.v2.Block + output: + type: proto:tycho.evm.v1.BlockChanges + + - name: store_pools + kind: store + initialBlock: 150442611 + updatePolicy: set_if_not_exists + valueType: proto:tycho.evm.uniswap.v2.Pool + inputs: + - map: map_pools_created + + - name: map_pool_events + kind: map + initialBlock: 150442611 + inputs: + - source: sf.ethereum.type.v2.Block + - map: map_pools_created + - store: store_pools + output: + type: proto:tycho.evm.v1.BlockChanges + +params: + map_pools_created: factory_address=f1D7CC64Fb4452F05c498126312eBE29f30Fbcf9&protocol_type_name=uniswap_v2_pool diff --git a/substreams/ethereum-uniswap-v2/build.rs b/substreams/ethereum-uniswap-v2/build.rs new file mode 100644 index 0000000..7992bb0 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/build.rs @@ -0,0 +1,12 @@ +use anyhow::{Ok, Result}; +use substreams_ethereum::Abigen; + +fn main() -> Result<(), anyhow::Error> { + Abigen::new("Factory", "abi/Factory.json")? + .generate()? + .write_to_file("src/abi/factory.rs")?; + Abigen::new("Pool", "abi/Pool.json")? + .generate()? + .write_to_file("src/abi/pool.rs")?; + Ok(()) +} diff --git a/substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml b/substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml new file mode 100644 index 0000000..7424d6c --- /dev/null +++ b/substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml @@ -0,0 +1,49 @@ +specVersion: v0.1.0 +package: + name: "ethereum_pancakeswap" + version: v0.2.0 + +protobuf: + files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/entity.proto + - uniswap.proto + importPaths: + - ./proto/v1 + - ../../proto/ + +binaries: + default: + type: wasm/rust-v1 + file: ../../target/wasm32-unknown-unknown/substreams/ethereum_uniswap_v2.wasm + +modules: + - name: map_pools_created + kind: map + initialBlock: 15614590 + inputs: + - params: string + - source: sf.ethereum.type.v2.Block + output: + type: proto:tycho.evm.v1.BlockChanges + + - name: store_pools + kind: store + initialBlock: 15614590 + updatePolicy: set_if_not_exists + valueType: proto:tycho.evm.v1.ProtocolComponent + inputs: + - map: map_pools_created + + - name: map_pool_events + kind: map + initialBlock: 15614590 + inputs: + - source: sf.ethereum.type.v2.Block + - map: map_pools_created + - store: store_pools + output: + type: proto:tycho.evm.v1.BlockChanges + +params: + map_pools_created: factory_address=1097053fd2ea711dad45caccc45eff7548fcb362&protocol_type_name=pancakeswap_pool diff --git a/substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml b/substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml new file mode 100644 index 0000000..e5334d1 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml @@ -0,0 +1,49 @@ +specVersion: v0.1.0 +package: + name: "ethereum_sushiswap_v2" + version: v0.2.0 + +protobuf: + files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/entity.proto + - uniswap.proto + importPaths: + - ./proto/v1 + - ../../proto/ + +binaries: + default: + type: wasm/rust-v1 + file: ../../target/wasm32-unknown-unknown/substreams/ethereum_uniswap_v2.wasm + +modules: + - name: map_pools_created + kind: map + initialBlock: 10794229 + inputs: + - params: string + - source: sf.ethereum.type.v2.Block + output: + type: proto:tycho.evm.v1.BlockChanges + + - name: store_pools + kind: store + initialBlock: 10794229 + updatePolicy: set_if_not_exists + valueType: proto:tycho.evm.uniswap.v2.Pool + inputs: + - map: map_pools_created + + - name: map_pool_events + kind: map + initialBlock: 10794229 + inputs: + - source: sf.ethereum.type.v2.Block + - map: map_pools_created + - store: store_pools + output: + type: proto:tycho.evm.v1.BlockChanges + +params: + map_pools_created: factory_address=c0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac&protocol_type_name=sushiswap_v2_pool diff --git a/substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml b/substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml new file mode 100644 index 0000000..1ed8f63 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml @@ -0,0 +1,49 @@ +specVersion: v0.1.0 +package: + name: "ethereum_uniswap_v2" + version: v0.2.0 + +protobuf: + files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/entity.proto + - uniswap.proto + importPaths: + - ./proto/v1 + - ../../proto/ + +binaries: + default: + type: wasm/rust-v1 + file: ../../target/wasm32-unknown-unknown/substreams/ethereum_uniswap_v2.wasm + +modules: + - name: map_pools_created + kind: map + initialBlock: 10008300 + inputs: + - params: string + - source: sf.ethereum.type.v2.Block + output: + type: proto:tycho.evm.v1.BlockChanges + + - name: store_pools + kind: store + initialBlock: 10008300 + updatePolicy: set_if_not_exists + valueType: proto:tycho.evm.uniswap.v2.Pool + inputs: + - map: map_pools_created + + - name: map_pool_events + kind: map + initialBlock: 10008300 + inputs: + - source: sf.ethereum.type.v2.Block + - map: map_pools_created + - store: store_pools + output: + type: proto:tycho.evm.v1.BlockChanges + +params: + map_pools_created: factory_address=5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f&protocol_type_name=uniswap_v2_pool diff --git a/substreams/ethereum-uniswap-v2/proto/v1/uniswap.proto b/substreams/ethereum-uniswap-v2/proto/v1/uniswap.proto new file mode 100644 index 0000000..3f26fd1 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/proto/v1/uniswap.proto @@ -0,0 +1,61 @@ +syntax = "proto3"; + +package tycho.evm.uniswap.v2; + +message Pools { + repeated Pool pools = 1; +} + +message Pool { + bytes address = 1; + + bytes token0 = 2; + bytes token1 = 3; + + bytes created_tx_hash = 4; +} + +message Events { + repeated Event events = 1; +} + +message Event { + oneof type { + DepositEvent deposit_type = 10; + WithdrawEvent withdraw_type = 20; + SyncEvent sync_type = 30; + SwapEvent swap_type = 40; + } + + string hash = 100; + uint32 log_index = 101; + uint64 log_ordinal = 102; + string to = 103; + string from = 104; + uint64 block_number = 105; + uint64 timestamp = 106; + string pool = 107; +} + +message DepositEvent { + repeated string input_token_amounts = 1; + optional string output_token_amount = 2; +} + +message WithdrawEvent { + repeated string input_token_amounts = 1; + optional string output_token_amount = 2; +} + +message SyncEvent { + string reserve0 = 1; + string reserve1 = 2; +} + +message SwapEvent { + string token_in = 1; + string amount_in = 2; + + string token_out = 3; + string amount_out = 4; +} diff --git a/substreams/ethereum-uniswap-v2/src/abi/factory.rs b/substreams/ethereum-uniswap-v2/src/abi/factory.rs new file mode 100644 index 0000000..2a92a8f --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/abi/factory.rs @@ -0,0 +1,904 @@ + const INTERNAL_ERR: &'static str = "`ethabi_derive` internal error"; + /// Contract's functions. + #[allow(dead_code, unused_imports, unused_variables)] + pub mod functions { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct AllPairs { + pub param0: substreams::scalar::BigInt, + } + impl AllPairs { + const METHOD_ID: [u8; 4] = [30u8, 61u8, 209u8, 139u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + param0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.param0.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for AllPairs { + const NAME: &'static str = "allPairs"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for AllPairs { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct AllPairsLength {} + impl AllPairsLength { + const METHOD_ID: [u8; 4] = [87u8, 79u8, 43u8, 163u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for AllPairsLength { + const NAME: &'static str = "allPairsLength"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for AllPairsLength { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct CreatePair { + pub token_a: Vec, + pub token_b: Vec, + } + impl CreatePair { + const METHOD_ID: [u8; 4] = [201u8, 198u8, 83u8, 150u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + token_a: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + token_b: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.token_a), + ), + ethabi::Token::Address( + ethabi::Address::from_slice(&self.token_b), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for CreatePair { + const NAME: &'static str = "createPair"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for CreatePair { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct FeeTo {} + impl FeeTo { + const METHOD_ID: [u8; 4] = [1u8, 126u8, 126u8, 88u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for FeeTo { + const NAME: &'static str = "feeTo"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for FeeTo { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct FeeToSetter {} + impl FeeToSetter { + const METHOD_ID: [u8; 4] = [9u8, 75u8, 116u8, 21u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for FeeToSetter { + const NAME: &'static str = "feeToSetter"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for FeeToSetter { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct GetPair { + pub param0: Vec, + pub param1: Vec, + } + impl GetPair { + const METHOD_ID: [u8; 4] = [230u8, 164u8, 57u8, 5u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + param0: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + param1: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.param0), + ), + ethabi::Token::Address(ethabi::Address::from_slice(&self.param1)), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for GetPair { + const NAME: &'static str = "getPair"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for GetPair { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SetFeeTo { + pub fee_to: Vec, + } + impl SetFeeTo { + const METHOD_ID: [u8; 4] = [244u8, 105u8, 1u8, 237u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + fee_to: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.fee_to))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for SetFeeTo { + const NAME: &'static str = "setFeeTo"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SetFeeToSetter { + pub fee_to_setter: Vec, + } + impl SetFeeToSetter { + const METHOD_ID: [u8; 4] = [162u8, 231u8, 74u8, 246u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + fee_to_setter: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.fee_to_setter), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for SetFeeToSetter { + const NAME: &'static str = "setFeeToSetter"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + } + /// Contract's events. + #[allow(dead_code, unused_imports, unused_variables)] + pub mod events { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct PairCreated { + pub token0: Vec, + pub token1: Vec, + pub pair: Vec, + pub param3: substreams::scalar::BigInt, + } + impl PairCreated { + const TOPIC_ID: [u8; 32] = [ + 13u8, + 54u8, + 72u8, + 189u8, + 15u8, + 107u8, + 168u8, + 1u8, + 52u8, + 163u8, + 59u8, + 169u8, + 39u8, + 90u8, + 197u8, + 133u8, + 217u8, + 211u8, + 21u8, + 240u8, + 173u8, + 131u8, + 85u8, + 205u8, + 222u8, + 253u8, + 227u8, + 26u8, + 250u8, + 40u8, + 208u8, + 233u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 64usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Uint(256usize)], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + token0: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'token0' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + token1: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'token1' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + pair: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + param3: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for PairCreated { + const NAME: &'static str = "PairCreated"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + } \ No newline at end of file diff --git a/substreams/ethereum-uniswap-v2/src/abi/mod.rs b/substreams/ethereum-uniswap-v2/src/abi/mod.rs new file mode 100644 index 0000000..f743394 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/abi/mod.rs @@ -0,0 +1,4 @@ +#![allow(clippy::all, clippy::pedantic, clippy::nursery)] + +pub mod factory; +pub mod pool; diff --git a/substreams/ethereum-uniswap-v2/src/abi/pool.rs b/substreams/ethereum-uniswap-v2/src/abi/pool.rs new file mode 100644 index 0000000..4590901 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/abi/pool.rs @@ -0,0 +1,3554 @@ + const INTERNAL_ERR: &'static str = "`ethabi_derive` internal error"; + /// Contract's functions. + #[allow(dead_code, unused_imports, unused_variables)] + pub mod functions { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct DomainSeparator {} + impl DomainSeparator { + const METHOD_ID: [u8; 4] = [54u8, 68u8, 229u8, 21u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result<[u8; 32usize], String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result<[u8; 32usize], String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut result = [0u8; 32]; + let v = values + .pop() + .expect("one output data should have existed") + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option<[u8; 32usize]> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for DomainSeparator { + const NAME: &'static str = "DOMAIN_SEPARATOR"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable<[u8; 32usize]> for DomainSeparator { + fn output(data: &[u8]) -> Result<[u8; 32usize], String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct MinimumLiquidity {} + impl MinimumLiquidity { + const METHOD_ID: [u8; 4] = [186u8, 154u8, 122u8, 86u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for MinimumLiquidity { + const NAME: &'static str = "MINIMUM_LIQUIDITY"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for MinimumLiquidity { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct PermitTypehash {} + impl PermitTypehash { + const METHOD_ID: [u8; 4] = [48u8, 173u8, 248u8, 31u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result<[u8; 32usize], String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result<[u8; 32usize], String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut result = [0u8; 32]; + let v = values + .pop() + .expect("one output data should have existed") + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option<[u8; 32usize]> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for PermitTypehash { + const NAME: &'static str = "PERMIT_TYPEHASH"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable<[u8; 32usize]> for PermitTypehash { + fn output(data: &[u8]) -> Result<[u8; 32usize], String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Allowance { + pub param0: Vec, + pub param1: Vec, + } + impl Allowance { + const METHOD_ID: [u8; 4] = [221u8, 98u8, 237u8, 62u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + param0: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + param1: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.param0), + ), + ethabi::Token::Address(ethabi::Address::from_slice(&self.param1)), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Allowance { + const NAME: &'static str = "allowance"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for Allowance { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Approve { + pub spender: Vec, + pub value: substreams::scalar::BigInt, + } + impl Approve { + const METHOD_ID: [u8; 4] = [9u8, 94u8, 167u8, 179u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Uint(256usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + spender: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + value: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.spender), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.value.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Bool], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_bool() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Approve { + const NAME: &'static str = "approve"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Approve { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct BalanceOf { + pub param0: Vec, + } + impl BalanceOf { + const METHOD_ID: [u8; 4] = [112u8, 160u8, 130u8, 49u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + param0: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.param0))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for BalanceOf { + const NAME: &'static str = "balanceOf"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for BalanceOf { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Burn { + pub to: Vec, + } + impl Burn { + const METHOD_ID: [u8; 4] = [137u8, 175u8, 203u8, 68u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + to: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.to))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option<(substreams::scalar::BigInt, substreams::scalar::BigInt)> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Burn { + const NAME: &'static str = "burn"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + > for Burn { + fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Decimals {} + impl Decimals { + const METHOD_ID: [u8; 4] = [49u8, 60u8, 229u8, 103u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(8usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Decimals { + const NAME: &'static str = "decimals"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for Decimals { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Factory {} + impl Factory { + const METHOD_ID: [u8; 4] = [196u8, 90u8, 1u8, 85u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Factory { + const NAME: &'static str = "factory"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Factory { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct GetReserves {} + impl GetReserves { + const METHOD_ID: [u8; 4] = [9u8, 2u8, 241u8, 172u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(112usize), + ethabi::ParamType::Uint(112usize), + ethabi::ParamType::Uint(32usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + > { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for GetReserves { + const NAME: &'static str = "getReserves"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + > for GetReserves { + fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Initialize { + pub token0: Vec, + pub token1: Vec, + } + impl Initialize { + const METHOD_ID: [u8; 4] = [72u8, 92u8, 201u8, 85u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + token0: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + token1: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.token0), + ), + ethabi::Token::Address(ethabi::Address::from_slice(&self.token1)), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Initialize { + const NAME: &'static str = "initialize"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct KLast {} + impl KLast { + const METHOD_ID: [u8; 4] = [116u8, 100u8, 252u8, 61u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for KLast { + const NAME: &'static str = "kLast"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for KLast { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Mint { + pub to: Vec, + } + impl Mint { + const METHOD_ID: [u8; 4] = [106u8, 98u8, 120u8, 66u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + to: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.to))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Mint { + const NAME: &'static str = "mint"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for Mint { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Name {} + impl Name { + const METHOD_ID: [u8; 4] = [6u8, 253u8, 222u8, 3u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::String], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_string() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Name { + const NAME: &'static str = "name"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Name { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Nonces { + pub param0: Vec, + } + impl Nonces { + const METHOD_ID: [u8; 4] = [126u8, 206u8, 190u8, 0u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + param0: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.param0))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Nonces { + const NAME: &'static str = "nonces"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for Nonces { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Permit { + pub owner: Vec, + pub spender: Vec, + pub value: substreams::scalar::BigInt, + pub deadline: substreams::scalar::BigInt, + pub v: substreams::scalar::BigInt, + pub r: [u8; 32usize], + pub s: [u8; 32usize], + } + impl Permit { + const METHOD_ID: [u8; 4] = [213u8, 5u8, 172u8, 207u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(8usize), + ethabi::ParamType::FixedBytes(32usize), + ethabi::ParamType::FixedBytes(32usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + owner: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + spender: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + value: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + deadline: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + v: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + r: { + let mut result = [0u8; 32]; + let v = values + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + s: { + let mut result = [0u8; 32]; + let v = values + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.owner)), + ethabi::Token::Address( + ethabi::Address::from_slice(&self.spender), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.value.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.deadline.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.v.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::FixedBytes(self.r.as_ref().to_vec()), + ethabi::Token::FixedBytes(self.s.as_ref().to_vec()), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Permit { + const NAME: &'static str = "permit"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Price0CumulativeLast {} + impl Price0CumulativeLast { + const METHOD_ID: [u8; 4] = [89u8, 9u8, 192u8, 213u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Price0CumulativeLast { + const NAME: &'static str = "price0CumulativeLast"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for Price0CumulativeLast { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Price1CumulativeLast {} + impl Price1CumulativeLast { + const METHOD_ID: [u8; 4] = [90u8, 61u8, 84u8, 147u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Price1CumulativeLast { + const NAME: &'static str = "price1CumulativeLast"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for Price1CumulativeLast { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Skim { + pub to: Vec, + } + impl Skim { + const METHOD_ID: [u8; 4] = [188u8, 37u8, 207u8, 119u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + to: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.to))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Skim { + const NAME: &'static str = "skim"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Swap { + pub amount0_out: substreams::scalar::BigInt, + pub amount1_out: substreams::scalar::BigInt, + pub to: Vec, + pub data: Vec, + } + impl Swap { + const METHOD_ID: [u8; 4] = [2u8, 44u8, 13u8, 159u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Address, + ethabi::ParamType::Bytes, + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + amount0_out: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1_out: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + to: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + data: values + .pop() + .expect(INTERNAL_ERR) + .into_bytes() + .expect(INTERNAL_ERR), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount0_out.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount1_out.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Address(ethabi::Address::from_slice(&self.to)), + ethabi::Token::Bytes(self.data.clone()), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Swap { + const NAME: &'static str = "swap"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Symbol {} + impl Symbol { + const METHOD_ID: [u8; 4] = [149u8, 216u8, 155u8, 65u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::String], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_string() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Symbol { + const NAME: &'static str = "symbol"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Symbol { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Sync {} + impl Sync { + const METHOD_ID: [u8; 4] = [255u8, 246u8, 202u8, 233u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Sync { + const NAME: &'static str = "sync"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Token0 {} + impl Token0 { + const METHOD_ID: [u8; 4] = [13u8, 254u8, 22u8, 129u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Token0 { + const NAME: &'static str = "token0"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Token0 { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Token1 {} + impl Token1 { + const METHOD_ID: [u8; 4] = [210u8, 18u8, 32u8, 167u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Token1 { + const NAME: &'static str = "token1"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Token1 { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct TotalSupply {} + impl TotalSupply { + const METHOD_ID: [u8; 4] = [24u8, 22u8, 13u8, 221u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for TotalSupply { + const NAME: &'static str = "totalSupply"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for TotalSupply { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Transfer { + pub to: Vec, + pub value: substreams::scalar::BigInt, + } + impl Transfer { + const METHOD_ID: [u8; 4] = [169u8, 5u8, 156u8, 187u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address, ethabi::ParamType::Uint(256usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + to: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + value: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.to)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.value.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Bool], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_bool() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Transfer { + const NAME: &'static str = "transfer"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Transfer { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct TransferFrom { + pub from: Vec, + pub to: Vec, + pub value: substreams::scalar::BigInt, + } + impl TransferFrom { + const METHOD_ID: [u8; 4] = [35u8, 184u8, 114u8, 221u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + from: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + to: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + value: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address(ethabi::Address::from_slice(&self.from)), + ethabi::Token::Address(ethabi::Address::from_slice(&self.to)), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.value.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Bool], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_bool() + .expect(INTERNAL_ERR), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for TransferFrom { + const NAME: &'static str = "transferFrom"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for TransferFrom { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + } + /// Contract's events. + #[allow(dead_code, unused_imports, unused_variables)] + pub mod events { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct Approval { + pub owner: Vec, + pub spender: Vec, + pub value: substreams::scalar::BigInt, + } + impl Approval { + const TOPIC_ID: [u8; 32] = [ + 140u8, + 91u8, + 225u8, + 229u8, + 235u8, + 236u8, + 125u8, + 91u8, + 209u8, + 79u8, + 113u8, + 66u8, + 125u8, + 30u8, + 132u8, + 243u8, + 221u8, + 3u8, + 20u8, + 192u8, + 247u8, + 178u8, + 41u8, + 30u8, + 91u8, + 32u8, + 10u8, + 200u8, + 199u8, + 195u8, + 185u8, + 37u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 32usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + owner: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'owner' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + spender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'spender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + value: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Approval { + const NAME: &'static str = "Approval"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Burn { + pub sender: Vec, + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + pub to: Vec, + } + impl Burn { + const TOPIC_ID: [u8; 32] = [ + 220u8, + 205u8, + 65u8, + 47u8, + 11u8, + 18u8, + 82u8, + 129u8, + 156u8, + 177u8, + 253u8, + 51u8, + 11u8, + 147u8, + 34u8, + 76u8, + 164u8, + 38u8, + 18u8, + 137u8, + 43u8, + 179u8, + 244u8, + 247u8, + 137u8, + 151u8, + 110u8, + 109u8, + 129u8, + 147u8, + 100u8, + 150u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 64usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + sender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'sender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + to: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'to' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Burn { + const NAME: &'static str = "Burn"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Mint { + pub sender: Vec, + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + } + impl Mint { + const TOPIC_ID: [u8; 32] = [ + 76u8, + 32u8, + 155u8, + 95u8, + 200u8, + 173u8, + 80u8, + 117u8, + 143u8, + 19u8, + 226u8, + 225u8, + 8u8, + 139u8, + 165u8, + 106u8, + 86u8, + 13u8, + 255u8, + 105u8, + 10u8, + 28u8, + 111u8, + 239u8, + 38u8, + 57u8, + 79u8, + 76u8, + 3u8, + 130u8, + 28u8, + 79u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 2usize { + return false; + } + if log.data.len() != 64usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + sender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'sender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Mint { + const NAME: &'static str = "Mint"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Swap { + pub sender: Vec, + pub amount0_in: substreams::scalar::BigInt, + pub amount1_in: substreams::scalar::BigInt, + pub amount0_out: substreams::scalar::BigInt, + pub amount1_out: substreams::scalar::BigInt, + pub to: Vec, + } + impl Swap { + const TOPIC_ID: [u8; 32] = [ + 215u8, + 138u8, + 217u8, + 95u8, + 164u8, + 108u8, + 153u8, + 75u8, + 101u8, + 81u8, + 208u8, + 218u8, + 133u8, + 252u8, + 39u8, + 95u8, + 230u8, + 19u8, + 206u8, + 55u8, + 101u8, + 127u8, + 184u8, + 213u8, + 227u8, + 209u8, + 48u8, + 132u8, + 1u8, + 89u8, + 216u8, + 34u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 128usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + sender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'sender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + to: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'to' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0_in: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1_in: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount0_out: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1_out: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Swap { + const NAME: &'static str = "Swap"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Sync { + pub reserve0: substreams::scalar::BigInt, + pub reserve1: substreams::scalar::BigInt, + } + impl Sync { + const TOPIC_ID: [u8; 32] = [ + 28u8, + 65u8, + 30u8, + 154u8, + 150u8, + 224u8, + 113u8, + 36u8, + 28u8, + 47u8, + 33u8, + 247u8, + 114u8, + 107u8, + 23u8, + 174u8, + 137u8, + 227u8, + 202u8, + 180u8, + 199u8, + 139u8, + 229u8, + 14u8, + 6u8, + 43u8, + 3u8, + 169u8, + 255u8, + 251u8, + 186u8, + 209u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 1usize { + return false; + } + if log.data.len() != 64usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(112usize), + ethabi::ParamType::Uint(112usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + reserve0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + reserve1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Sync { + const NAME: &'static str = "Sync"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Transfer { + pub from: Vec, + pub to: Vec, + pub value: substreams::scalar::BigInt, + } + impl Transfer { + const TOPIC_ID: [u8; 32] = [ + 221u8, + 242u8, + 82u8, + 173u8, + 27u8, + 226u8, + 200u8, + 155u8, + 105u8, + 194u8, + 176u8, + 104u8, + 252u8, + 55u8, + 141u8, + 170u8, + 149u8, + 43u8, + 167u8, + 241u8, + 99u8, + 196u8, + 161u8, + 22u8, + 40u8, + 245u8, + 90u8, + 77u8, + 245u8, + 35u8, + 179u8, + 239u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 32usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + from: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'from' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + to: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'to' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + value: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Transfer { + const NAME: &'static str = "Transfer"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + } \ No newline at end of file diff --git a/substreams/ethereum-uniswap-v2/src/lib.rs b/substreams/ethereum-uniswap-v2/src/lib.rs new file mode 100644 index 0000000..dfbef57 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/lib.rs @@ -0,0 +1,10 @@ +#![allow(clippy::not_unsafe_ptr_arg_deref)] + +mod abi; +mod modules; +mod pb; + +pub use modules::*; + +mod store_key; +mod traits; diff --git a/substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs b/substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs new file mode 100644 index 0000000..8e6a6dd --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs @@ -0,0 +1,99 @@ +use std::str::FromStr; + +use ethabi::ethereum_types::Address; +use serde::Deserialize; +use substreams::prelude::BigInt; +use substreams_ethereum::pb::eth::v2::{self as eth}; + +use substreams_helper::{event_handler::EventHandler, hex::Hexable}; + +use crate::{ + abi::factory::events::PairCreated, + pb::tycho::evm::v1::{ + Attribute, Block, BlockChanges, ChangeType, EntityChanges, FinancialType, + ImplementationType, ProtocolComponent, ProtocolType, Transaction, TransactionChanges, + }, +}; + +#[derive(Debug, Deserialize)] +struct Params { + factory_address: String, + protocol_type_name: String, +} + +#[substreams::handlers::map] +pub fn map_pools_created( + params: String, + block: eth::Block, +) -> Result { + let mut new_pools: Vec = vec![]; + + let params: Params = serde_qs::from_str(params.as_str()).expect("Unable to deserialize params"); + + get_pools(&block, &mut new_pools, ¶ms); + + let tycho_block: Block = block.into(); + + Ok(BlockChanges { block: Some(tycho_block), changes: new_pools }) +} + +fn get_pools(block: ð::Block, new_pools: &mut Vec, params: &Params) { + // Extract new pools from PairCreated events + let mut on_pair_created = |event: PairCreated, _tx: ð::TransactionTrace, _log: ð::Log| { + let tycho_tx: Transaction = _tx.into(); + + new_pools.push(TransactionChanges { + tx: Some(tycho_tx.clone()), + contract_changes: vec![], + entity_changes: vec![EntityChanges { + component_id: event.pair.to_hex(), + attributes: vec![ + Attribute { + name: "reserve0".to_string(), + value: BigInt::from(0).to_signed_bytes_le(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "reserve1".to_string(), + value: BigInt::from(0).to_signed_bytes_le(), + change: ChangeType::Creation.into(), + }, + ], + }], + component_changes: vec![ProtocolComponent { + id: event.pair.to_hex(), + tokens: vec![event.token0, event.token1], + contracts: vec![], + static_att: vec![ + // Trading Fee is hardcoded to 0.3%, saved as int in bps (basis points) + Attribute { + name: "fee".to_string(), + value: BigInt::from(30).to_signed_bytes_le(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "pool_address".to_string(), + value: event.pair, + change: ChangeType::Creation.into(), + }, + ], + change: i32::from(ChangeType::Creation), + protocol_type: Some(ProtocolType { + name: params.protocol_type_name.to_string(), + financial_type: FinancialType::Swap.into(), + attribute_schema: vec![], + implementation_type: ImplementationType::Custom.into(), + }), + tx: Some(tycho_tx), + }], + balance_changes: vec![], + }) + }; + + let mut eh = EventHandler::new(block); + + eh.filter_by_address(vec![Address::from_str(¶ms.factory_address).unwrap()]); + + eh.on::(&mut on_pair_created); + eh.handle_events(); +} diff --git a/substreams/ethereum-uniswap-v2/src/modules/2_store_pools.rs b/substreams/ethereum-uniswap-v2/src/modules/2_store_pools.rs new file mode 100644 index 0000000..b0d8256 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/modules/2_store_pools.rs @@ -0,0 +1,25 @@ +use substreams::store::{StoreNew, StoreSetIfNotExists, StoreSetIfNotExistsProto}; + +use crate::{ + pb::tycho::evm::v1::{BlockChanges, ProtocolComponent}, + store_key::StoreKey, +}; + +#[substreams::handlers::store] +pub fn store_pools( + pools_created: BlockChanges, + store: StoreSetIfNotExistsProto, +) { + // Store pools. Required so the next steps can match any event to a known pool by their address + + for change in pools_created.changes { + for new_protocol_component in change.component_changes { + // Use ordinal 0 because the address should be unique, so ordering doesn't matter. + store.set_if_not_exists( + 0, + StoreKey::Pool.get_unique_pool_key(&new_protocol_component.id), + &new_protocol_component, + ); + } + } +} diff --git a/substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs b/substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs new file mode 100644 index 0000000..c4e902d --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs @@ -0,0 +1,232 @@ +use itertools::Itertools; +use std::collections::HashMap; +use substreams::store::{StoreGet, StoreGetProto}; +use substreams_ethereum::pb::eth::v2::{self as eth}; + +use substreams_helper::{event_handler::EventHandler, hex::Hexable}; + +use crate::{ + abi::pool::events::Sync, + pb::tycho::evm::{ + v1, + v1::{ + Attribute, BalanceChange, BlockChanges, ChangeType, EntityChanges, ProtocolComponent, + TransactionChanges, + }, + }, + store_key::StoreKey, + traits::PoolAddresser, +}; + +// Auxiliary struct to serve as a key for the HashMaps. +#[derive(Clone, Hash, Eq, PartialEq)] +struct ComponentKey { + component_id: String, + name: T, +} + +impl ComponentKey { + fn new(component_id: String, name: T) -> Self { + ComponentKey { component_id, name } + } +} + +#[derive(Clone)] +struct PartialChanges { + transaction: v1::Transaction, + entity_changes: HashMap, Attribute>, + balance_changes: HashMap>, BalanceChange>, +} + +impl PartialChanges { + // Consolidate the entity changes into a vector of EntityChanges. Initially, the entity changes + // are in a map to prevent duplicates. For each transaction, we need to have only one final + // state change, per state. Example: + // If we have two sync events for the same pool (in the same tx), we need to have only one final + // state change for the reserves. This will be the last sync event, as it is the final state + // of the pool after the transaction. + fn consolidate_entity_changes(self) -> Vec { + self.entity_changes + .into_iter() + .map(|(key, attribute)| (key.component_id, attribute)) + .into_group_map() + .into_iter() + .map(|(component_id, attributes)| EntityChanges { component_id, attributes }) + .collect() + } +} + +#[substreams::handlers::map] +pub fn map_pool_events( + block: eth::Block, + block_entity_changes: BlockChanges, + pools_store: StoreGetProto, +) -> Result { + // Sync event is sufficient for our use-case. Since it's emitted on every reserve-altering + // function call, we can use it as the only event to update the reserves of a pool. + let mut block_entity_changes = block_entity_changes; + let mut tx_changes: HashMap, PartialChanges> = HashMap::new(); + + handle_sync(&block, &mut tx_changes, &pools_store); + merge_block(&mut tx_changes, &mut block_entity_changes); + + Ok(block_entity_changes) +} + +/// Handle the sync events and update the reserves of the pools. +/// +/// This function is called for each block, and it will handle the sync events for each transaction. +/// On UniswapV2, Sync events are emitted on every reserve-altering function call, so we can use +/// only this event to keep track of the pool state. +/// +/// This function also relies on an intermediate HashMap to store the changes for each transaction. +/// This is necessary because we need to consolidate the changes for each transaction before adding +/// them to the block_entity_changes. This HashMap prevents us from having duplicate changes for the +/// same pool and token. See the PartialChanges struct for more details. +fn handle_sync( + block: ð::Block, + tx_changes: &mut HashMap, PartialChanges>, + store: &StoreGetProto, +) { + let mut on_sync = |event: Sync, _tx: ð::TransactionTrace, _log: ð::Log| { + let pool_address_hex = _log.address.to_hex(); + + let pool = + store.must_get_last(StoreKey::Pool.get_unique_pool_key(pool_address_hex.as_str())); + // Convert reserves to bytes + let reserves_bytes = [event.reserve0, event.reserve1]; + + let tx_change = tx_changes + .entry(_tx.hash.clone()) + .or_insert_with(|| PartialChanges { + transaction: _tx.into(), + entity_changes: HashMap::new(), + balance_changes: HashMap::new(), + }); + + for (i, reserve_bytes) in reserves_bytes.iter().enumerate() { + let attribute_name = format!("reserve{}", i); + // By using a HashMap, we can overwrite the previous value of the reserve attribute if + // it is for the same pool and the same attribute name (reserves). + tx_change.entity_changes.insert( + ComponentKey::new(pool_address_hex.clone(), attribute_name.clone()), + Attribute { + name: attribute_name, + value: reserve_bytes + .clone() + .to_signed_bytes_le(), //TODO: Unify bytes encoding (either be or le) + change: ChangeType::Update.into(), + }, + ); + } + + // Update balance changes for each token + for (index, token) in pool.tokens.iter().enumerate() { + let balance = &reserves_bytes[index]; + // HashMap also prevents having duplicate balance changes for the same pool and token. + tx_change.balance_changes.insert( + ComponentKey::new(pool_address_hex.clone(), token.clone()), + BalanceChange { + token: token.clone(), + balance: balance.clone().to_signed_bytes_be(), + component_id: pool_address_hex.as_bytes().to_vec(), + }, + ); + } + }; + + let mut eh = EventHandler::new(block); + // Filter the sync events by the pool address, to make sure we don't process events for other + // Protocols that use the same event signature. + eh.filter_by_address(PoolAddresser { store }); + eh.on::(&mut on_sync); + eh.handle_events(); +} + +/// Merge the changes from the sync events with the create_pool events previously mapped on +/// block_entity_changes. +/// +/// Parameters: +/// - tx_changes: HashMap with the changes for each transaction. This is the same HashMap used in +/// handle_sync +/// - block_entity_changes: The BlockChanges struct that will be updated with the changes from the +/// sync events. +/// This HashMap comes pre-filled with the changes for the create_pool events, mapped in +/// 1_map_pool_created. +/// +/// This function is called after the handle_sync function, and it is expected that +/// block_entity_changes will be complete after this function ends. +fn merge_block( + tx_changes: &mut HashMap, PartialChanges>, + block_entity_changes: &mut BlockChanges, +) { + let mut tx_entity_changes_map = HashMap::new(); + + // Add created pools to the tx_changes_map + for change in block_entity_changes + .changes + .clone() + .into_iter() + { + let transaction = change.tx.as_ref().unwrap(); + tx_entity_changes_map + .entry(transaction.hash.clone()) + .and_modify(|c: &mut TransactionChanges| { + c.component_changes + .extend(change.component_changes.clone()); + c.entity_changes + .extend(change.entity_changes.clone()); + }) + .or_insert(change); + } + + // First, iterate through the previously created transactions, extracted from the + // map_pool_created step. If there are sync events for this transaction, add them to the + // block_entity_changes and the corresponding balance changes. + for change in tx_entity_changes_map.values_mut() { + let tx = change + .clone() + .tx + .expect("Transaction not found") + .clone(); + + // If there are sync events for this transaction, add them to the block_entity_changes + if let Some(partial_changes) = tx_changes.remove(&tx.hash) { + change.entity_changes = partial_changes + .clone() + .consolidate_entity_changes(); + change.balance_changes = partial_changes + .balance_changes + .into_values() + .collect(); + } + } + + // If there are any transactions left in the tx_changes, it means that they are transactions + // that changed the state of the pools, but were not included in the block_entity_changes. + // This happens for every regular transaction that does not actually create a pool. By the + // end of this function, we expect block_entity_changes to be up-to-date with the changes + // for all sync and new_pools in the block. + for partial_changes in tx_changes.values() { + tx_entity_changes_map.insert( + partial_changes.transaction.hash.clone(), + TransactionChanges { + tx: Some(partial_changes.transaction.clone()), + contract_changes: vec![], + entity_changes: partial_changes + .clone() + .consolidate_entity_changes(), + balance_changes: partial_changes + .balance_changes + .clone() + .into_values() + .collect(), + component_changes: vec![], + }, + ); + } + + block_entity_changes.changes = tx_entity_changes_map + .into_values() + .collect(); +} diff --git a/substreams/ethereum-uniswap-v2/src/modules/mod.rs b/substreams/ethereum-uniswap-v2/src/modules/mod.rs new file mode 100644 index 0000000..ccdf7ed --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/modules/mod.rs @@ -0,0 +1,11 @@ +pub use map_pool_created::map_pools_created; +pub use map_pool_events::map_pool_events; +pub use store_pools::store_pools; + +#[path = "1_map_pool_created.rs"] +mod map_pool_created; +#[path = "2_store_pools.rs"] +mod store_pools; + +#[path = "3_map_pool_events.rs"] +mod map_pool_events; diff --git a/substreams/ethereum-uniswap-v2/src/pb/mod.rs b/substreams/ethereum-uniswap-v2/src/pb/mod.rs new file mode 100644 index 0000000..f868620 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/pb/mod.rs @@ -0,0 +1,17 @@ +// @generated +pub mod tycho { + pub mod evm { + pub mod uniswap { + // @@protoc_insertion_point(attribute:tycho.evm.uniswap.v2) + pub mod v2 { + include!("tycho.evm.uniswap.v2.rs"); + // @@protoc_insertion_point(tycho.evm.uniswap.v2) + } + } + // @@protoc_insertion_point(attribute:tycho.evm.v1) + pub mod v1 { + include!("tycho.evm.v1.rs"); + // @@protoc_insertion_point(tycho.evm.v1) + } + } +} diff --git a/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.uniswap.v2.rs b/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.uniswap.v2.rs new file mode 100644 index 0000000..c814ee4 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.uniswap.v2.rs @@ -0,0 +1,99 @@ +// @generated +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Pools { + #[prost(message, repeated, tag="1")] + pub pools: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Pool { + #[prost(bytes="vec", tag="1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(bytes="vec", tag="2")] + pub token0: ::prost::alloc::vec::Vec, + #[prost(bytes="vec", tag="3")] + pub token1: ::prost::alloc::vec::Vec, + #[prost(bytes="vec", tag="4")] + pub created_tx_hash: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Events { + #[prost(message, repeated, tag="1")] + pub events: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Event { + #[prost(string, tag="100")] + pub hash: ::prost::alloc::string::String, + #[prost(uint32, tag="101")] + pub log_index: u32, + #[prost(uint64, tag="102")] + pub log_ordinal: u64, + #[prost(string, tag="103")] + pub to: ::prost::alloc::string::String, + #[prost(string, tag="104")] + pub from: ::prost::alloc::string::String, + #[prost(uint64, tag="105")] + pub block_number: u64, + #[prost(uint64, tag="106")] + pub timestamp: u64, + #[prost(string, tag="107")] + pub pool: ::prost::alloc::string::String, + #[prost(oneof="event::Type", tags="10, 20, 30, 40")] + pub r#type: ::core::option::Option, +} +/// Nested message and enum types in `Event`. +pub mod event { + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Type { + #[prost(message, tag="10")] + DepositType(super::DepositEvent), + #[prost(message, tag="20")] + WithdrawType(super::WithdrawEvent), + #[prost(message, tag="30")] + SyncType(super::SyncEvent), + #[prost(message, tag="40")] + SwapType(super::SwapEvent), + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DepositEvent { + #[prost(string, repeated, tag="1")] + pub input_token_amounts: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, optional, tag="2")] + pub output_token_amount: ::core::option::Option<::prost::alloc::string::String>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WithdrawEvent { + #[prost(string, repeated, tag="1")] + pub input_token_amounts: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, optional, tag="2")] + pub output_token_amount: ::core::option::Option<::prost::alloc::string::String>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SyncEvent { + #[prost(string, tag="1")] + pub reserve0: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub reserve1: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SwapEvent { + #[prost(string, tag="1")] + pub token_in: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub amount_in: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub token_out: ::prost::alloc::string::String, + #[prost(string, tag="4")] + pub amount_out: ::prost::alloc::string::String, +} +// @@protoc_insertion_point(module) diff --git a/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.v1.rs b/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.v1.rs new file mode 100644 index 0000000..6dac022 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.v1.rs @@ -0,0 +1,307 @@ +// @generated +// This file contains the proto definitions for Substreams common to all integrations. + +/// A struct describing a block. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Block { + /// The blocks hash. + #[prost(bytes="vec", tag="1")] + pub hash: ::prost::alloc::vec::Vec, + /// The parent blocks hash. + #[prost(bytes="vec", tag="2")] + pub parent_hash: ::prost::alloc::vec::Vec, + /// The block number. + #[prost(uint64, tag="3")] + pub number: u64, + /// The block timestamp. + #[prost(uint64, tag="4")] + pub ts: u64, +} +/// A struct describing a transaction. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Transaction { + /// The transaction hash. + #[prost(bytes="vec", tag="1")] + pub hash: ::prost::alloc::vec::Vec, + /// The sender of the transaction. + #[prost(bytes="vec", tag="2")] + pub from: ::prost::alloc::vec::Vec, + /// The receiver of the transaction. + #[prost(bytes="vec", tag="3")] + pub to: ::prost::alloc::vec::Vec, + /// The transactions index within the block. + #[prost(uint64, tag="4")] + pub index: u64, +} +/// A custom struct representing an arbitrary attribute of a protocol component. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Attribute { + /// The name of the attribute. + #[prost(string, tag="1")] + pub name: ::prost::alloc::string::String, + /// The value of the attribute. + #[prost(bytes="vec", tag="2")] + pub value: ::prost::alloc::vec::Vec, + /// The type of change the attribute underwent. + #[prost(enumeration="ChangeType", tag="3")] + pub change: i32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProtocolType { + #[prost(string, tag="1")] + pub name: ::prost::alloc::string::String, + #[prost(enumeration="FinancialType", tag="2")] + pub financial_type: i32, + #[prost(message, repeated, tag="3")] + pub attribute_schema: ::prost::alloc::vec::Vec, + #[prost(enumeration="ImplementationType", tag="4")] + pub implementation_type: i32, +} +/// A struct describing a part of the protocol. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProtocolComponent { + /// A unique identifier for the component within the protocol. + /// Can be a stringified address or a string describing the trading pair. + #[prost(string, tag="1")] + pub id: ::prost::alloc::string::String, + /// Addresses of the ERC20 tokens used by the component. + #[prost(bytes="vec", repeated, tag="2")] + pub tokens: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + /// Addresses of the contracts used by the component. + #[prost(bytes="vec", repeated, tag="3")] + pub contracts: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + /// Attributes of the component. + /// The inner ChangeType of the attribute has to match the ChangeType of the ProtocolComponent. + #[prost(message, repeated, tag="4")] + pub static_att: ::prost::alloc::vec::Vec, + /// Type of change the component underwent. + #[prost(enumeration="ChangeType", tag="5")] + pub change: i32, + /// / Represents the functionality of the component. + #[prost(message, optional, tag="6")] + pub protocol_type: ::core::option::Option, + /// Transaction where this component was created + #[prost(message, optional, tag="7")] + pub tx: ::core::option::Option, +} +/// A struct for following the changes of Total Value Locked (TVL) of a protocol component. +/// Note that if the ProtocolComponent contains multiple contracts, the TVL is tracked for the component as a whole. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BalanceChange { + /// The address of the ERC20 token whose balance changed. + #[prost(bytes="vec", tag="1")] + pub token: ::prost::alloc::vec::Vec, + /// The new balance of the token. + #[prost(bytes="vec", tag="2")] + pub balance: ::prost::alloc::vec::Vec, + /// The id of the component whose TVL is tracked. Note: This MUST be utf8 encoded. + #[prost(bytes="vec", tag="3")] + pub component_id: ::prost::alloc::vec::Vec, +} +// Native entities + +/// A component is a set of attributes that are associated with a custom entity. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EntityChanges { + /// A unique identifier of the entity within the protocol. + #[prost(string, tag="1")] + pub component_id: ::prost::alloc::string::String, + /// The set of attributes that are associated with the entity. + #[prost(message, repeated, tag="2")] + pub attributes: ::prost::alloc::vec::Vec, +} +// VM entities + +/// A key value entry into contract storage. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContractSlot { + /// A contract's storage slot. + #[prost(bytes="vec", tag="2")] + pub slot: ::prost::alloc::vec::Vec, + /// The new value for this storage slot. + #[prost(bytes="vec", tag="3")] + pub value: ::prost::alloc::vec::Vec, +} +/// Changes made to a single contract's state. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContractChange { + /// The contract's address + #[prost(bytes="vec", tag="1")] + pub address: ::prost::alloc::vec::Vec, + /// The new balance of the contract, empty bytes indicates no change. + #[prost(bytes="vec", tag="2")] + pub balance: ::prost::alloc::vec::Vec, + /// The new code of the contract, empty bytes indicates no change. + #[prost(bytes="vec", tag="3")] + pub code: ::prost::alloc::vec::Vec, + /// The changes to this contract's slots, empty sequence indicates no change. + #[prost(message, repeated, tag="4")] + pub slots: ::prost::alloc::vec::Vec, + /// Whether this is an update, a creation or a deletion. + #[prost(enumeration="ChangeType", tag="5")] + pub change: i32, +} +// Aggregate entities + +/// A set of changes aggregated by transaction. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionChanges { + /// The transaction instance that results in the changes. + #[prost(message, optional, tag="1")] + pub tx: ::core::option::Option, + /// Contains the changes induced by the above transaction, aggregated on a per-contract basis. + /// Contains the contract changes induced by the above transaction, usually for tracking VM components. + #[prost(message, repeated, tag="2")] + pub contract_changes: ::prost::alloc::vec::Vec, + /// Contains the entity changes induced by the above transaction. + /// Usually for tracking native components or used for VM extensions (plugins). + #[prost(message, repeated, tag="3")] + pub entity_changes: ::prost::alloc::vec::Vec, + /// An array of newly added components. + #[prost(message, repeated, tag="4")] + pub component_changes: ::prost::alloc::vec::Vec, + /// An array of balance changes to components. + #[prost(message, repeated, tag="5")] + pub balance_changes: ::prost::alloc::vec::Vec, +} +/// A set of transaction changes within a single block. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockChanges { + /// The block for which these changes are collectively computed. + #[prost(message, optional, tag="1")] + pub block: ::core::option::Option, + /// The set of transaction changes observed in the specified block. + #[prost(message, repeated, tag="2")] + pub changes: ::prost::alloc::vec::Vec, +} +/// Enum to specify the type of a change. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ChangeType { + Unspecified = 0, + Update = 1, + Creation = 2, + Deletion = 3, +} +impl ChangeType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ChangeType::Unspecified => "CHANGE_TYPE_UNSPECIFIED", + ChangeType::Update => "CHANGE_TYPE_UPDATE", + ChangeType::Creation => "CHANGE_TYPE_CREATION", + ChangeType::Deletion => "CHANGE_TYPE_DELETION", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CHANGE_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "CHANGE_TYPE_UPDATE" => Some(Self::Update), + "CHANGE_TYPE_CREATION" => Some(Self::Creation), + "CHANGE_TYPE_DELETION" => Some(Self::Deletion), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum FinancialType { + Swap = 0, + Lend = 1, + Leverage = 2, + Psm = 3, +} +impl FinancialType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + FinancialType::Swap => "SWAP", + FinancialType::Lend => "LEND", + FinancialType::Leverage => "LEVERAGE", + FinancialType::Psm => "PSM", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SWAP" => Some(Self::Swap), + "LEND" => Some(Self::Lend), + "LEVERAGE" => Some(Self::Leverage), + "PSM" => Some(Self::Psm), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ImplementationType { + Vm = 0, + Custom = 1, +} +impl ImplementationType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ImplementationType::Vm => "VM", + ImplementationType::Custom => "CUSTOM", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "VM" => Some(Self::Vm), + "CUSTOM" => Some(Self::Custom), + _ => None, + } + } +} +// WARNING: DEPRECATED. Please use common.proto's TransactionChanges and BlockChanges instead. +// This file contains the definition for the native integration of Substreams. + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionEntityChanges { + #[prost(message, optional, tag="1")] + pub tx: ::core::option::Option, + #[prost(message, repeated, tag="2")] + pub entity_changes: ::prost::alloc::vec::Vec, + /// An array of newly added components. + #[prost(message, repeated, tag="3")] + pub component_changes: ::prost::alloc::vec::Vec, + /// An array of balance changes to components. + #[prost(message, repeated, tag="4")] + pub balance_changes: ::prost::alloc::vec::Vec, +} +/// A set of transaction changes within a single block. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockEntityChanges { + /// The block for which these changes are collectively computed. + #[prost(message, optional, tag="1")] + pub block: ::core::option::Option, + /// The set of transaction changes observed in the specified block. + #[prost(message, repeated, tag="2")] + pub changes: ::prost::alloc::vec::Vec, +} +// @@protoc_insertion_point(module) diff --git a/substreams/ethereum-uniswap-v2/src/store_key.rs b/substreams/ethereum-uniswap-v2/src/store_key.rs new file mode 100644 index 0000000..f4ea140 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/store_key.rs @@ -0,0 +1,16 @@ +#[derive(Clone)] +pub enum StoreKey { + Pool, +} + +impl StoreKey { + pub fn get_unique_pool_key(&self, key: &str) -> String { + format!("{}:{}", self.unique_id(), key) + } + + pub fn unique_id(&self) -> String { + match self { + StoreKey::Pool => "Pool".to_string(), + } + } +} diff --git a/substreams/ethereum-uniswap-v2/src/traits.rs b/substreams/ethereum-uniswap-v2/src/traits.rs new file mode 100644 index 0000000..9edd693 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/src/traits.rs @@ -0,0 +1,51 @@ +use ethabi::ethereum_types::Address; +use substreams::store::{StoreGet, StoreGetProto}; +use substreams_ethereum::pb::eth::v2::{self as eth}; + +use substreams_helper::{common::HasAddresser, hex::Hexable}; + +use crate::{ + pb::tycho::evm::v1::{Block, ProtocolComponent, Transaction}, + store_key::StoreKey, +}; + +pub struct PoolAddresser<'a> { + pub store: &'a StoreGetProto, +} + +impl<'a> HasAddresser for PoolAddresser<'a> { + fn has_address(&self, key: Address) -> bool { + let pool = self + .store + .get_last(StoreKey::Pool.get_unique_pool_key(&key.to_hex())); + + pool.is_some() + } +} + +impl From for Block { + fn from(block: eth::Block) -> Self { + Self { + hash: block.hash.clone(), + parent_hash: block + .header + .as_ref() + .expect("Block header not present") + .parent_hash + .clone(), + number: block.number, + ts: block.timestamp_seconds(), + } + } +} + +impl From<ð::TransactionTrace> for Transaction { + fn from(tx: ð::TransactionTrace) -> Self { + Self { + hash: tx.hash.clone(), + from: tx.from.clone(), + to: tx.to.clone(), + index: tx.index.into(), + } + } +} diff --git a/substreams/ethereum-uniswap-v3/Cargo.toml b/substreams/ethereum-uniswap-v3/Cargo.toml new file mode 100644 index 0000000..e4c9359 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "substreams-ethereum-uniswap-v3" +version = "0.2.0" +edition = "2021" + +[lib] +name = "substreams_ethereum_uniswap_v3" +crate-type = ["cdylib"] + +[dependencies] +substreams.workspace = true +substreams-ethereum.workspace = true +prost.workspace = true +ethabi.workspace = true +anyhow = { workspace = true, features = [] } +hex-literal.workspace = true +substreams-helper.workspace = true +num-bigint = "0.4.4" +hex.workspace = true +tiny-keccak = "2.0" +substreams-entity-change = "1.3" + +[target.wasm32-unknown-unknown.dependencies] +getrandom = { version = "0.2", features = ["custom"] } + +[build-dependencies] +anyhow.workspace = true +substreams-ethereum.workspace = true diff --git a/substreams/ethereum-uniswap-v3/Makefile b/substreams/ethereum-uniswap-v3/Makefile new file mode 100644 index 0000000..a6fcb34 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/Makefile @@ -0,0 +1,2 @@ +build: + cargo build --target wasm32-unknown-unknown --profile substreams \ No newline at end of file diff --git a/substreams/ethereum-uniswap-v3/abi/Factory.json b/substreams/ethereum-uniswap-v3/abi/Factory.json new file mode 100644 index 0000000..633c8d5 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/abi/Factory.json @@ -0,0 +1,198 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "FeeAmountEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "indexed": false, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolCreated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "createPool", + "outputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "enableFeeAmount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "feeAmountTickSpacing", + "outputs": [ + { + "internalType": "int24", + "name": "", + "type": "int24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "getPool", + "outputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/substreams/ethereum-uniswap-v3/abi/Pool.json b/substreams/ethereum-uniswap-v3/abi/Pool.json new file mode 100644 index 0000000..6dd2066 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/abi/Pool.json @@ -0,0 +1,988 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "name": "Collect", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "name": "CollectProtocol", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "paid0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "paid1", + "type": "uint256" + } + ], + "name": "Flash", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint16", + "name": "observationCardinalityNextOld", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "observationCardinalityNextNew", + "type": "uint16" + } + ], + "name": "IncreaseObservationCardinalityNext", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Initialize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol0Old", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol1Old", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol0New", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "feeProtocol1New", + "type": "uint8" + } + ], + "name": "SetFeeProtocol", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount0", + "type": "int256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount1", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Swap", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount0Requested", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Requested", + "type": "uint128" + } + ], + "name": "collect", + "outputs": [ + { + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint128", + "name": "amount0Requested", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Requested", + "type": "uint128" + } + ], + "name": "collectProtocol", + "outputs": [ + { + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "fee", + "outputs": [ + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeGrowthGlobal0X128", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeGrowthGlobal1X128", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "flash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + } + ], + "name": "increaseObservationCardinalityNext", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "liquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxLiquidityPerTick", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "observations", + "outputs": [ + { + "internalType": "uint32", + "name": "blockTimestamp", + "type": "uint32" + }, + { + "internalType": "int56", + "name": "tickCumulative", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityCumulativeX128", + "type": "uint160" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32[]", + "name": "secondsAgos", + "type": "uint32[]" + } + ], + "name": "observe", + "outputs": [ + { + "internalType": "int56[]", + "name": "tickCumulatives", + "type": "int56[]" + }, + { + "internalType": "uint160[]", + "name": "secondsPerLiquidityCumulativeX128s", + "type": "uint160[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "uint128", + "name": "_liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "tokensOwed0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "tokensOwed1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolFees", + "outputs": [ + { + "internalType": "uint128", + "name": "token0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "token1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "feeProtocol0", + "type": "uint8" + }, + { + "internalType": "uint8", + "name": "feeProtocol1", + "type": "uint8" + } + ], + "name": "setFeeProtocol", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "slot0", + "outputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint16", + "name": "observationIndex", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinality", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "feeProtocol", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "unlocked", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + } + ], + "name": "snapshotCumulativesInside", + "outputs": [ + { + "internalType": "int56", + "name": "tickCumulativeInside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityInsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsInside", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "int256", + "name": "amountSpecified", + "type": "int256" + }, + { + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "int256", + "name": "amount0", + "type": "int256" + }, + { + "internalType": "int256", + "name": "amount1", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int16", + "name": "wordPosition", + "type": "int16" + } + ], + "name": "tickBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tickSpacing", + "outputs": [ + { + "internalType": "int24", + "name": "", + "type": "int24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "ticks", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside1X128", + "type": "uint256" + }, + { + "internalType": "int56", + "name": "tickCumulativeOutside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityOutsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsOutside", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token0", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token1", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml b/substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml new file mode 100644 index 0000000..1d3f0f7 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml @@ -0,0 +1,67 @@ +specVersion: v0.1.0 +package: + name: "substreams_arbitrum_uniswap_v3" + version: v0.2.0 + +protobuf: + files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/entity.proto + - uniswap.proto + importPaths: + - ./proto/v1 + - ../../proto/ + +binaries: + default: + type: wasm/rust-v1 + file: ../../target/wasm32-unknown-unknown/substreams/substreams_ethereum_uniswap_v3.wasm + +modules: + - name: map_pools_created + kind: map + initialBlock: 37418321 + inputs: + - params: string + - source: sf.ethereum.type.v2.Block + output: + type: proto:tycho.evm.v1.BlockChanges + + - name: store_pools + kind: store + initialBlock: 37418321 + updatePolicy: set_if_not_exists + valueType: proto:uniswap.v3.Pool + inputs: + - map: map_pools_created + + - name: map_balance_changes + kind: map + initialBlock: 37418321 + inputs: + - source: sf.ethereum.type.v2.Block + - store: store_pools + output: + type: proto:tycho.evm.uniswap.v3.BalanceDeltas + + - name: store_pools_balances + kind: store + initialBlock: 37418321 + updatePolicy: add + valueType: bigint + inputs: + - map: map_balance_changes + + - name: map_pool_events + kind: map + initialBlock: 37418321 + inputs: + - source: sf.ethereum.type.v2.Block + - map: map_pools_created + - store: store_pools + - store: store_pools_balances + output: + type: proto:tycho.evm.v1.BlockChanges + +params: + map_pools_created: "1F98431c8aD98523631AE4a59f267346ea31F984" diff --git a/substreams/ethereum-uniswap-v3/build.rs b/substreams/ethereum-uniswap-v3/build.rs new file mode 100644 index 0000000..7992bb0 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/build.rs @@ -0,0 +1,12 @@ +use anyhow::{Ok, Result}; +use substreams_ethereum::Abigen; + +fn main() -> Result<(), anyhow::Error> { + Abigen::new("Factory", "abi/Factory.json")? + .generate()? + .write_to_file("src/abi/factory.rs")?; + Abigen::new("Pool", "abi/Pool.json")? + .generate()? + .write_to_file("src/abi/pool.rs")?; + Ok(()) +} diff --git a/substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml b/substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml new file mode 100644 index 0000000..aeda329 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml @@ -0,0 +1,67 @@ +specVersion: v0.1.0 +package: + name: "substreams_ethereum_uniswap_v3" + version: v0.2.0 + +protobuf: + files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/entity.proto + - uniswap.proto + importPaths: + - ./proto/v1 + - ../../proto/ + +binaries: + default: + type: wasm/rust-v1 + file: ../../target/wasm32-unknown-unknown/substreams/substreams_ethereum_uniswap_v3.wasm + +modules: + - name: map_pools_created + kind: map + initialBlock: 12369621 + inputs: + - params: string + - source: sf.ethereum.type.v2.Block + output: + type: proto:tycho.evm.v1.BlockChanges + + - name: store_pools + kind: store + initialBlock: 12369621 + updatePolicy: set_if_not_exists + valueType: proto:uniswap.v3.Pool + inputs: + - map: map_pools_created + + - name: map_balance_changes + kind: map + initialBlock: 12369621 + inputs: + - source: sf.ethereum.type.v2.Block + - store: store_pools + output: + type: proto:tycho.evm.uniswap.v3.BalanceDeltas + + - name: store_pools_balances + kind: store + initialBlock: 12369621 + updatePolicy: add + valueType: bigint + inputs: + - map: map_balance_changes + + - name: map_pool_events + kind: map + initialBlock: 12369621 + inputs: + - source: sf.ethereum.type.v2.Block + - map: map_pools_created + - store: store_pools + - store: store_pools_balances + output: + type: proto:tycho.evm.v1.BlockChanges + +params: + map_pools_created: "1F98431c8aD98523631AE4a59f267346ea31F984" diff --git a/substreams/ethereum-uniswap-v3/proto/v1/uniswap.proto b/substreams/ethereum-uniswap-v3/proto/v1/uniswap.proto new file mode 100644 index 0000000..c81955f --- /dev/null +++ b/substreams/ethereum-uniswap-v3/proto/v1/uniswap.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package uniswap.v3; + +message Pool { + bytes address = 1; + bytes token0 = 2; + bytes token1 = 3; + bytes created_tx_hash = 4; +} + +// A change to a pool's balance. +message BalanceDelta { + // The address of the ERC20 token. + bytes token_address = 1; + // The delta of the token. + bytes amount = 2; + // The sign of the delta, true for positive, false for negative. + bool sign = 3; + // The address of the pool whose balance changed. + bytes pool_address = 4; + // Used to determine the order of the balance changes. Necessary for the balance store. + uint64 ordinal = 5; +} + +// A group of BalanceDelta +message BalanceDeltas { + repeated BalanceDelta deltas = 1; +} \ No newline at end of file diff --git a/substreams/ethereum-uniswap-v3/src/abi/factory.rs b/substreams/ethereum-uniswap-v3/src/abi/factory.rs new file mode 100644 index 0000000..19b46de --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/abi/factory.rs @@ -0,0 +1,1040 @@ + const INTERNAL_ERR: &'static str = "`ethabi_derive` internal error"; + /// Contract's functions. + #[allow(dead_code, unused_imports, unused_variables)] + pub mod functions { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct CreatePool { + pub token_a: Vec, + pub token_b: Vec, + pub fee: substreams::scalar::BigInt, + } + impl CreatePool { + const METHOD_ID: [u8; 4] = [161u8, 103u8, 18u8, 149u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Uint(24usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + token_a: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + token_b: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + fee: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.token_a), + ), + ethabi::Token::Address( + ethabi::Address::from_slice(&self.token_b), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.fee.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for CreatePool { + const NAME: &'static str = "createPool"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for CreatePool { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct EnableFeeAmount { + pub fee: substreams::scalar::BigInt, + pub tick_spacing: substreams::scalar::BigInt, + } + impl EnableFeeAmount { + const METHOD_ID: [u8; 4] = [138u8, 124u8, 25u8, 95u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(24usize), + ethabi::ParamType::Int(24usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + fee: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + tick_spacing: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.fee.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + { + let non_full_signed_bytes = self + .tick_spacing + .to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for EnableFeeAmount { + const NAME: &'static str = "enableFeeAmount"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct FeeAmountTickSpacing { + pub fee: substreams::scalar::BigInt, + } + impl FeeAmountTickSpacing { + const METHOD_ID: [u8; 4] = [34u8, 175u8, 204u8, 203u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(24usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + fee: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.fee.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Int(24usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for FeeAmountTickSpacing { + const NAME: &'static str = "feeAmountTickSpacing"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for FeeAmountTickSpacing { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct GetPool { + pub token_a: Vec, + pub token_b: Vec, + pub fee: substreams::scalar::BigInt, + } + impl GetPool { + const METHOD_ID: [u8; 4] = [22u8, 152u8, 238u8, 130u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Address, + ethabi::ParamType::Uint(24usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + token_a: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + token_b: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + fee: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.token_a), + ), + ethabi::Token::Address( + ethabi::Address::from_slice(&self.token_b), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.fee.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for GetPool { + const NAME: &'static str = "getPool"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for GetPool { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Owner {} + impl Owner { + const METHOD_ID: [u8; 4] = [141u8, 165u8, 203u8, 91u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Owner { + const NAME: &'static str = "owner"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Owner { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SetOwner { + pub owner: Vec, + } + impl SetOwner { + const METHOD_ID: [u8; 4] = [19u8, 175u8, 64u8, 53u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + owner: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::Address(ethabi::Address::from_slice(&self.owner))], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for SetOwner { + const NAME: &'static str = "setOwner"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + } + /// Contract's events. + #[allow(dead_code, unused_imports, unused_variables)] + pub mod events { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct FeeAmountEnabled { + pub fee: substreams::scalar::BigInt, + pub tick_spacing: substreams::scalar::BigInt, + } + impl FeeAmountEnabled { + const TOPIC_ID: [u8; 32] = [ + 198u8, + 106u8, + 63u8, + 223u8, + 7u8, + 35u8, + 44u8, + 221u8, + 24u8, + 95u8, + 235u8, + 204u8, + 101u8, + 121u8, + 212u8, + 8u8, + 194u8, + 65u8, + 180u8, + 122u8, + 226u8, + 249u8, + 144u8, + 125u8, + 132u8, + 190u8, + 101u8, + 81u8, + 65u8, + 238u8, + 174u8, + 204u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 0usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Ok(Self { + fee: { + let mut v = [0 as u8; 32]; + ethabi::decode( + &[ethabi::ParamType::Uint(24usize)], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'fee' from topic of type 'uint24': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + tick_spacing: substreams::scalar::BigInt::from_signed_bytes_be( + log.topics[2usize].as_ref(), + ), + }) + } + } + impl substreams_ethereum::Event for FeeAmountEnabled { + const NAME: &'static str = "FeeAmountEnabled"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct OwnerChanged { + pub old_owner: Vec, + pub new_owner: Vec, + } + impl OwnerChanged { + const TOPIC_ID: [u8; 32] = [ + 181u8, + 50u8, + 7u8, + 59u8, + 56u8, + 200u8, + 49u8, + 69u8, + 227u8, + 229u8, + 19u8, + 83u8, + 119u8, + 160u8, + 139u8, + 249u8, + 170u8, + 181u8, + 91u8, + 192u8, + 253u8, + 124u8, + 17u8, + 121u8, + 205u8, + 79u8, + 185u8, + 149u8, + 210u8, + 165u8, + 21u8, + 156u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 0usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Ok(Self { + old_owner: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'old_owner' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + new_owner: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'new_owner' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + } + impl substreams_ethereum::Event for OwnerChanged { + const NAME: &'static str = "OwnerChanged"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct PoolCreated { + pub token0: Vec, + pub token1: Vec, + pub fee: substreams::scalar::BigInt, + pub tick_spacing: substreams::scalar::BigInt, + pub pool: Vec, + } + impl PoolCreated { + const TOPIC_ID: [u8; 32] = [ + 120u8, + 60u8, + 202u8, + 28u8, + 4u8, + 18u8, + 221u8, + 13u8, + 105u8, + 94u8, + 120u8, + 69u8, + 104u8, + 201u8, + 109u8, + 162u8, + 233u8, + 194u8, + 47u8, + 249u8, + 137u8, + 53u8, + 122u8, + 46u8, + 139u8, + 29u8, + 155u8, + 43u8, + 78u8, + 107u8, + 113u8, + 24u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 4usize { + return false; + } + if log.data.len() != 64usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Int(24usize), ethabi::ParamType::Address], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + token0: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'token0' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + token1: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'token1' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + fee: { + let mut v = [0 as u8; 32]; + ethabi::decode( + &[ethabi::ParamType::Uint(24usize)], + log.topics[3usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'fee' from topic of type 'uint24': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + tick_spacing: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + pool: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + }) + } + } + impl substreams_ethereum::Event for PoolCreated { + const NAME: &'static str = "PoolCreated"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + } \ No newline at end of file diff --git a/substreams/ethereum-uniswap-v3/src/abi/mod.rs b/substreams/ethereum-uniswap-v3/src/abi/mod.rs new file mode 100644 index 0000000..f743394 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/abi/mod.rs @@ -0,0 +1,4 @@ +#![allow(clippy::all, clippy::pedantic, clippy::nursery)] + +pub mod factory; +pub mod pool; diff --git a/substreams/ethereum-uniswap-v3/src/abi/pool.rs b/substreams/ethereum-uniswap-v3/src/abi/pool.rs new file mode 100644 index 0000000..1208919 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/abi/pool.rs @@ -0,0 +1,5153 @@ + const INTERNAL_ERR: &'static str = "`ethabi_derive` internal error"; + /// Contract's functions. + #[allow(dead_code, unused_imports, unused_variables)] + pub mod functions { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct Burn { + pub tick_lower: substreams::scalar::BigInt, + pub tick_upper: substreams::scalar::BigInt, + pub amount: substreams::scalar::BigInt, + } + impl Burn { + const METHOD_ID: [u8; 4] = [163u8, 65u8, 35u8, 167u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Uint(128usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + tick_lower: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + tick_upper: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + { + let non_full_signed_bytes = self + .tick_lower + .to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + { + let non_full_signed_bytes = self + .tick_upper + .to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option<(substreams::scalar::BigInt, substreams::scalar::BigInt)> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Burn { + const NAME: &'static str = "burn"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + > for Burn { + fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Collect { + pub recipient: Vec, + pub tick_lower: substreams::scalar::BigInt, + pub tick_upper: substreams::scalar::BigInt, + pub amount0_requested: substreams::scalar::BigInt, + pub amount1_requested: substreams::scalar::BigInt, + } + impl Collect { + const METHOD_ID: [u8; 4] = [79u8, 30u8, 179u8, 216u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Uint(128usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + recipient: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tick_lower: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + tick_upper: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + amount0_requested: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1_requested: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.recipient), + ), + { + let non_full_signed_bytes = self + .tick_lower + .to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + { + let non_full_signed_bytes = self + .tick_upper + .to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount0_requested.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount1_requested.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Uint(128usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option<(substreams::scalar::BigInt, substreams::scalar::BigInt)> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Collect { + const NAME: &'static str = "collect"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + > for Collect { + fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct CollectProtocol { + pub recipient: Vec, + pub amount0_requested: substreams::scalar::BigInt, + pub amount1_requested: substreams::scalar::BigInt, + } + impl CollectProtocol { + const METHOD_ID: [u8; 4] = [133u8, 182u8, 103u8, 41u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Uint(128usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + recipient: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0_requested: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1_requested: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.recipient), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount0_requested.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount1_requested.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Uint(128usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option<(substreams::scalar::BigInt, substreams::scalar::BigInt)> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for CollectProtocol { + const NAME: &'static str = "collectProtocol"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + > for CollectProtocol { + fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Factory {} + impl Factory { + const METHOD_ID: [u8; 4] = [196u8, 90u8, 1u8, 85u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Factory { + const NAME: &'static str = "factory"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Factory { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Fee {} + impl Fee { + const METHOD_ID: [u8; 4] = [221u8, 202u8, 63u8, 67u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(24usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Fee { + const NAME: &'static str = "fee"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable for Fee { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct FeeGrowthGlobal0X128 {} + impl FeeGrowthGlobal0X128 { + const METHOD_ID: [u8; 4] = [243u8, 5u8, 131u8, 153u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for FeeGrowthGlobal0X128 { + const NAME: &'static str = "feeGrowthGlobal0X128"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for FeeGrowthGlobal0X128 { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct FeeGrowthGlobal1X128 {} + impl FeeGrowthGlobal1X128 { + const METHOD_ID: [u8; 4] = [70u8, 20u8, 19u8, 25u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for FeeGrowthGlobal1X128 { + const NAME: &'static str = "feeGrowthGlobal1X128"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for FeeGrowthGlobal1X128 { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Flash { + pub recipient: Vec, + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + pub data: Vec, + } + impl Flash { + const METHOD_ID: [u8; 4] = [73u8, 14u8, 108u8, 188u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Bytes, + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + recipient: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + data: values + .pop() + .expect(INTERNAL_ERR) + .into_bytes() + .expect(INTERNAL_ERR), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.recipient), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount0.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount1.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Bytes(self.data.clone()), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Flash { + const NAME: &'static str = "flash"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct IncreaseObservationCardinalityNext { + pub observation_cardinality_next: substreams::scalar::BigInt, + } + impl IncreaseObservationCardinalityNext { + const METHOD_ID: [u8; 4] = [50u8, 20u8, 143u8, 103u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(16usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + observation_cardinality_next: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self + .observation_cardinality_next + .clone() + .to_bytes_be() + { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for IncreaseObservationCardinalityNext { + const NAME: &'static str = "increaseObservationCardinalityNext"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Initialize { + pub sqrt_price_x96: substreams::scalar::BigInt, + } + impl Initialize { + const METHOD_ID: [u8; 4] = [246u8, 55u8, 115u8, 29u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(160usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + sqrt_price_x96: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.sqrt_price_x96.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for Initialize { + const NAME: &'static str = "initialize"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Liquidity {} + impl Liquidity { + const METHOD_ID: [u8; 4] = [26u8, 104u8, 101u8, 2u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(128usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Liquidity { + const NAME: &'static str = "liquidity"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for Liquidity { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct MaxLiquidityPerTick {} + impl MaxLiquidityPerTick { + const METHOD_ID: [u8; 4] = [112u8, 207u8, 117u8, 74u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(128usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for MaxLiquidityPerTick { + const NAME: &'static str = "maxLiquidityPerTick"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for MaxLiquidityPerTick { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Mint { + pub recipient: Vec, + pub tick_lower: substreams::scalar::BigInt, + pub tick_upper: substreams::scalar::BigInt, + pub amount: substreams::scalar::BigInt, + pub data: Vec, + } + impl Mint { + const METHOD_ID: [u8; 4] = [60u8, 138u8, 125u8, 141u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Bytes, + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + recipient: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tick_lower: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + tick_upper: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + data: values + .pop() + .expect(INTERNAL_ERR) + .into_bytes() + .expect(INTERNAL_ERR), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.recipient), + ), + { + let non_full_signed_bytes = self + .tick_lower + .to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + { + let non_full_signed_bytes = self + .tick_upper + .to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.amount.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Bytes(self.data.clone()), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option<(substreams::scalar::BigInt, substreams::scalar::BigInt)> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Mint { + const NAME: &'static str = "mint"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + > for Mint { + fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Observations { + pub index: substreams::scalar::BigInt, + } + impl Observations { + const METHOD_ID: [u8; 4] = [37u8, 44u8, 9u8, 215u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + index: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.index.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(32usize), + ethabi::ParamType::Int(56usize), + ethabi::ParamType::Uint(160usize), + ethabi::ParamType::Bool, + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + values.pop().expect(INTERNAL_ERR).into_bool().expect(INTERNAL_ERR), + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + > { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Observations { + const NAME: &'static str = "observations"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + > for Observations { + fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Observe { + pub seconds_agos: Vec, + } + impl Observe { + const METHOD_ID: [u8; 4] = [136u8, 59u8, 219u8, 253u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Array( + Box::new(ethabi::ParamType::Uint(32usize)), + ), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + seconds_agos: values + .pop() + .expect(INTERNAL_ERR) + .into_array() + .expect(INTERNAL_ERR) + .into_iter() + .map(|inner| { + let mut v = [0 as u8; 32]; + inner + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + .collect(), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + { + let v = self + .seconds_agos + .iter() + .map(|inner| ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match inner.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + )) + .collect(); + ethabi::Token::Array(v) + }, + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + (Vec, Vec), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + (Vec, Vec), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Array( + Box::new(ethabi::ParamType::Int(56usize)), + ), + ethabi::ParamType::Array( + Box::new(ethabi::ParamType::Uint(160usize)), + ), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + values + .pop() + .expect(INTERNAL_ERR) + .into_array() + .expect(INTERNAL_ERR) + .into_iter() + .map(|inner| { + let mut v = [0 as u8; 32]; + inner + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }) + .collect(), + values + .pop() + .expect(INTERNAL_ERR) + .into_array() + .expect(INTERNAL_ERR) + .into_iter() + .map(|inner| { + let mut v = [0 as u8; 32]; + inner + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + .collect(), + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option< + (Vec, Vec), + > { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Observe { + const NAME: &'static str = "observe"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + (Vec, Vec), + > for Observe { + fn output( + data: &[u8], + ) -> Result< + (Vec, Vec), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Positions { + pub key: [u8; 32usize], + } + impl Positions { + const METHOD_ID: [u8; 4] = [81u8, 78u8, 164u8, 191u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::FixedBytes(32usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + key: { + let mut result = [0u8; 32]; + let v = values + .pop() + .expect(INTERNAL_ERR) + .into_fixed_bytes() + .expect(INTERNAL_ERR); + result.copy_from_slice(&v); + result + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ethabi::Token::FixedBytes(self.key.as_ref().to_vec())], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Uint(128usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + > { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Positions { + const NAME: &'static str = "positions"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + > for Positions { + fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct ProtocolFees {} + impl ProtocolFees { + const METHOD_ID: [u8; 4] = [26u8, 216u8, 176u8, 59u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Uint(128usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option<(substreams::scalar::BigInt, substreams::scalar::BigInt)> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for ProtocolFees { + const NAME: &'static str = "protocolFees"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + > for ProtocolFees { + fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SetFeeProtocol { + pub fee_protocol0: substreams::scalar::BigInt, + pub fee_protocol1: substreams::scalar::BigInt, + } + impl SetFeeProtocol { + const METHOD_ID: [u8; 4] = [130u8, 6u8, 164u8, 209u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(8usize), + ethabi::ParamType::Uint(8usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + fee_protocol0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + fee_protocol1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.fee_protocol0.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.fee_protocol1.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + } + impl substreams_ethereum::Function for SetFeeProtocol { + const NAME: &'static str = "setFeeProtocol"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Slot0 {} + impl Slot0 { + const METHOD_ID: [u8; 4] = [56u8, 80u8, 199u8, 189u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(160usize), + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Uint(16usize), + ethabi::ParamType::Uint(16usize), + ethabi::ParamType::Uint(16usize), + ethabi::ParamType::Uint(8usize), + ethabi::ParamType::Bool, + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + values.pop().expect(INTERNAL_ERR).into_bool().expect(INTERNAL_ERR), + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + > { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Slot0 { + const NAME: &'static str = "slot0"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + > for Slot0 { + fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SnapshotCumulativesInside { + pub tick_lower: substreams::scalar::BigInt, + pub tick_upper: substreams::scalar::BigInt, + } + impl SnapshotCumulativesInside { + const METHOD_ID: [u8; 4] = [163u8, 136u8, 7u8, 242u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Int(24usize), + ethabi::ParamType::Int(24usize), + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + tick_lower: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + tick_upper: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + { + let non_full_signed_bytes = self + .tick_lower + .to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + { + let non_full_signed_bytes = self + .tick_upper + .to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Int(56usize), + ethabi::ParamType::Uint(160usize), + ethabi::ParamType::Uint(32usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + > { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for SnapshotCumulativesInside { + const NAME: &'static str = "snapshotCumulativesInside"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + > for SnapshotCumulativesInside { + fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + ), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Swap { + pub recipient: Vec, + pub zero_for_one: bool, + pub amount_specified: substreams::scalar::BigInt, + pub sqrt_price_limit_x96: substreams::scalar::BigInt, + pub data: Vec, + } + impl Swap { + const METHOD_ID: [u8; 4] = [18u8, 138u8, 203u8, 8u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Bool, + ethabi::ParamType::Int(256usize), + ethabi::ParamType::Uint(160usize), + ethabi::ParamType::Bytes, + ], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + recipient: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + zero_for_one: values + .pop() + .expect(INTERNAL_ERR) + .into_bool() + .expect(INTERNAL_ERR), + amount_specified: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + sqrt_price_limit_x96: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + data: values + .pop() + .expect(INTERNAL_ERR) + .into_bytes() + .expect(INTERNAL_ERR), + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + ethabi::Token::Address( + ethabi::Address::from_slice(&self.recipient), + ), + ethabi::Token::Bool(self.zero_for_one.clone()), + { + let non_full_signed_bytes = self + .amount_specified + .to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + ethabi::Token::Uint( + ethabi::Uint::from_big_endian( + match self.sqrt_price_limit_x96.clone().to_bytes_be() { + (num_bigint::Sign::Plus, bytes) => bytes, + (num_bigint::Sign::NoSign, bytes) => bytes, + (num_bigint::Sign::Minus, _) => { + panic!("negative numbers are not supported") + } + } + .as_slice(), + ), + ), + ethabi::Token::Bytes(self.data.clone()), + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Int(256usize), + ethabi::ParamType::Int(256usize), + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option<(substreams::scalar::BigInt, substreams::scalar::BigInt)> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Swap { + const NAME: &'static str = "swap"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + > for Swap { + fn output( + data: &[u8], + ) -> Result< + (substreams::scalar::BigInt, substreams::scalar::BigInt), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct TickBitmap { + pub word_position: substreams::scalar::BigInt, + } + impl TickBitmap { + const METHOD_ID: [u8; 4] = [83u8, 57u8, 194u8, 150u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Int(16usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + word_position: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + { + let non_full_signed_bytes = self + .word_position + .to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Uint(256usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for TickBitmap { + const NAME: &'static str = "tickBitmap"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for TickBitmap { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct TickSpacing {} + impl TickSpacing { + const METHOD_ID: [u8; 4] = [208u8, 201u8, 58u8, 124u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result { + let mut values = ethabi::decode( + &[ethabi::ParamType::Int(24usize)], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok({ + let mut v = [0 as u8; 32]; + values + .pop() + .expect("one output data should have existed") + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for TickSpacing { + const NAME: &'static str = "tickSpacing"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable + for TickSpacing { + fn output(data: &[u8]) -> Result { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Ticks { + pub tick: substreams::scalar::BigInt, + } + impl Ticks { + const METHOD_ID: [u8; 4] = [243u8, 13u8, 186u8, 147u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + let maybe_data = call.input.get(4..); + if maybe_data.is_none() { + return Err("no data to decode".to_string()); + } + let mut values = ethabi::decode( + &[ethabi::ParamType::Int(24usize)], + maybe_data.unwrap(), + ) + .map_err(|e| format!("unable to decode call.input: {:?}", e))?; + values.reverse(); + Ok(Self { + tick: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + }) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode( + &[ + { + let non_full_signed_bytes = self.tick.to_signed_bytes_be(); + let full_signed_bytes_init = if non_full_signed_bytes[0] + & 0x80 == 0x80 + { + 0xff + } else { + 0x00 + }; + let mut full_signed_bytes = [full_signed_bytes_init + as u8; 32]; + non_full_signed_bytes + .into_iter() + .rev() + .enumerate() + .for_each(|(i, byte)| full_signed_bytes[31 - i] = byte); + ethabi::Token::Int( + ethabi::Int::from_big_endian(full_signed_bytes.as_ref()), + ) + }, + ], + ); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + String, + > { + Self::output(call.return_data.as_ref()) + } + pub fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + String, + > { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Int(128usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Int(56usize), + ethabi::ParamType::Uint(160usize), + ethabi::ParamType::Uint(32usize), + ethabi::ParamType::Bool, + ], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + values.reverse(); + Ok(( + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + values.pop().expect(INTERNAL_ERR).into_bool().expect(INTERNAL_ERR), + )) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call( + &self, + address: Vec, + ) -> Option< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + > { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Ticks { + const NAME: &'static str = "ticks"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + > for Ticks { + fn output( + data: &[u8], + ) -> Result< + ( + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + substreams::scalar::BigInt, + bool, + ), + String, + > { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Token0 {} + impl Token0 { + const METHOD_ID: [u8; 4] = [13u8, 254u8, 22u8, 129u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Token0 { + const NAME: &'static str = "token0"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Token0 { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Token1 {} + impl Token1 { + const METHOD_ID: [u8; 4] = [210u8, 18u8, 32u8, 167u8]; + pub fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Ok(Self {}) + } + pub fn encode(&self) -> Vec { + let data = ethabi::encode(&[]); + let mut encoded = Vec::with_capacity(4 + data.len()); + encoded.extend(Self::METHOD_ID); + encoded.extend(data); + encoded + } + pub fn output_call( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result, String> { + Self::output(call.return_data.as_ref()) + } + pub fn output(data: &[u8]) -> Result, String> { + let mut values = ethabi::decode( + &[ethabi::ParamType::Address], + data.as_ref(), + ) + .map_err(|e| format!("unable to decode output data: {:?}", e))?; + Ok( + values + .pop() + .expect("one output data should have existed") + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + ) + } + pub fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + match call.input.get(0..4) { + Some(signature) => Self::METHOD_ID == signature, + None => false, + } + } + pub fn call(&self, address: Vec) -> Option> { + use substreams_ethereum::pb::eth::rpc; + let rpc_calls = rpc::RpcCalls { + calls: vec![ + rpc::RpcCall { to_addr : address, data : self.encode(), } + ], + }; + let responses = substreams_ethereum::rpc::eth_call(&rpc_calls).responses; + let response = responses + .get(0) + .expect("one response should have existed"); + if response.failed { + return None; + } + match Self::output(response.raw.as_ref()) { + Ok(data) => Some(data), + Err(err) => { + use substreams_ethereum::Function; + substreams::log::info!( + "Call output for function `{}` failed to decode with error: {}", + Self::NAME, err + ); + None + } + } + } + } + impl substreams_ethereum::Function for Token1 { + const NAME: &'static str = "token1"; + fn match_call(call: &substreams_ethereum::pb::eth::v2::Call) -> bool { + Self::match_call(call) + } + fn decode( + call: &substreams_ethereum::pb::eth::v2::Call, + ) -> Result { + Self::decode(call) + } + fn encode(&self) -> Vec { + self.encode() + } + } + impl substreams_ethereum::rpc::RPCDecodable> for Token1 { + fn output(data: &[u8]) -> Result, String> { + Self::output(data) + } + } + } + /// Contract's events. + #[allow(dead_code, unused_imports, unused_variables)] + pub mod events { + use super::INTERNAL_ERR; + #[derive(Debug, Clone, PartialEq)] + pub struct Burn { + pub owner: Vec, + pub tick_lower: substreams::scalar::BigInt, + pub tick_upper: substreams::scalar::BigInt, + pub amount: substreams::scalar::BigInt, + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + } + impl Burn { + const TOPIC_ID: [u8; 32] = [ + 12u8, + 57u8, + 108u8, + 217u8, + 137u8, + 163u8, + 159u8, + 68u8, + 89u8, + 181u8, + 250u8, + 26u8, + 237u8, + 106u8, + 154u8, + 141u8, + 205u8, + 188u8, + 69u8, + 144u8, + 138u8, + 207u8, + 214u8, + 126u8, + 2u8, + 140u8, + 213u8, + 104u8, + 218u8, + 152u8, + 152u8, + 44u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 4usize { + return false; + } + if log.data.len() != 96usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + owner: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'owner' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tick_lower: substreams::scalar::BigInt::from_signed_bytes_be( + log.topics[2usize].as_ref(), + ), + tick_upper: substreams::scalar::BigInt::from_signed_bytes_be( + log.topics[3usize].as_ref(), + ), + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Burn { + const NAME: &'static str = "Burn"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Collect { + pub owner: Vec, + pub recipient: Vec, + pub tick_lower: substreams::scalar::BigInt, + pub tick_upper: substreams::scalar::BigInt, + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + } + impl Collect { + const TOPIC_ID: [u8; 32] = [ + 112u8, + 147u8, + 83u8, + 56u8, + 230u8, + 151u8, + 117u8, + 69u8, + 106u8, + 133u8, + 221u8, + 239u8, + 34u8, + 108u8, + 57u8, + 95u8, + 182u8, + 104u8, + 182u8, + 63u8, + 160u8, + 17u8, + 95u8, + 95u8, + 32u8, + 97u8, + 11u8, + 56u8, + 142u8, + 108u8, + 169u8, + 192u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 4usize { + return false; + } + if log.data.len() != 96usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Uint(128usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + owner: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'owner' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tick_lower: substreams::scalar::BigInt::from_signed_bytes_be( + log.topics[2usize].as_ref(), + ), + tick_upper: substreams::scalar::BigInt::from_signed_bytes_be( + log.topics[3usize].as_ref(), + ), + recipient: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Collect { + const NAME: &'static str = "Collect"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct CollectProtocol { + pub sender: Vec, + pub recipient: Vec, + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + } + impl CollectProtocol { + const TOPIC_ID: [u8; 32] = [ + 89u8, + 107u8, + 87u8, + 57u8, + 6u8, + 33u8, + 141u8, + 52u8, + 17u8, + 133u8, + 11u8, + 38u8, + 166u8, + 180u8, + 55u8, + 214u8, + 196u8, + 82u8, + 47u8, + 219u8, + 67u8, + 210u8, + 210u8, + 56u8, + 98u8, + 99u8, + 248u8, + 109u8, + 80u8, + 184u8, + 177u8, + 81u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 64usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Uint(128usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + sender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'sender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + recipient: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'recipient' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for CollectProtocol { + const NAME: &'static str = "CollectProtocol"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Flash { + pub sender: Vec, + pub recipient: Vec, + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + pub paid0: substreams::scalar::BigInt, + pub paid1: substreams::scalar::BigInt, + } + impl Flash { + const TOPIC_ID: [u8; 32] = [ + 189u8, + 189u8, + 183u8, + 29u8, + 120u8, + 96u8, + 55u8, + 107u8, + 165u8, + 43u8, + 37u8, + 165u8, + 2u8, + 139u8, + 238u8, + 162u8, + 53u8, + 129u8, + 54u8, + 74u8, + 64u8, + 82u8, + 47u8, + 107u8, + 207u8, + 184u8, + 107u8, + 177u8, + 242u8, + 220u8, + 166u8, + 51u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 128usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + sender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'sender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + recipient: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'recipient' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + paid0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + paid1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Flash { + const NAME: &'static str = "Flash"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct IncreaseObservationCardinalityNext { + pub observation_cardinality_next_old: substreams::scalar::BigInt, + pub observation_cardinality_next_new: substreams::scalar::BigInt, + } + impl IncreaseObservationCardinalityNext { + const TOPIC_ID: [u8; 32] = [ + 172u8, + 73u8, + 229u8, + 24u8, + 249u8, + 10u8, + 53u8, + 143u8, + 101u8, + 46u8, + 68u8, + 0u8, + 22u8, + 79u8, + 5u8, + 165u8, + 216u8, + 247u8, + 227u8, + 94u8, + 119u8, + 71u8, + 39u8, + 155u8, + 195u8, + 169u8, + 61u8, + 191u8, + 88u8, + 78u8, + 18u8, + 90u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 1usize { + return false; + } + if log.data.len() != 64usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(16usize), + ethabi::ParamType::Uint(16usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + observation_cardinality_next_old: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + observation_cardinality_next_new: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for IncreaseObservationCardinalityNext { + const NAME: &'static str = "IncreaseObservationCardinalityNext"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Initialize { + pub sqrt_price_x96: substreams::scalar::BigInt, + pub tick: substreams::scalar::BigInt, + } + impl Initialize { + const TOPIC_ID: [u8; 32] = [ + 152u8, + 99u8, + 96u8, + 54u8, + 203u8, + 102u8, + 169u8, + 193u8, + 154u8, + 55u8, + 67u8, + 94u8, + 252u8, + 30u8, + 144u8, + 20u8, + 33u8, + 144u8, + 33u8, + 78u8, + 138u8, + 190u8, + 184u8, + 33u8, + 189u8, + 186u8, + 63u8, + 41u8, + 144u8, + 221u8, + 76u8, + 149u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 1usize { + return false; + } + if log.data.len() != 64usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(160usize), + ethabi::ParamType::Int(24usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + sqrt_price_x96: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + tick: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Initialize { + const NAME: &'static str = "Initialize"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Mint { + pub sender: Vec, + pub owner: Vec, + pub tick_lower: substreams::scalar::BigInt, + pub tick_upper: substreams::scalar::BigInt, + pub amount: substreams::scalar::BigInt, + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + } + impl Mint { + const TOPIC_ID: [u8; 32] = [ + 122u8, + 83u8, + 8u8, + 11u8, + 164u8, + 20u8, + 21u8, + 139u8, + 231u8, + 236u8, + 105u8, + 185u8, + 135u8, + 181u8, + 251u8, + 125u8, + 7u8, + 222u8, + 225u8, + 1u8, + 254u8, + 133u8, + 72u8, + 143u8, + 8u8, + 83u8, + 174u8, + 22u8, + 35u8, + 157u8, + 11u8, + 222u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 4usize { + return false; + } + if log.data.len() != 128usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Address, + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Uint(256usize), + ethabi::ParamType::Uint(256usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + owner: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'owner' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + tick_lower: substreams::scalar::BigInt::from_signed_bytes_be( + log.topics[2usize].as_ref(), + ), + tick_upper: substreams::scalar::BigInt::from_signed_bytes_be( + log.topics[3usize].as_ref(), + ), + sender: values + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Mint { + const NAME: &'static str = "Mint"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct SetFeeProtocol { + pub fee_protocol0_old: substreams::scalar::BigInt, + pub fee_protocol1_old: substreams::scalar::BigInt, + pub fee_protocol0_new: substreams::scalar::BigInt, + pub fee_protocol1_new: substreams::scalar::BigInt, + } + impl SetFeeProtocol { + const TOPIC_ID: [u8; 32] = [ + 151u8, + 61u8, + 141u8, + 146u8, + 187u8, + 41u8, + 159u8, + 74u8, + 246u8, + 206u8, + 73u8, + 181u8, + 42u8, + 138u8, + 219u8, + 133u8, + 174u8, + 70u8, + 185u8, + 242u8, + 20u8, + 196u8, + 196u8, + 252u8, + 6u8, + 172u8, + 119u8, + 64u8, + 18u8, + 55u8, + 177u8, + 51u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 1usize { + return false; + } + if log.data.len() != 128usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Uint(8usize), + ethabi::ParamType::Uint(8usize), + ethabi::ParamType::Uint(8usize), + ethabi::ParamType::Uint(8usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + fee_protocol0_old: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + fee_protocol1_old: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + fee_protocol0_new: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + fee_protocol1_new: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for SetFeeProtocol { + const NAME: &'static str = "SetFeeProtocol"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + #[derive(Debug, Clone, PartialEq)] + pub struct Swap { + pub sender: Vec, + pub recipient: Vec, + pub amount0: substreams::scalar::BigInt, + pub amount1: substreams::scalar::BigInt, + pub sqrt_price_x96: substreams::scalar::BigInt, + pub liquidity: substreams::scalar::BigInt, + pub tick: substreams::scalar::BigInt, + } + impl Swap { + const TOPIC_ID: [u8; 32] = [ + 196u8, + 32u8, + 121u8, + 249u8, + 74u8, + 99u8, + 80u8, + 215u8, + 230u8, + 35u8, + 95u8, + 41u8, + 23u8, + 73u8, + 36u8, + 249u8, + 40u8, + 204u8, + 42u8, + 200u8, + 24u8, + 235u8, + 100u8, + 254u8, + 216u8, + 0u8, + 78u8, + 17u8, + 95u8, + 188u8, + 202u8, + 103u8, + ]; + pub fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + if log.topics.len() != 3usize { + return false; + } + if log.data.len() != 160usize { + return false; + } + return log.topics.get(0).expect("bounds already checked").as_ref() + == Self::TOPIC_ID; + } + pub fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + let mut values = ethabi::decode( + &[ + ethabi::ParamType::Int(256usize), + ethabi::ParamType::Int(256usize), + ethabi::ParamType::Uint(160usize), + ethabi::ParamType::Uint(128usize), + ethabi::ParamType::Int(24usize), + ], + log.data.as_ref(), + ) + .map_err(|e| format!("unable to decode log.data: {:?}", e))?; + values.reverse(); + Ok(Self { + sender: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[1usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'sender' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + recipient: ethabi::decode( + &[ethabi::ParamType::Address], + log.topics[2usize].as_ref(), + ) + .map_err(|e| { + format!( + "unable to decode param 'recipient' from topic of type 'address': {:?}", + e + ) + })? + .pop() + .expect(INTERNAL_ERR) + .into_address() + .expect(INTERNAL_ERR) + .as_bytes() + .to_vec(), + amount0: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + amount1: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + sqrt_price_x96: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + liquidity: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_uint() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_unsigned_bytes_be(&v) + }, + tick: { + let mut v = [0 as u8; 32]; + values + .pop() + .expect(INTERNAL_ERR) + .into_int() + .expect(INTERNAL_ERR) + .to_big_endian(v.as_mut_slice()); + substreams::scalar::BigInt::from_signed_bytes_be(&v) + }, + }) + } + } + impl substreams_ethereum::Event for Swap { + const NAME: &'static str = "Swap"; + fn match_log(log: &substreams_ethereum::pb::eth::v2::Log) -> bool { + Self::match_log(log) + } + fn decode( + log: &substreams_ethereum::pb::eth::v2::Log, + ) -> Result { + Self::decode(log) + } + } + } \ No newline at end of file diff --git a/substreams/ethereum-uniswap-v3/src/events/burn.rs b/substreams/ethereum-uniswap-v3/src/events/burn.rs new file mode 100644 index 0000000..2152244 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/events/burn.rs @@ -0,0 +1,45 @@ +use substreams_ethereum::pb::eth::v2::StorageChange; +use substreams_helper::storage_change::StorageChangesFilter; + +use crate::{ + abi::pool::events::Burn, + pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, +}; + +use super::{BalanceDelta, EventTrait}; + +impl EventTrait for Burn { + fn get_changed_attributes( + &self, + storage_changes: &[StorageChange], + pool_address: &[u8; 20], + ) -> Vec { + let storage_vec = storage_changes.to_vec(); + + let filtered_storage_changes = storage_vec + .filter_by_address(pool_address) + .into_iter() + .cloned() + .collect(); + + let pool_storage = UniswapPoolStorage::new(&filtered_storage_changes); + + let mut changed_attributes = + pool_storage.get_changed_attributes(TRACKED_SLOTS.to_vec().iter().collect()); + + let changed_ticks = + pool_storage.get_ticks_changes(vec![&self.tick_upper, &self.tick_lower]); + + changed_attributes.extend(changed_ticks); + + changed_attributes + } + + fn get_balance_delta(&self, _pool: &Pool, _ordinal: u64) -> Vec { + // Burn event balances deltas are accounted for by the Collect event. + // In the case of a burn, the Collect event amounts will include both the burned amount and + // the fees earned. + vec![] + } +} diff --git a/substreams/ethereum-uniswap-v3/src/events/collect.rs b/substreams/ethereum-uniswap-v3/src/events/collect.rs new file mode 100644 index 0000000..8969d67 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/events/collect.rs @@ -0,0 +1,58 @@ +use substreams_ethereum::pb::eth::v2::StorageChange; + +use crate::{ + abi::pool::events::Collect, + pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, +}; +use substreams_helper::storage_change::StorageChangesFilter; + +use super::{BalanceDelta, EventTrait}; + +impl EventTrait for Collect { + fn get_changed_attributes( + &self, + storage_changes: &[StorageChange], + pool_address: &[u8; 20], + ) -> Vec { + let storage_vec = storage_changes.to_vec(); + + let filtered_storage_changes = storage_vec + .filter_by_address(pool_address) + .into_iter() + .cloned() + .collect(); + + let pool_storage = UniswapPoolStorage::new(&filtered_storage_changes); + + let mut changed_attributes = + pool_storage.get_changed_attributes(TRACKED_SLOTS.to_vec().iter().collect()); + + let changed_ticks = + pool_storage.get_ticks_changes(vec![&self.tick_upper, &self.tick_lower]); + + changed_attributes.extend(changed_ticks); + + changed_attributes + } + + fn get_balance_delta(&self, pool: &Pool, ordinal: u64) -> Vec { + let changed_balance = vec![ + BalanceDelta { + token_address: pool.token0.clone(), + amount: self.amount0.clone().to_bytes_le().1, + sign: false, + pool_address: pool.address.clone(), + ordinal, + }, + BalanceDelta { + token_address: pool.token1.clone(), + amount: self.amount1.clone().to_bytes_le().1, + sign: false, + pool_address: pool.address.clone(), + ordinal, + }, + ]; + changed_balance + } +} diff --git a/substreams/ethereum-uniswap-v3/src/events/collect_fee_protocol.rs b/substreams/ethereum-uniswap-v3/src/events/collect_fee_protocol.rs new file mode 100644 index 0000000..87fec7e --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/events/collect_fee_protocol.rs @@ -0,0 +1,50 @@ +use substreams_ethereum::pb::eth::v2::StorageChange; +use substreams_helper::storage_change::StorageChangesFilter; + +use crate::{ + abi::pool::events::CollectProtocol, + pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, +}; + +use super::{BalanceDelta, EventTrait}; + +impl EventTrait for CollectProtocol { + fn get_changed_attributes( + &self, + storage_changes: &[StorageChange], + pool_address: &[u8; 20], + ) -> Vec { + let storage_vec = storage_changes.to_vec(); + + let filtered_storage_changes = storage_vec + .filter_by_address(pool_address) + .into_iter() + .cloned() + .collect(); + + let pool_storage = UniswapPoolStorage::new(&filtered_storage_changes); + + pool_storage.get_changed_attributes(TRACKED_SLOTS.to_vec().iter().collect()) + } + + fn get_balance_delta(&self, pool: &Pool, ordinal: u64) -> Vec { + let changed_balance = vec![ + BalanceDelta { + token_address: pool.token0.clone(), + amount: self.amount0.clone().to_bytes_le().1, + sign: false, + pool_address: pool.address.clone(), + ordinal, + }, + BalanceDelta { + token_address: pool.token1.clone(), + amount: self.amount1.clone().to_bytes_le().1, + sign: false, + pool_address: pool.address.clone(), + ordinal, + }, + ]; + changed_balance + } +} diff --git a/substreams/ethereum-uniswap-v3/src/events/flash.rs b/substreams/ethereum-uniswap-v3/src/events/flash.rs new file mode 100644 index 0000000..ccc08d4 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/events/flash.rs @@ -0,0 +1,50 @@ +use substreams_ethereum::pb::eth::v2::StorageChange; + +use crate::{ + abi::pool::events::Flash, + pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, +}; +use substreams_helper::storage_change::StorageChangesFilter; + +use super::{BalanceDelta, EventTrait}; + +impl EventTrait for Flash { + fn get_changed_attributes( + &self, + storage_changes: &[StorageChange], + pool_address: &[u8; 20], + ) -> Vec { + let storage_vec = storage_changes.to_vec(); + + let filtered_storage_changes = storage_vec + .filter_by_address(pool_address) + .into_iter() + .cloned() + .collect(); + + let pool_storage = UniswapPoolStorage::new(&filtered_storage_changes); + + pool_storage.get_changed_attributes(TRACKED_SLOTS.to_vec().iter().collect()) + } + + fn get_balance_delta(&self, pool: &Pool, ordinal: u64) -> Vec { + let changed_balance = vec![ + BalanceDelta { + token_address: pool.token0.clone(), + amount: self.paid0.clone().to_bytes_le().1, + sign: true, + pool_address: pool.address.clone(), + ordinal, + }, + BalanceDelta { + token_address: pool.token1.clone(), + amount: self.paid1.clone().to_bytes_le().1, + sign: true, + pool_address: pool.address.clone(), + ordinal, + }, + ]; + changed_balance + } +} diff --git a/substreams/ethereum-uniswap-v3/src/events/initialize.rs b/substreams/ethereum-uniswap-v3/src/events/initialize.rs new file mode 100644 index 0000000..cc1b9ef --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/events/initialize.rs @@ -0,0 +1,34 @@ +use substreams_ethereum::pb::eth::v2::StorageChange; + +use crate::{ + abi::pool::events::Initialize, + pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, +}; +use substreams_helper::storage_change::StorageChangesFilter; + +use super::{BalanceDelta, EventTrait}; + +impl EventTrait for Initialize { + fn get_changed_attributes( + &self, + storage_changes: &[StorageChange], + pool_address: &[u8; 20], + ) -> Vec { + let storage_vec = storage_changes.to_vec(); + + let filtered_storage_changes = storage_vec + .filter_by_address(pool_address) + .into_iter() + .cloned() + .collect(); + + let pool_storage = UniswapPoolStorage::new(&filtered_storage_changes); + + pool_storage.get_changed_attributes(TRACKED_SLOTS.to_vec().iter().collect()) + } + + fn get_balance_delta(&self, _pool: &Pool, _ordinal: u64) -> Vec { + vec![] + } +} diff --git a/substreams/ethereum-uniswap-v3/src/events/mint.rs b/substreams/ethereum-uniswap-v3/src/events/mint.rs new file mode 100644 index 0000000..f5374da --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/events/mint.rs @@ -0,0 +1,58 @@ +use substreams_ethereum::pb::eth::v2::StorageChange; + +use crate::{ + abi::pool::events::Mint, + pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, +}; +use substreams_helper::storage_change::StorageChangesFilter; + +use super::{BalanceDelta, EventTrait}; + +impl EventTrait for Mint { + fn get_changed_attributes( + &self, + storage_changes: &[StorageChange], + pool_address: &[u8; 20], + ) -> Vec { + let storage_vec = storage_changes.to_vec(); + + let filtered_storage_changes = storage_vec + .filter_by_address(pool_address) + .into_iter() + .cloned() + .collect(); + + let pool_storage = UniswapPoolStorage::new(&filtered_storage_changes); + + let mut changed_attributes = + pool_storage.get_changed_attributes(TRACKED_SLOTS.to_vec().iter().collect()); + + let changed_ticks = + pool_storage.get_ticks_changes(vec![&self.tick_upper, &self.tick_lower]); + + changed_attributes.extend(changed_ticks); + + changed_attributes + } + + fn get_balance_delta(&self, pool: &Pool, ordinal: u64) -> Vec { + let changed_balance = vec![ + BalanceDelta { + token_address: pool.token0.clone(), + amount: self.amount0.clone().to_bytes_le().1, + sign: true, + pool_address: pool.address.clone(), + ordinal, + }, + BalanceDelta { + token_address: pool.token1.clone(), + amount: self.amount1.clone().to_bytes_le().1, + sign: true, + pool_address: pool.address.clone(), + ordinal, + }, + ]; + changed_balance + } +} diff --git a/substreams/ethereum-uniswap-v3/src/events/mod.rs b/substreams/ethereum-uniswap-v3/src/events/mod.rs new file mode 100644 index 0000000..9d1f14c --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/events/mod.rs @@ -0,0 +1,150 @@ +use substreams_ethereum::{ + pb::eth::v2::{Log, StorageChange}, + Event, +}; + +use crate::{ + abi::pool::events::{ + Burn, Collect, CollectProtocol, Flash, Initialize, Mint, SetFeeProtocol, Swap, + }, + pb::{ + tycho::evm::v1::Attribute, + uniswap::v3::{BalanceDelta, Pool}, + }, +}; + +pub mod burn; +pub mod collect; +pub mod collect_fee_protocol; +pub mod flash; +pub mod initialize; +pub mod mint; +pub mod set_fee_protocol; +pub mod swap; + +/// A trait for extracting changed attributes and balance from an event. +pub trait EventTrait { + /// Get all relevant changed attributes from the `[StorageChange]`. + /// If an attribute is changed multiple times, only the last state will be returned. + /// + /// # Arguments + /// + /// * `storage_changes` - A slice of `StorageChange` that indicates the changes in storage. + /// * `pool` - Reference to the `Pool`. + /// + /// # Returns + /// + /// A vector of `Attribute` that represents the changed attributes. + fn get_changed_attributes( + &self, + storage_changes: &[StorageChange], + pool_address: &[u8; 20], + ) -> Vec; + + /// Get all balance deltas from the event. + /// + /// # Arguments + /// + /// * `pool` - Reference to the `Pool`. + /// * `ordinal` - The ordinal number of the event. This is used by the balance store to sort the + /// balance deltas in the correct order. + /// + /// # Returns + /// + /// A vector of `BalanceDelta` that represents the balance deltas. + fn get_balance_delta(&self, pool: &Pool, ordinal: u64) -> Vec; +} + +/// Represent every events of a UniswapV3 pool. +pub enum EventType { + Initialize(Initialize), + Swap(Swap), + Flash(Flash), + Mint(Mint), + Burn(Burn), + Collect(Collect), + SetFeeProtocol(SetFeeProtocol), + CollectProtocol(CollectProtocol), +} + +impl EventType { + fn as_event_trait(&self) -> &dyn EventTrait { + match self { + EventType::Initialize(e) => e, + EventType::Swap(e) => e, + EventType::Flash(e) => e, + EventType::Mint(e) => e, + EventType::Burn(e) => e, + EventType::Collect(e) => e, + EventType::SetFeeProtocol(e) => e, + EventType::CollectProtocol(e) => e, + } + } +} + +/// Decodes a given log into an `EventType`. +/// +/// # Arguments +/// +/// * `event` - A reference to the `Log`. +/// +/// # Returns +/// +/// An `Option` that represents the decoded event type. +pub fn decode_event(event: &Log) -> Option { + [ + Initialize::match_and_decode(event).map(EventType::Initialize), + Swap::match_and_decode(event).map(EventType::Swap), + Flash::match_and_decode(event).map(EventType::Flash), + Mint::match_and_decode(event).map(EventType::Mint), + Burn::match_and_decode(event).map(EventType::Burn), + Collect::match_and_decode(event).map(EventType::Collect), + SetFeeProtocol::match_and_decode(event).map(EventType::SetFeeProtocol), + CollectProtocol::match_and_decode(event).map(EventType::CollectProtocol), + ] + .into_iter() + .find_map(std::convert::identity) +} + +/// Gets the changed attributes from the log. +/// +/// # Arguments +/// +/// * `event` - A reference to the `Log`. +/// * `storage_changes` - A slice of `StorageChange` that indicates the changes in storage. +/// * `pool` - Reference to the `Pool` structure. +/// +/// # Returns +/// +/// A vector of `Attribute` that represents the changed attributes. +pub fn get_log_changed_attributes( + event: &Log, + storage_changes: &[StorageChange], + pool_address: &[u8; 20], +) -> Vec { + decode_event(event) + .map(|e| { + e.as_event_trait() + .get_changed_attributes(storage_changes, pool_address) + }) + .unwrap_or_default() +} + +/// Gets the changed balances from the log. +/// +/// # Arguments +/// +/// * `event` - A reference to the `Log`. +/// * `pool` - Reference to the `Pool` structure. +/// +/// # Returns +/// +/// A vector of `BalanceDelta` that represents +pub fn get_log_changed_balances(event: &Log, pool: &Pool) -> Vec { + decode_event(event) + .map(|e| { + e.as_event_trait() + .get_balance_delta(pool, event.ordinal) + }) + .unwrap_or_default() +} diff --git a/substreams/ethereum-uniswap-v3/src/events/set_fee_protocol.rs b/substreams/ethereum-uniswap-v3/src/events/set_fee_protocol.rs new file mode 100644 index 0000000..80e394d --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/events/set_fee_protocol.rs @@ -0,0 +1,34 @@ +use substreams_ethereum::pb::eth::v2::StorageChange; + +use crate::{ + abi::pool::events::SetFeeProtocol, + pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, +}; +use substreams_helper::storage_change::StorageChangesFilter; + +use super::{BalanceDelta, EventTrait}; + +impl EventTrait for SetFeeProtocol { + fn get_changed_attributes( + &self, + storage_changes: &[StorageChange], + pool_address: &[u8; 20], + ) -> Vec { + let storage_vec = storage_changes.to_vec(); + + let filtered_storage_changes = storage_vec + .filter_by_address(pool_address) + .into_iter() + .cloned() + .collect(); + + let pool_storage = UniswapPoolStorage::new(&filtered_storage_changes); + + pool_storage.get_changed_attributes(TRACKED_SLOTS.to_vec().iter().collect()) + } + + fn get_balance_delta(&self, _pool: &Pool, _ordinal: u64) -> Vec { + vec![] + } +} diff --git a/substreams/ethereum-uniswap-v3/src/events/swap.rs b/substreams/ethereum-uniswap-v3/src/events/swap.rs new file mode 100644 index 0000000..d69bcb4 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/events/swap.rs @@ -0,0 +1,50 @@ +use num_bigint::Sign; +use substreams::scalar::BigInt; +use substreams_ethereum::pb::eth::v2::StorageChange; +use substreams_helper::storage_change::StorageChangesFilter; + +use crate::{ + abi::pool::events::Swap, + pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, +}; + +use super::{BalanceDelta, EventTrait}; + +impl EventTrait for Swap { + fn get_changed_attributes( + &self, + storage_changes: &[StorageChange], + pool_address: &[u8; 20], + ) -> Vec { + let storage_vec = storage_changes.to_vec(); + + let filtered_storage_changes = storage_vec + .filter_by_address(pool_address) + .into_iter() + .cloned() + .collect(); + + let pool_storage = UniswapPoolStorage::new(&filtered_storage_changes); + + pool_storage.get_changed_attributes(TRACKED_SLOTS.to_vec().iter().collect()) + } + + fn get_balance_delta(&self, pool: &Pool, ordinal: u64) -> Vec { + let create_balance_delta = |token_address: Vec, amount: BigInt| -> BalanceDelta { + let (amount_sign, amount_bytes) = amount.clone().to_bytes_le(); + BalanceDelta { + token_address, + amount: amount_bytes, + sign: amount_sign == Sign::Plus, + pool_address: pool.address.clone(), + ordinal, + } + }; + + vec![ + create_balance_delta(pool.token0.clone(), self.amount0.clone()), + create_balance_delta(pool.token1.clone(), self.amount1.clone()), + ] + } +} diff --git a/substreams/ethereum-uniswap-v3/src/lib.rs b/substreams/ethereum-uniswap-v3/src/lib.rs new file mode 100644 index 0000000..77428d7 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/lib.rs @@ -0,0 +1,11 @@ +#![allow(clippy::not_unsafe_ptr_arg_deref)] + +mod abi; +mod modules; +mod pb; +mod storage; + +pub use modules::*; + +mod events; +mod traits; diff --git a/substreams/ethereum-uniswap-v3/src/modules/1_map_pool_created.rs b/substreams/ethereum-uniswap-v3/src/modules/1_map_pool_created.rs new file mode 100644 index 0000000..4688ee2 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/modules/1_map_pool_created.rs @@ -0,0 +1,103 @@ +use std::str::FromStr; + +use ethabi::ethereum_types::Address; +use substreams::scalar::BigInt; +use substreams_ethereum::pb::eth::v2::{self as eth}; + +use substreams_helper::{event_handler::EventHandler, hex::Hexable}; + +use crate::{ + abi::factory::events::PoolCreated, + pb::tycho::evm::v1::{ + Attribute, BlockChanges, ChangeType, EntityChanges, FinancialType, ImplementationType, + ProtocolComponent, ProtocolType, Transaction, TransactionChanges, + }, +}; + +#[substreams::handlers::map] +pub fn map_pools_created( + params: String, + block: eth::Block, +) -> Result { + let mut new_pools: Vec = vec![]; + let factory_address = params.as_str(); + + get_new_pools(&block, &mut new_pools, factory_address); + + Ok(BlockChanges { block: Some(block.into()), changes: new_pools }) +} + +// Extract new pools from PoolCreated events +fn get_new_pools( + block: ð::Block, + new_pools: &mut Vec, + factory_address: &str, +) { + // Extract new pools from PoolCreated events + let mut on_pool_created = |event: PoolCreated, _tx: ð::TransactionTrace, _log: ð::Log| { + let tycho_tx: Transaction = _tx.into(); + + new_pools.push(TransactionChanges { + tx: Some(tycho_tx.clone()), + contract_changes: vec![], + entity_changes: vec![EntityChanges { + component_id: event.pool.to_hex(), + attributes: vec![ + Attribute { + name: "liquidity".to_string(), + value: BigInt::from(0).to_signed_bytes_le(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "tick".to_string(), + value: BigInt::from(0).to_signed_bytes_le(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "sqrt_price_x96".to_string(), + value: BigInt::from(0).to_signed_bytes_le(), + change: ChangeType::Creation.into(), + }, + ], + }], + component_changes: vec![ProtocolComponent { + id: event.pool.to_hex(), + tokens: vec![event.token0, event.token1], + contracts: vec![], + static_att: vec![ + Attribute { + name: "fee".to_string(), + value: event.fee.to_signed_bytes_le(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "tick_spacing".to_string(), + value: event.tick_spacing.to_signed_bytes_le(), + change: ChangeType::Creation.into(), + }, + Attribute { + name: "pool_address".to_string(), + value: event.pool, + change: ChangeType::Creation.into(), + }, + ], + change: i32::from(ChangeType::Creation), + protocol_type: Option::from(ProtocolType { + name: "uniswap_v3_pool".to_string(), + financial_type: FinancialType::Swap.into(), + attribute_schema: vec![], + implementation_type: ImplementationType::Custom.into(), + }), + tx: Some(tycho_tx), + }], + balance_changes: vec![], + }) + }; + + let mut eh = EventHandler::new(block); + + eh.filter_by_address(vec![Address::from_str(factory_address).unwrap()]); + + eh.on::(&mut on_pool_created); + eh.handle_events(); +} diff --git a/substreams/ethereum-uniswap-v3/src/modules/2_store_pools.rs b/substreams/ethereum-uniswap-v3/src/modules/2_store_pools.rs new file mode 100644 index 0000000..e70f64e --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/modules/2_store_pools.rs @@ -0,0 +1,23 @@ +use std::str; + +use substreams::store::{StoreNew, StoreSetIfNotExists, StoreSetIfNotExistsProto}; + +use crate::pb::{tycho::evm::v1::BlockChanges, uniswap::v3::Pool}; + +#[substreams::handlers::store] +pub fn store_pools(pools_created: BlockChanges, store: StoreSetIfNotExistsProto) { + // Store pools. Required so the next maps can match any event to a known pool by their address + + for change in pools_created.changes { + for component_change in &change.component_changes { + let pool_address: &str = &component_change.id; + let pool: Pool = Pool { + address: hex::decode(pool_address.trim_start_matches("0x")).unwrap(), + token0: component_change.tokens[0].clone(), + token1: component_change.tokens[1].clone(), + created_tx_hash: change.tx.as_ref().unwrap().hash.clone(), + }; + store.set_if_not_exists(0, format!("{}:{}", "Pool", pool_address), &pool); + } + } +} diff --git a/substreams/ethereum-uniswap-v3/src/modules/3_map_balance_changes.rs b/substreams/ethereum-uniswap-v3/src/modules/3_map_balance_changes.rs new file mode 100644 index 0000000..4d888bb --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/modules/3_map_balance_changes.rs @@ -0,0 +1,40 @@ +use anyhow::Ok; +use substreams::store::{StoreGet, StoreGetProto}; +use substreams_ethereum::pb::eth::v2::{self as eth}; +use substreams_helper::hex::Hexable; + +use crate::{ + events::get_log_changed_balances, + pb::uniswap::v3::{BalanceDeltas, Pool}, +}; + +#[substreams::handlers::map] +pub fn map_balance_changes( + block: eth::Block, + pools_store: StoreGetProto, +) -> Result { + let mut balances_deltas = Vec::new(); + for trx in block.transactions() { + let mut tx_deltas = Vec::new(); + for log in trx + .calls + .iter() + .filter(|call| !call.state_reverted) + .flat_map(|call| &call.logs) + { + // Skip if the log is not from a known uniswapV3 pool. + if let Some(pool) = + pools_store.get_last(format!("{}:{}", "Pool", &log.address.to_hex())) + { + tx_deltas.extend(get_log_changed_balances(log, &pool)) + } else { + continue; + } + } + if !tx_deltas.is_empty() { + balances_deltas.extend(tx_deltas); + } + } + + Ok(BalanceDeltas { deltas: balances_deltas }) +} diff --git a/substreams/ethereum-uniswap-v3/src/modules/4_store_pools_balances.rs b/substreams/ethereum-uniswap-v3/src/modules/4_store_pools_balances.rs new file mode 100644 index 0000000..88fa7ca --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/modules/4_store_pools_balances.rs @@ -0,0 +1,28 @@ +use crate::pb::uniswap::v3::BalanceDeltas; +use num_bigint::Sign; +use substreams::{ + scalar::BigInt, + store::{StoreAdd, StoreAddBigInt, StoreNew}, +}; + +#[substreams::handlers::store] +pub fn store_pools_balances(balances_deltas: BalanceDeltas, store: StoreAddBigInt) { + let mut deltas = balances_deltas.deltas.clone(); + + deltas.sort_unstable_by_key(|delta| delta.ordinal); + + deltas.iter().for_each(|delta| { + store.add( + delta.ordinal, + format!( + "pool:{0}:token:{1}", + hex::encode(&delta.pool_address), + hex::encode(&delta.token_address) + ), + BigInt::from_bytes_le( + if delta.sign { Sign::Plus } else { Sign::Minus }, + delta.amount.as_slice(), + ), + ); + }); +} diff --git a/substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs b/substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs new file mode 100644 index 0000000..c31b3b2 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs @@ -0,0 +1,235 @@ +use std::{collections::HashMap, usize, vec}; +use substreams::store::{StoreGet, StoreGetBigInt, StoreGetProto}; +use substreams_ethereum::pb::eth::v2::{self as eth, TransactionTrace}; + +use substreams_helper::hex::Hexable; + +use crate::{ + events::{get_log_changed_attributes, get_log_changed_balances}, + pb::{ + tycho::evm::v1::{BalanceChange, Block, BlockChanges, EntityChanges, TransactionChanges}, + uniswap::v3::Pool, + }, +}; + +#[substreams::handlers::map] +pub fn map_pool_events( + block: eth::Block, + created_pools: BlockChanges, + pools_store: StoreGetProto, + balance_store: StoreGetBigInt, +) -> Result { + let mut tx_changes_map: HashMap, TransactionChanges> = HashMap::new(); + + // Add created pools to the tx_changes_map + for change in created_pools.changes.into_iter() { + let transaction = change.tx.as_ref().unwrap(); + tx_changes_map + .entry(transaction.hash.clone()) + .and_modify(|c| { + c.component_changes + .extend(change.component_changes.clone()); + c.entity_changes + .extend(change.entity_changes.clone()); + }) + .or_insert(change); + } + + for trx in block.transactions() { + for (log, call_view) in trx.logs_with_calls() { + // Skip if the log is not from a known uniswapV3 pool. + if let Some(pool) = + pools_store.get_last(format!("{}:{}", "Pool", &log.address.to_hex())) + { + let changed_attributes = get_log_changed_attributes( + log, + &call_view.call.storage_changes, + pool.address + .clone() + .as_slice() + .try_into() + .expect("Pool address is not 20 bytes long"), + ); + + let mut balance_changes: Vec = vec![]; + + if !(get_log_changed_balances(log, &pool).is_empty()) { + let token_0_balance = balance_store.get_last(format!( + "pool:{0}:token:{1}", + hex::encode(&pool.address), + hex::encode(&pool.token0) + )); + let token_1_balance = balance_store.get_last(format!( + "pool:{0}:token:{1}", + hex::encode(&pool.address), + hex::encode(&pool.token1) + )); + + let pool_address_utf8 = pool + .address + .clone() + .to_hex() + .as_bytes() + .to_vec(); + + let token_0_balance_change = BalanceChange { + component_id: pool_address_utf8.clone(), + token: pool.token0.clone(), + balance: token_0_balance + .clone() + .expect("Couldn't get balance from store") + .to_bytes_be() + .1, + }; + let token_1_balance_change = BalanceChange { + component_id: pool_address_utf8.clone(), + token: pool.token1.clone(), + balance: token_1_balance + .clone() + .expect("Couldn't get balance from store") + .to_bytes_be() + .1, + }; + + balance_changes.extend([token_0_balance_change, token_1_balance_change]); + } + + // Create entity changes + let entity_changes: Vec = vec![EntityChanges { + component_id: pool.address.clone().to_hex(), + attributes: changed_attributes, + }]; + + update_tx_changes_map(entity_changes, balance_changes, &mut tx_changes_map, trx); + } else { + continue; + } + } + } + + // Make a list of all HashMap values: + let tx_entity_changes: Vec = tx_changes_map.into_values().collect(); + + let tycho_block: Block = block.into(); + + let block_entity_changes = + BlockChanges { block: Some(tycho_block), changes: tx_entity_changes }; + + Ok(block_entity_changes) +} + +fn update_tx_changes_map( + entity_changes: Vec, + balance_changes: Vec, + tx_changes_map: &mut HashMap, TransactionChanges>, + tx_trace: &TransactionTrace, +) { + // Get the tx hash + let tx_hash = tx_trace.hash.clone(); + + // Get the tx changes from the map + let tx_changes = tx_changes_map.get_mut(&tx_hash); + + // Update the tx changes + if let Some(tx_changes) = tx_changes { + // Merge the entity changes + tx_changes.entity_changes = + merge_entity_changes(&tx_changes.entity_changes, &entity_changes); + + // Merge the balance changes + tx_changes.balance_changes = + merge_balance_changes(&tx_changes.balance_changes, &balance_changes); + } else { + // If the tx is not in the map, add it + let tx_changes = TransactionChanges { + tx: Some(tx_trace.into()), + contract_changes: vec![], + entity_changes, + balance_changes, + component_changes: vec![], + }; + tx_changes_map.insert(tx_hash, tx_changes); + } +} + +/// Merges new entity changes into an existing collection of entity changes and returns the merged +/// result. For each entity change, if an entity change with the same component_id exists, its +/// attributes are merged. If an attribute with the same name exists, the new attribute replaces the +/// old one. +/// +/// Parameters: +/// - `existing_changes`: A reference to a vector of existing entity changes. +/// - `new_changes`: A reference to a vector of new entity changes to be merged. +/// +/// Returns: +/// A new `Vec` containing the merged entity changes. +fn merge_entity_changes( + existing_changes: &[EntityChanges], + new_changes: &Vec, +) -> Vec { + let mut changes_map = existing_changes + .iter() + .cloned() + .map(|change| (change.component_id.clone(), change)) + .collect::>(); + + for change in new_changes { + match changes_map.get_mut(&change.component_id) { + Some(existing_change) => { + let mut attributes_map = existing_change + .attributes + .iter() + .cloned() + .map(|attr| (attr.name.clone(), attr)) + .collect::>(); + + for attr in &change.attributes { + attributes_map.insert(attr.name.clone(), attr.clone()); + } + + existing_change.attributes = attributes_map.into_values().collect(); + } + None => { + changes_map.insert(change.component_id.clone(), change.clone()); + } + } + } + + changes_map.into_values().collect() +} + +#[derive(Debug, PartialEq, Eq, Hash)] +struct BalanceChangeKey { + token: Vec, + component_id: Vec, +} + +/// Merges two vectors of `BalanceChange` structures into a single vector. If two `BalanceChange` +/// instances have the same combination of `token` and `component_id`, the value from the +/// `new_entries` vector will replace the one from the `current` vector. +/// +/// Parameters: +/// - `current`: A reference to a vector of `BalanceChange` instances representing the current +/// balance changes. +/// - `new_entries`: A reference to a vector of `BalanceChange` instances representing new balance +/// changes to be merged. +/// +/// Returns: +/// A `Vec` that contains the merged balance changes. +fn merge_balance_changes( + current: &[BalanceChange], + new_entries: &Vec, +) -> Vec { + let mut balances = HashMap::new(); + + for balance_change in current.iter().chain(new_entries) { + let key = BalanceChangeKey { + token: balance_change.token.clone(), + component_id: balance_change.component_id.clone(), + }; + + balances.insert(key, balance_change.clone()); + } + + balances.into_values().collect() +} diff --git a/substreams/ethereum-uniswap-v3/src/modules/mod.rs b/substreams/ethereum-uniswap-v3/src/modules/mod.rs new file mode 100644 index 0000000..774e9bf --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/modules/mod.rs @@ -0,0 +1,18 @@ +pub use map_pool_created::map_pools_created; +pub use map_pool_events::map_pool_events; +pub use store_pools::store_pools; + +#[path = "1_map_pool_created.rs"] +mod map_pool_created; + +#[path = "2_store_pools.rs"] +mod store_pools; + +#[path = "3_map_balance_changes.rs"] +mod map_balance_changes; + +#[path = "4_store_pools_balances.rs"] +mod store_pools_balances; + +#[path = "5_map_pool_events.rs"] +mod map_pool_events; diff --git a/substreams/ethereum-uniswap-v3/src/pb/mod.rs b/substreams/ethereum-uniswap-v3/src/pb/mod.rs new file mode 100644 index 0000000..39167a4 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/pb/mod.rs @@ -0,0 +1,17 @@ +// @generated +pub mod tycho { + pub mod evm { + // @@protoc_insertion_point(attribute:tycho.evm.v1) + pub mod v1 { + include!("tycho.evm.v1.rs"); + // @@protoc_insertion_point(tycho.evm.v1) + } + } +} +pub mod uniswap { + // @@protoc_insertion_point(attribute:uniswap.v3) + pub mod v3 { + include!("uniswap.v3.rs"); + // @@protoc_insertion_point(uniswap.v3) + } +} diff --git a/substreams/ethereum-uniswap-v3/src/pb/tycho.evm.v1.rs b/substreams/ethereum-uniswap-v3/src/pb/tycho.evm.v1.rs new file mode 100644 index 0000000..6dac022 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/pb/tycho.evm.v1.rs @@ -0,0 +1,307 @@ +// @generated +// This file contains the proto definitions for Substreams common to all integrations. + +/// A struct describing a block. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Block { + /// The blocks hash. + #[prost(bytes="vec", tag="1")] + pub hash: ::prost::alloc::vec::Vec, + /// The parent blocks hash. + #[prost(bytes="vec", tag="2")] + pub parent_hash: ::prost::alloc::vec::Vec, + /// The block number. + #[prost(uint64, tag="3")] + pub number: u64, + /// The block timestamp. + #[prost(uint64, tag="4")] + pub ts: u64, +} +/// A struct describing a transaction. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Transaction { + /// The transaction hash. + #[prost(bytes="vec", tag="1")] + pub hash: ::prost::alloc::vec::Vec, + /// The sender of the transaction. + #[prost(bytes="vec", tag="2")] + pub from: ::prost::alloc::vec::Vec, + /// The receiver of the transaction. + #[prost(bytes="vec", tag="3")] + pub to: ::prost::alloc::vec::Vec, + /// The transactions index within the block. + #[prost(uint64, tag="4")] + pub index: u64, +} +/// A custom struct representing an arbitrary attribute of a protocol component. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Attribute { + /// The name of the attribute. + #[prost(string, tag="1")] + pub name: ::prost::alloc::string::String, + /// The value of the attribute. + #[prost(bytes="vec", tag="2")] + pub value: ::prost::alloc::vec::Vec, + /// The type of change the attribute underwent. + #[prost(enumeration="ChangeType", tag="3")] + pub change: i32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProtocolType { + #[prost(string, tag="1")] + pub name: ::prost::alloc::string::String, + #[prost(enumeration="FinancialType", tag="2")] + pub financial_type: i32, + #[prost(message, repeated, tag="3")] + pub attribute_schema: ::prost::alloc::vec::Vec, + #[prost(enumeration="ImplementationType", tag="4")] + pub implementation_type: i32, +} +/// A struct describing a part of the protocol. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProtocolComponent { + /// A unique identifier for the component within the protocol. + /// Can be a stringified address or a string describing the trading pair. + #[prost(string, tag="1")] + pub id: ::prost::alloc::string::String, + /// Addresses of the ERC20 tokens used by the component. + #[prost(bytes="vec", repeated, tag="2")] + pub tokens: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + /// Addresses of the contracts used by the component. + #[prost(bytes="vec", repeated, tag="3")] + pub contracts: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + /// Attributes of the component. + /// The inner ChangeType of the attribute has to match the ChangeType of the ProtocolComponent. + #[prost(message, repeated, tag="4")] + pub static_att: ::prost::alloc::vec::Vec, + /// Type of change the component underwent. + #[prost(enumeration="ChangeType", tag="5")] + pub change: i32, + /// / Represents the functionality of the component. + #[prost(message, optional, tag="6")] + pub protocol_type: ::core::option::Option, + /// Transaction where this component was created + #[prost(message, optional, tag="7")] + pub tx: ::core::option::Option, +} +/// A struct for following the changes of Total Value Locked (TVL) of a protocol component. +/// Note that if the ProtocolComponent contains multiple contracts, the TVL is tracked for the component as a whole. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BalanceChange { + /// The address of the ERC20 token whose balance changed. + #[prost(bytes="vec", tag="1")] + pub token: ::prost::alloc::vec::Vec, + /// The new balance of the token. + #[prost(bytes="vec", tag="2")] + pub balance: ::prost::alloc::vec::Vec, + /// The id of the component whose TVL is tracked. Note: This MUST be utf8 encoded. + #[prost(bytes="vec", tag="3")] + pub component_id: ::prost::alloc::vec::Vec, +} +// Native entities + +/// A component is a set of attributes that are associated with a custom entity. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EntityChanges { + /// A unique identifier of the entity within the protocol. + #[prost(string, tag="1")] + pub component_id: ::prost::alloc::string::String, + /// The set of attributes that are associated with the entity. + #[prost(message, repeated, tag="2")] + pub attributes: ::prost::alloc::vec::Vec, +} +// VM entities + +/// A key value entry into contract storage. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContractSlot { + /// A contract's storage slot. + #[prost(bytes="vec", tag="2")] + pub slot: ::prost::alloc::vec::Vec, + /// The new value for this storage slot. + #[prost(bytes="vec", tag="3")] + pub value: ::prost::alloc::vec::Vec, +} +/// Changes made to a single contract's state. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ContractChange { + /// The contract's address + #[prost(bytes="vec", tag="1")] + pub address: ::prost::alloc::vec::Vec, + /// The new balance of the contract, empty bytes indicates no change. + #[prost(bytes="vec", tag="2")] + pub balance: ::prost::alloc::vec::Vec, + /// The new code of the contract, empty bytes indicates no change. + #[prost(bytes="vec", tag="3")] + pub code: ::prost::alloc::vec::Vec, + /// The changes to this contract's slots, empty sequence indicates no change. + #[prost(message, repeated, tag="4")] + pub slots: ::prost::alloc::vec::Vec, + /// Whether this is an update, a creation or a deletion. + #[prost(enumeration="ChangeType", tag="5")] + pub change: i32, +} +// Aggregate entities + +/// A set of changes aggregated by transaction. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionChanges { + /// The transaction instance that results in the changes. + #[prost(message, optional, tag="1")] + pub tx: ::core::option::Option, + /// Contains the changes induced by the above transaction, aggregated on a per-contract basis. + /// Contains the contract changes induced by the above transaction, usually for tracking VM components. + #[prost(message, repeated, tag="2")] + pub contract_changes: ::prost::alloc::vec::Vec, + /// Contains the entity changes induced by the above transaction. + /// Usually for tracking native components or used for VM extensions (plugins). + #[prost(message, repeated, tag="3")] + pub entity_changes: ::prost::alloc::vec::Vec, + /// An array of newly added components. + #[prost(message, repeated, tag="4")] + pub component_changes: ::prost::alloc::vec::Vec, + /// An array of balance changes to components. + #[prost(message, repeated, tag="5")] + pub balance_changes: ::prost::alloc::vec::Vec, +} +/// A set of transaction changes within a single block. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockChanges { + /// The block for which these changes are collectively computed. + #[prost(message, optional, tag="1")] + pub block: ::core::option::Option, + /// The set of transaction changes observed in the specified block. + #[prost(message, repeated, tag="2")] + pub changes: ::prost::alloc::vec::Vec, +} +/// Enum to specify the type of a change. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ChangeType { + Unspecified = 0, + Update = 1, + Creation = 2, + Deletion = 3, +} +impl ChangeType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ChangeType::Unspecified => "CHANGE_TYPE_UNSPECIFIED", + ChangeType::Update => "CHANGE_TYPE_UPDATE", + ChangeType::Creation => "CHANGE_TYPE_CREATION", + ChangeType::Deletion => "CHANGE_TYPE_DELETION", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CHANGE_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "CHANGE_TYPE_UPDATE" => Some(Self::Update), + "CHANGE_TYPE_CREATION" => Some(Self::Creation), + "CHANGE_TYPE_DELETION" => Some(Self::Deletion), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum FinancialType { + Swap = 0, + Lend = 1, + Leverage = 2, + Psm = 3, +} +impl FinancialType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + FinancialType::Swap => "SWAP", + FinancialType::Lend => "LEND", + FinancialType::Leverage => "LEVERAGE", + FinancialType::Psm => "PSM", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SWAP" => Some(Self::Swap), + "LEND" => Some(Self::Lend), + "LEVERAGE" => Some(Self::Leverage), + "PSM" => Some(Self::Psm), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ImplementationType { + Vm = 0, + Custom = 1, +} +impl ImplementationType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ImplementationType::Vm => "VM", + ImplementationType::Custom => "CUSTOM", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "VM" => Some(Self::Vm), + "CUSTOM" => Some(Self::Custom), + _ => None, + } + } +} +// WARNING: DEPRECATED. Please use common.proto's TransactionChanges and BlockChanges instead. +// This file contains the definition for the native integration of Substreams. + +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionEntityChanges { + #[prost(message, optional, tag="1")] + pub tx: ::core::option::Option, + #[prost(message, repeated, tag="2")] + pub entity_changes: ::prost::alloc::vec::Vec, + /// An array of newly added components. + #[prost(message, repeated, tag="3")] + pub component_changes: ::prost::alloc::vec::Vec, + /// An array of balance changes to components. + #[prost(message, repeated, tag="4")] + pub balance_changes: ::prost::alloc::vec::Vec, +} +/// A set of transaction changes within a single block. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockEntityChanges { + /// The block for which these changes are collectively computed. + #[prost(message, optional, tag="1")] + pub block: ::core::option::Option, + /// The set of transaction changes observed in the specified block. + #[prost(message, repeated, tag="2")] + pub changes: ::prost::alloc::vec::Vec, +} +// @@protoc_insertion_point(module) diff --git a/substreams/ethereum-uniswap-v3/src/pb/uniswap.types.v1.rs b/substreams/ethereum-uniswap-v3/src/pb/uniswap.types.v1.rs new file mode 100644 index 0000000..80dc421 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/pb/uniswap.types.v1.rs @@ -0,0 +1,541 @@ +// @generated +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Erc20Tokens { + #[prost(message, repeated, tag="1")] + pub tokens: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Erc20Token { + #[prost(string, tag="1")] + pub address: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub symbol: ::prost::alloc::string::String, + #[prost(uint64, tag="4")] + pub decimals: u64, + #[prost(string, tag="5")] + pub total_supply: ::prost::alloc::string::String, + #[prost(string, repeated, tag="6")] + pub whitelist_pools: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Liquidity { + #[prost(string, tag="1")] + pub pool_address: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="2")] + pub value: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Pools { + #[prost(message, repeated, tag="1")] + pub pools: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Pool { + #[prost(string, tag="1")] + pub address: ::prost::alloc::string::String, + #[prost(uint64, tag="3")] + pub created_at_timestamp: u64, + #[prost(uint64, tag="4")] + pub created_at_block_number: u64, + #[prost(message, optional, tag="5")] + pub token0: ::core::option::Option, + #[prost(message, optional, tag="6")] + pub token1: ::core::option::Option, + /// Integer + #[prost(string, tag="7")] + pub fee_tier: ::prost::alloc::string::String, + /// internals + #[prost(int32, tag="30")] + pub tick_spacing: i32, + #[prost(uint64, tag="31")] + pub log_ordinal: u64, + #[prost(string, tag="32")] + pub transaction_id: ::prost::alloc::string::String, + #[prost(bool, tag="33")] + pub ignore_pool: bool, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Events { + #[prost(message, repeated, tag="1")] + pub pool_sqrt_prices: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="2")] + pub pool_liquidities: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="7")] + pub fee_growth_global_updates: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="10")] + pub fee_growth_inside_updates: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="11")] + pub fee_growth_outside_updates: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="3")] + pub pool_events: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="4")] + pub transactions: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="6")] + pub flashes: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="8")] + pub ticks_created: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="9")] + pub ticks_updated: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="20")] + pub created_positions: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="21")] + pub increase_liquidity_positions: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="22")] + pub decrease_liquidity_positions: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="23")] + pub collect_positions: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag="24")] + pub transfer_positions: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `Events`. +pub mod events { + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct FeeGrowthGlobal { + #[prost(string, tag="1")] + pub pool_address: ::prost::alloc::string::String, + #[prost(uint64, tag="2")] + pub ordinal: u64, + #[prost(int32, tag="3")] + pub token_idx: i32, + /// Integer + #[prost(string, tag="4")] + pub new_value: ::prost::alloc::string::String, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct FeeGrowthInside { + #[prost(string, tag="1")] + pub pool_address: ::prost::alloc::string::String, + #[prost(int32, tag="2")] + pub tick_idx: i32, + #[prost(uint64, tag="3")] + pub ordinal: u64, + /// Integer + #[prost(string, tag="4")] + pub new_value: ::prost::alloc::string::String, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct FeeGrowthOutside { + #[prost(string, tag="1")] + pub pool_address: ::prost::alloc::string::String, + #[prost(int32, tag="2")] + pub tick_lower: i32, + #[prost(int32, tag="3")] + pub tick_upper: i32, + #[prost(uint64, tag="4")] + pub ordinal: u64, + /// Integer + #[prost(string, tag="5")] + pub new_value: ::prost::alloc::string::String, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct TickCreated { + #[prost(string, tag="1")] + pub pool_address: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="2")] + pub idx: ::prost::alloc::string::String, + #[prost(uint64, tag="3")] + pub log_ordinal: u64, + #[prost(uint64, tag="4")] + pub created_at_timestamp: u64, + #[prost(uint64, tag="5")] + pub created_at_block_number: u64, + /// Decimal + #[prost(string, tag="6")] + pub price0: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="7")] + pub price1: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="8")] + pub amount: ::prost::alloc::string::String, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct TickUpdated { + #[prost(string, tag="1")] + pub pool_address: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="2")] + pub idx: ::prost::alloc::string::String, + #[prost(uint64, tag="3")] + pub log_ordinal: u64, + /// Integer + #[prost(string, tag="4")] + pub fee_growth_outside_0x_128: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="5")] + pub fee_growth_outside_1x_128: ::prost::alloc::string::String, + #[prost(uint64, tag="6")] + pub timestamp: u64, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct PoolSqrtPrice { + #[prost(string, tag="1")] + pub pool_address: ::prost::alloc::string::String, + #[prost(uint64, tag="2")] + pub ordinal: u64, + /// Integer + #[prost(string, tag="3")] + pub sqrt_price: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="4")] + pub tick: ::prost::alloc::string::String, + #[prost(bool, tag="5")] + pub initialized: bool, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct PoolEvent { + #[prost(uint64, tag="100")] + pub log_ordinal: u64, + #[prost(uint64, tag="101")] + pub log_index: u64, + #[prost(string, tag="102")] + pub pool_address: ::prost::alloc::string::String, + #[prost(string, tag="103")] + pub token0: ::prost::alloc::string::String, + #[prost(string, tag="104")] + pub token1: ::prost::alloc::string::String, + #[prost(string, tag="105")] + pub fee: ::prost::alloc::string::String, + #[prost(string, tag="106")] + pub transaction_id: ::prost::alloc::string::String, + #[prost(uint64, tag="107")] + pub timestamp: u64, + #[prost(uint64, tag="108")] + pub created_at_block_number: u64, + #[prost(oneof="pool_event::Type", tags="1, 2, 3")] + pub r#type: ::core::option::Option, + } + /// Nested message and enum types in `PoolEvent`. + pub mod pool_event { + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Swap { + #[prost(string, tag="1")] + pub sender: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub recipient: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub origin: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="4")] + pub amount_0: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="5")] + pub amount_1: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="6")] + pub sqrt_price: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="7")] + pub liquidity: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="8")] + pub tick: ::prost::alloc::string::String, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Burn { + #[prost(string, tag="1")] + pub owner: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub origin: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="3")] + pub amount: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="4")] + pub amount_0: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="5")] + pub amount_1: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="6")] + pub tick_lower: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="7")] + pub tick_upper: ::prost::alloc::string::String, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Mint { + #[prost(string, tag="1")] + pub owner: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub sender: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub origin: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="4")] + pub amount_0: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="5")] + pub amount_1: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="6")] + pub tick_lower: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="7")] + pub tick_upper: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="8")] + pub amount: ::prost::alloc::string::String, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Type { + #[prost(message, tag="1")] + Swap(Swap), + #[prost(message, tag="2")] + Burn(Burn), + #[prost(message, tag="3")] + Mint(Mint), + } + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct PoolLiquidity { + #[prost(string, tag="1")] + pub pool_address: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="2")] + pub liquidity: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub token0: ::prost::alloc::string::String, + #[prost(string, tag="4")] + pub token1: ::prost::alloc::string::String, + /// internals + #[prost(uint64, tag="30")] + pub log_ordinal: u64, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Flash { + #[prost(string, tag="1")] + pub pool_address: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="2")] + pub fee_growth_global_0x_128: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="3")] + pub fee_growth_global_1x_128: ::prost::alloc::string::String, + #[prost(uint64, tag="4")] + pub log_ordinal: u64, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct Transaction { + #[prost(string, tag="1")] + pub id: ::prost::alloc::string::String, + #[prost(uint64, tag="2")] + pub block_number: u64, + #[prost(uint64, tag="3")] + pub timestamp: u64, + #[prost(uint64, tag="4")] + pub gas_used: u64, + /// Integer + #[prost(string, tag="5")] + pub gas_price: ::prost::alloc::string::String, + #[prost(uint64, tag="6")] + pub log_ordinal: u64, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct PositionEvent { + #[prost(oneof="position_event::Type", tags="1, 2, 3, 4, 5")] + pub r#type: ::core::option::Option, + } + /// Nested message and enum types in `PositionEvent`. + pub mod position_event { + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Type { + #[prost(message, tag="1")] + CreatedPosition(super::CreatedPosition), + #[prost(message, tag="2")] + IncreaseLiquidityPosition(super::IncreaseLiquidityPosition), + #[prost(message, tag="3")] + DecreaseLiquidityPosition(super::DecreaseLiquidityPosition), + #[prost(message, tag="4")] + CollectPosition(super::CollectPosition), + #[prost(message, tag="5")] + TransferPosition(super::TransferPosition), + } + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct CreatedPosition { + #[prost(string, tag="1")] + pub token_id: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub pool: ::prost::alloc::string::String, + #[prost(string, tag="3")] + pub token0: ::prost::alloc::string::String, + #[prost(string, tag="4")] + pub token1: ::prost::alloc::string::String, + #[prost(string, tag="5")] + pub tick_lower: ::prost::alloc::string::String, + #[prost(string, tag="6")] + pub tick_upper: ::prost::alloc::string::String, + #[prost(string, tag="7")] + pub transaction: ::prost::alloc::string::String, + #[prost(uint64, tag="8")] + pub log_ordinal: u64, + #[prost(uint64, tag="9")] + pub timestamp: u64, + #[prost(uint64, tag="10")] + pub block_number: u64, + /// BigInt + #[prost(string, optional, tag="11")] + pub fee_growth_inside0_last_x128: ::core::option::Option<::prost::alloc::string::String>, + /// BigInt + #[prost(string, optional, tag="12")] + pub fee_growth_inside1_last_x128: ::core::option::Option<::prost::alloc::string::String>, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct IncreaseLiquidityPosition { + #[prost(string, tag="1")] + pub token_id: ::prost::alloc::string::String, + /// BigInt + #[prost(string, tag="2")] + pub liquidity: ::prost::alloc::string::String, + /// BigDecimal + #[prost(string, tag="3")] + pub deposited_token0: ::prost::alloc::string::String, + /// BigDecimal + #[prost(string, tag="4")] + pub deposited_token1: ::prost::alloc::string::String, + /// BigInt + #[prost(string, optional, tag="5")] + pub fee_growth_inside0_last_x128: ::core::option::Option<::prost::alloc::string::String>, + /// BigInt + #[prost(string, optional, tag="6")] + pub fee_growth_inside1_last_x128: ::core::option::Option<::prost::alloc::string::String>, + #[prost(uint64, tag="10")] + pub log_ordinal: u64, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct DecreaseLiquidityPosition { + #[prost(string, tag="1")] + pub token_id: ::prost::alloc::string::String, + /// BigInt + #[prost(string, tag="2")] + pub liquidity: ::prost::alloc::string::String, + /// BigDecimal + #[prost(string, tag="3")] + pub withdrawn_token0: ::prost::alloc::string::String, + /// BigDecimal + #[prost(string, tag="4")] + pub withdrawn_token1: ::prost::alloc::string::String, + /// BigInt + #[prost(string, optional, tag="5")] + pub fee_growth_inside0_last_x128: ::core::option::Option<::prost::alloc::string::String>, + /// BigInt + #[prost(string, optional, tag="6")] + pub fee_growth_inside1_last_x128: ::core::option::Option<::prost::alloc::string::String>, + #[prost(uint64, tag="10")] + pub log_ordinal: u64, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct CollectPosition { + #[prost(string, tag="1")] + pub token_id: ::prost::alloc::string::String, + /// BigInt + #[prost(string, tag="2")] + pub collected_fees_token0: ::prost::alloc::string::String, + /// BigInt + #[prost(string, tag="3")] + pub collected_fees_token1: ::prost::alloc::string::String, + /// BigInt + #[prost(string, optional, tag="5")] + pub fee_growth_inside0_last_x128: ::core::option::Option<::prost::alloc::string::String>, + /// BigInt + #[prost(string, optional, tag="6")] + pub fee_growth_inside1_last_x128: ::core::option::Option<::prost::alloc::string::String>, + #[prost(uint64, tag="10")] + pub log_ordinal: u64, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct TransferPosition { + #[prost(string, tag="1")] + pub token_id: ::prost::alloc::string::String, + #[prost(string, tag="2")] + pub owner: ::prost::alloc::string::String, + #[prost(uint64, tag="10")] + pub log_ordinal: u64, + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SnapshotPositions { + #[prost(message, repeated, tag="1")] + pub snapshot_positions: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SnapshotPosition { + #[prost(string, tag="1")] + pub pool: ::prost::alloc::string::String, + /// the token_id of the position + #[prost(string, tag="2")] + pub position: ::prost::alloc::string::String, + #[prost(uint64, tag="3")] + pub block_number: u64, + #[prost(string, tag="4")] + pub owner: ::prost::alloc::string::String, + #[prost(uint64, tag="6")] + pub timestamp: u64, + /// Decimal + #[prost(string, tag="7")] + pub liquidity: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="8")] + pub deposited_token0: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="9")] + pub deposited_token1: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="10")] + pub withdrawn_token0: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="11")] + pub withdrawn_token1: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="12")] + pub collected_fees_token0: ::prost::alloc::string::String, + /// Decimal + #[prost(string, tag="13")] + pub collected_fees_token1: ::prost::alloc::string::String, + #[prost(string, tag="14")] + pub transaction: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="15")] + pub fee_growth_inside_0_last_x_128: ::prost::alloc::string::String, + /// Integer + #[prost(string, tag="16")] + pub fee_growth_inside_1_last_x_128: ::prost::alloc::string::String, + /// internal + #[prost(uint64, tag="17")] + pub log_ordinal: u64, +} +// @@protoc_insertion_point(module) diff --git a/substreams/ethereum-uniswap-v3/src/pb/uniswap.v3.rs b/substreams/ethereum-uniswap-v3/src/pb/uniswap.v3.rs new file mode 100644 index 0000000..92c93b4 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/pb/uniswap.v3.rs @@ -0,0 +1,41 @@ +// @generated +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Pool { + #[prost(bytes="vec", tag="1")] + pub address: ::prost::alloc::vec::Vec, + #[prost(bytes="vec", tag="2")] + pub token0: ::prost::alloc::vec::Vec, + #[prost(bytes="vec", tag="3")] + pub token1: ::prost::alloc::vec::Vec, + #[prost(bytes="vec", tag="4")] + pub created_tx_hash: ::prost::alloc::vec::Vec, +} +/// A change to a pool's balance. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BalanceDelta { + /// The address of the ERC20 token. + #[prost(bytes="vec", tag="1")] + pub token_address: ::prost::alloc::vec::Vec, + /// The delta of the token. + #[prost(bytes="vec", tag="2")] + pub amount: ::prost::alloc::vec::Vec, + /// The sign of the delta, true for positive, false for negative. + #[prost(bool, tag="3")] + pub sign: bool, + /// The address of the pool whose balance changed. + #[prost(bytes="vec", tag="4")] + pub pool_address: ::prost::alloc::vec::Vec, + /// Used to determine the order of the balance changes. Necessary for the balance store. + #[prost(uint64, tag="5")] + pub ordinal: u64, +} +/// A group of BalanceDelta +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BalanceDeltas { + #[prost(message, repeated, tag="1")] + pub deltas: ::prost::alloc::vec::Vec, +} +// @@protoc_insertion_point(module) diff --git a/substreams/ethereum-uniswap-v3/src/storage/constants.rs b/substreams/ethereum-uniswap-v3/src/storage/constants.rs new file mode 100644 index 0000000..6e77e1c --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/storage/constants.rs @@ -0,0 +1,60 @@ +use hex_literal::hex; + +use super::pool_storage::StorageLocation; + +const SLOT0: [u8; 32] = hex!("0000000000000000000000000000000000000000000000000000000000000000"); + +const LIQUIDITY_SLOT: StorageLocation = StorageLocation { + name: "liquidity", + slot: hex!("0000000000000000000000000000000000000000000000000000000000000004"), + offset: 0, + number_of_bytes: 16, + signed: false, +}; + +const PROTOCOL_FEES_TOKEN_0_SLOT: StorageLocation = StorageLocation { + name: "protocol_fees/token0", + slot: hex!("0000000000000000000000000000000000000000000000000000000000000003"), + offset: 0, + number_of_bytes: 16, + signed: false, +}; + +const PROTOCOL_FEES_TOKEN_1_SLOT: StorageLocation = StorageLocation { + name: "protocol_fees/token1", + slot: hex!("0000000000000000000000000000000000000000000000000000000000000003"), + offset: 16, + number_of_bytes: 16, + signed: false, +}; + +const SQRT_PRICE_X96_SLOT: StorageLocation = StorageLocation { + name: "sqrt_price_x96", + slot: SLOT0, + offset: 0, + number_of_bytes: 20, + signed: false, +}; + +const CURRENT_TICK_SLOT: StorageLocation = + StorageLocation { name: "tick", slot: SLOT0, offset: 20, number_of_bytes: 3, signed: true }; + +const FEE_PROTOCOL_SLOT: StorageLocation = StorageLocation { + name: "fee_protocol", + slot: SLOT0, + offset: 29, + number_of_bytes: 1, + signed: false, +}; + +pub(crate) const TICKS_MAP_SLOT: [u8; 32] = + hex!("0000000000000000000000000000000000000000000000000000000000000005"); + +pub(crate) const TRACKED_SLOTS: [StorageLocation; 6] = [ + LIQUIDITY_SLOT, + PROTOCOL_FEES_TOKEN_0_SLOT, + PROTOCOL_FEES_TOKEN_1_SLOT, + SQRT_PRICE_X96_SLOT, + CURRENT_TICK_SLOT, + FEE_PROTOCOL_SLOT, +]; diff --git a/substreams/ethereum-uniswap-v3/src/storage/mod.rs b/substreams/ethereum-uniswap-v3/src/storage/mod.rs new file mode 100644 index 0000000..58b49ca --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/storage/mod.rs @@ -0,0 +1,4 @@ +pub mod pool_storage; + +pub mod constants; +mod utils; diff --git a/substreams/ethereum-uniswap-v3/src/storage/pool_storage.rs b/substreams/ethereum-uniswap-v3/src/storage/pool_storage.rs new file mode 100644 index 0000000..a0997f9 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/storage/pool_storage.rs @@ -0,0 +1,132 @@ +use crate::{ + pb::tycho::evm::v1::{Attribute, ChangeType}, + storage::utils, +}; + +use substreams::scalar::BigInt; +use substreams_ethereum::pb::eth::v2::StorageChange; + +use super::{constants::TICKS_MAP_SLOT, utils::read_bytes}; + +/// `StorageLocation` is a struct that represents a specific location within a contract's storage +/// associated with a name. +/// +/// # Fields +/// +/// * `name` - A string slice (`&str`) reference representing the unique name associated with this +/// storage location. +/// * `slot` - A fixed-size byte array `[u8; 32]` representing the slot in the contract storage +/// where this data is stored. This acts as a primary identifier for the location of the data. +/// * `offset` - A usize value indicating the offset in bytes from the start of the slot. This +/// allows for fine-grained control and access within a single slot. +/// * `number_of_bytes` - A usize value indicating the size of the data in bytes. +/// ``` +#[derive(Clone)] +pub struct StorageLocation<'a> { + pub name: &'a str, + pub slot: [u8; 32], + pub offset: usize, + pub number_of_bytes: usize, + pub signed: bool, +} + +pub struct UniswapPoolStorage<'a> { + pub storage_changes: &'a Vec, +} + +impl<'a> UniswapPoolStorage<'a> { + pub fn new(storage_changes: &'a Vec) -> UniswapPoolStorage<'a> { + Self { storage_changes } + } + + /// Iterates through storage changes and checks for modifications in the provided list of + /// storage locations. For each change, it compares the old and new values at the specified + /// offset and length for that location. If a change is detected, it's added to the returned + /// `Attribute` list. + /// + /// Arguments: + /// locations: Vec<&StorageLocation> - A vector of references to StorageLocation objects + /// that define the slots, offsets, and lengths to be checked for changes. + /// + /// Returns: + /// `Vec`: A vector containing Attributes for each change detected in the tracked + /// slots. Returns an empty vector if no changes are detected. + pub fn get_changed_attributes(&self, locations: Vec<&StorageLocation>) -> Vec { + let mut attributes = Vec::new(); + + // For each storage change, check if it changes a tracked slot. + // If it does, add the attribute to the list of attributes + for change in self.storage_changes { + for storage_location in locations.iter() { + // Check if the change slot matches the tracked slot + if change.key == storage_location.slot { + let old_data = read_bytes( + &change.old_value, + storage_location.offset, + storage_location.number_of_bytes, + ); + let new_data = read_bytes( + &change.new_value, + storage_location.offset, + storage_location.number_of_bytes, + ); + + // Check if there is a change in the data + if old_data != new_data { + let value = match storage_location.signed { + true => BigInt::from_signed_bytes_be(new_data), + false => BigInt::from_unsigned_bytes_be(new_data), + }; + attributes.push(Attribute { + name: storage_location.name.to_string(), + value: value.to_signed_bytes_le(), + change: ChangeType::Update.into(), + }); + } + } + } + } + + attributes + } + + /// Iterates over a list of tick indexes and checks for modifications in the list of + /// storage changes. If a relevent change is detected, it's added to the returned `Attribute` + /// list. + /// + /// Arguments: + /// ticks_idx: `Vec<&BigInt>` - A vector of references to tick indexes as BigInt objects. + /// + /// Returns: + /// `Vec`: A vector containing Attributes for each change detected. Returns an + /// empty vector if no changes are detected. + /// + /// Note: Currently, we only track the net-liquidity attribute for each tick. + pub fn get_ticks_changes(&self, ticks_idx: Vec<&BigInt>) -> Vec { + let mut storage_locs = Vec::new(); + let mut tick_names = Vec::new(); + + // First, create all the names and push them into tick_names. + // We need this to keep the references to the names alive until we call + // `get_changed_attributes()` + for tick_idx in ticks_idx.iter() { + tick_names.push(format!("ticks/{}/net-liquidity", tick_idx)); + } + + // Then, iterate over ticks_idx and tick_names simultaneously + for (tick_idx, tick_name) in ticks_idx.iter().zip(tick_names.iter()) { + let tick_slot = + utils::calc_map_slot(&utils::left_pad_from_bigint(tick_idx), &TICKS_MAP_SLOT); + + storage_locs.push(StorageLocation { + name: tick_name, + slot: tick_slot, + offset: 16, + number_of_bytes: 16, + signed: true, + }); + } + + self.get_changed_attributes(storage_locs.iter().collect()) + } +} diff --git a/substreams/ethereum-uniswap-v3/src/storage/utils.rs b/substreams/ethereum-uniswap-v3/src/storage/utils.rs new file mode 100644 index 0000000..827bd8e --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/storage/utils.rs @@ -0,0 +1,165 @@ +use substreams::scalar::BigInt; +use tiny_keccak::{Hasher, Keccak}; + +pub fn calc_map_slot(map_index: &[u8; 32], base_slot: &[u8; 32]) -> [u8; 32] { + let mut output = [0u8; 32]; + let mut hasher = Keccak::v256(); + hasher.update(map_index); + hasher.update(base_slot); + hasher.finalize(&mut output); + output +} + +pub fn left_pad_from_bigint(input: &BigInt) -> [u8; 32] { + if input.lt(&BigInt::zero()) { + return left_pad(&input.to_signed_bytes_be(), 255); + } + + left_pad(&input.to_signed_bytes_be(), 0) +} + +pub fn left_pad(input: &[u8], padding_value: u8) -> [u8; 32] { + if input.len() > 32 { + panic!("cannot convert vec to H256"); + } + let mut data = [padding_value; 32]; + let offset = 32 - input.len(); + data[offset..(input.len() + offset)].copy_from_slice(input); + + data +} + +pub fn read_bytes(buf: &[u8], offset: usize, number_of_bytes: usize) -> &[u8] { + let buf_length = buf.len(); + if buf_length < number_of_bytes { + panic!( + "attempting to read {number_of_bytes} bytes in buffer size {buf_size}", + number_of_bytes = number_of_bytes, + buf_size = buf.len() + ) + } + + if offset > (buf_length - 1) { + panic!( + "offset {offset} exceeds buffer size {buf_size}", + offset = offset, + buf_size = buf.len() + ) + } + + let end = buf_length - 1 - offset; + let start_opt = (end + 1).checked_sub(number_of_bytes); + if start_opt.is_none() { + panic!( + "number of bytes {number_of_bytes} with offset {offset} exceeds buffer size +{buf_size}", + number_of_bytes = number_of_bytes, + offset = offset, + buf_size = buf.len() + ) + } + let start = start_opt.unwrap(); + + &buf[start..=end] +} + +#[cfg(test)] +mod tests { + use crate::storage::utils::{left_pad, read_bytes}; + use hex_literal::hex; + use std::{fmt::Write, num::ParseIntError}; + + #[test] + fn left_pad_lt_32_bytes() { + let input = hex!("dd62ed3e"); + assert_eq!( + hex!("00000000000000000000000000000000000000000000000000000000dd62ed3e"), + left_pad(&input, 0) + ) + } + + #[test] + fn left_pad_eq_32_bytes() { + let input = hex!("00000a0000000000005d000000000000000000000000000000000000dd62ed3e"); + assert_eq!( + hex!("00000a0000000000005d000000000000000000000000000000000000dd62ed3e"), + left_pad(&input, 0) + ) + } + + #[test] + #[should_panic] + fn left_pad_gt_32_bytes() { + let input = hex!("070000000a0000000000005d000000000000000000000000000000000000dd62ed3e"); + let _ = left_pad(&input, 0); + } + + #[test] + #[should_panic] + fn read_bytes_buf_too_small() { + let buf = decode_hex("ff").unwrap(); + let offset = 0; + let number_of_bytes = 3; + let _ = read_bytes(&buf, offset, number_of_bytes); + } + + #[test] + fn read_one_byte_with_no_offset() { + let buf = decode_hex("aabb").unwrap(); + let offset = 0; + let number_of_bytes = 1; + assert_eq!(read_bytes(&buf, offset, number_of_bytes), hex!("bb")); + } + + #[test] + fn read_one_byte_with_offset() { + let buf = decode_hex("aabb").unwrap(); + let offset = 1; + let number_of_bytes = 1; + assert_eq!(read_bytes(&buf, offset, number_of_bytes), hex!("aa")); + } + + #[test] + #[should_panic] + fn read_bytes_overflow() { + let buf = decode_hex("aabb").unwrap(); + let offset = 1; + let number_of_bytes = 2; + let _ = read_bytes(&buf, offset, number_of_bytes); + } + + #[test] + fn read_bytes_with_no_offset() { + let buf = + decode_hex("ffffffffffffffffffffecb6826b89a60000000000000000000013497d94765a").unwrap(); + let offset = 0; + let number_of_bytes = 16; + let out = read_bytes(&buf, offset, number_of_bytes); + assert_eq!(encode_hex(out), "0000000000000000000013497d94765a".to_string()); + } + + #[test] + fn read_byte_with_big_offset() { + let buf = + decode_hex("0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + let offset = 31; + let number_of_bytes = 1; + let out = read_bytes(&buf, offset, number_of_bytes); + assert_eq!(encode_hex(out), "01".to_string()); + } + + fn decode_hex(s: &str) -> Result, ParseIntError> { + (0..s.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) + .collect() + } + + fn encode_hex(bytes: &[u8]) -> String { + let mut s = String::with_capacity(bytes.len() * 2); + for &b in bytes { + write!(&mut s, "{:02x}", b).unwrap(); + } + s + } +} diff --git a/substreams/ethereum-uniswap-v3/src/traits.rs b/substreams/ethereum-uniswap-v3/src/traits.rs new file mode 100644 index 0000000..4f573b8 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/src/traits.rs @@ -0,0 +1,30 @@ +use substreams_ethereum::pb::eth::v2::{self as eth}; + +use crate::pb::tycho::evm::v1::{Block, Transaction}; + +impl From for Block { + fn from(block: eth::Block) -> Self { + Self { + hash: block.hash.clone(), + parent_hash: block + .header + .as_ref() + .expect("Block header not present") + .parent_hash + .clone(), + number: block.number, + ts: block.timestamp_seconds(), + } + } +} + +impl From<ð::TransactionTrace> for Transaction { + fn from(tx: ð::TransactionTrace) -> Self { + Self { + hash: tx.hash.clone(), + from: tx.from.clone(), + to: tx.to.clone(), + index: tx.index.into(), + } + } +} From d7cc19c4ff826b76552a1405bff5f2f11e82cdd2 Mon Sep 17 00:00:00 2001 From: zizou <111426680+flopell@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:11:19 +0200 Subject: [PATCH 2/8] refactor(substreams): refactor uniswapv2 to use `tycho-substreams` --- substreams/Cargo.lock | 1 + substreams/ethereum-uniswap-v2/Cargo.toml | 1 + .../arbitrum-uniswap-v2.yaml | 3 - substreams/ethereum-uniswap-v2/buf.gen.yaml | 12 + .../ethereum-pancakeswap.yaml | 3 - .../ethereum-sushiswap.yaml | 3 - .../ethereum-uniswap-v2.yaml | 3 - .../src/modules/1_map_pool_created.rs | 11 +- .../src/modules/2_store_pools.rs | 6 +- .../src/modules/3_map_pool_events.rs | 16 +- substreams/ethereum-uniswap-v2/src/pb/mod.rs | 5 - .../src/pb/tycho.evm.v1.rs | 307 ------------------ substreams/ethereum-uniswap-v2/src/traits.rs | 35 +- 13 files changed, 25 insertions(+), 381 deletions(-) create mode 100644 substreams/ethereum-uniswap-v2/buf.gen.yaml delete mode 100644 substreams/ethereum-uniswap-v2/src/pb/tycho.evm.v1.rs diff --git a/substreams/Cargo.lock b/substreams/Cargo.lock index 0654435..84e0b41 100644 --- a/substreams/Cargo.lock +++ b/substreams/Cargo.lock @@ -1051,6 +1051,7 @@ dependencies = [ "substreams", "substreams-ethereum", "substreams-helper", + "tycho-substreams", ] [[package]] diff --git a/substreams/ethereum-uniswap-v2/Cargo.toml b/substreams/ethereum-uniswap-v2/Cargo.toml index ab2e76a..6348817 100644 --- a/substreams/ethereum-uniswap-v2/Cargo.toml +++ b/substreams/ethereum-uniswap-v2/Cargo.toml @@ -15,6 +15,7 @@ ethabi.workspace = true anyhow = { workspace = true, features = [] } hex-literal.workspace = true substreams-helper.workspace = true +tycho-substreams.workspace = true num-bigint = "0.4.4" itertools = "0.12.1" serde_qs = "0.13.0" diff --git a/substreams/ethereum-uniswap-v2/arbitrum-uniswap-v2.yaml b/substreams/ethereum-uniswap-v2/arbitrum-uniswap-v2.yaml index 7f287bc..22b4ee3 100644 --- a/substreams/ethereum-uniswap-v2/arbitrum-uniswap-v2.yaml +++ b/substreams/ethereum-uniswap-v2/arbitrum-uniswap-v2.yaml @@ -5,12 +5,9 @@ package: protobuf: files: - - tycho/evm/v1/common.proto - - tycho/evm/v1/entity.proto - uniswap.proto importPaths: - ./proto/v1 - - ../../proto/ binaries: default: diff --git a/substreams/ethereum-uniswap-v2/buf.gen.yaml b/substreams/ethereum-uniswap-v2/buf.gen.yaml new file mode 100644 index 0000000..d2e6544 --- /dev/null +++ b/substreams/ethereum-uniswap-v2/buf.gen.yaml @@ -0,0 +1,12 @@ + +version: v1 +plugins: +- plugin: buf.build/community/neoeinstein-prost:v0.2.2 + out: src/pb + opt: + - file_descriptor_set=false + +- plugin: buf.build/community/neoeinstein-prost-crate:v0.3.1 + out: src/pb + opt: + - no_features diff --git a/substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml b/substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml index 7424d6c..1944e69 100644 --- a/substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml +++ b/substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml @@ -5,12 +5,9 @@ package: protobuf: files: - - tycho/evm/v1/common.proto - - tycho/evm/v1/entity.proto - uniswap.proto importPaths: - ./proto/v1 - - ../../proto/ binaries: default: diff --git a/substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml b/substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml index e5334d1..8a9233c 100644 --- a/substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml +++ b/substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml @@ -5,12 +5,9 @@ package: protobuf: files: - - tycho/evm/v1/common.proto - - tycho/evm/v1/entity.proto - uniswap.proto importPaths: - ./proto/v1 - - ../../proto/ binaries: default: diff --git a/substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml b/substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml index 1ed8f63..c1e8bb1 100644 --- a/substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml +++ b/substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml @@ -5,12 +5,9 @@ package: protobuf: files: - - tycho/evm/v1/common.proto - - tycho/evm/v1/entity.proto - uniswap.proto importPaths: - ./proto/v1 - - ../../proto/ binaries: default: diff --git a/substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs b/substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs index 8e6a6dd..582706c 100644 --- a/substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs +++ b/substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs @@ -7,14 +7,9 @@ use substreams_ethereum::pb::eth::v2::{self as eth}; use substreams_helper::{event_handler::EventHandler, hex::Hexable}; -use crate::{ - abi::factory::events::PairCreated, - pb::tycho::evm::v1::{ - Attribute, Block, BlockChanges, ChangeType, EntityChanges, FinancialType, - ImplementationType, ProtocolComponent, ProtocolType, Transaction, TransactionChanges, - }, -}; +use crate::abi::factory::events::PairCreated; +use tycho_substreams::prelude::*; #[derive(Debug, Deserialize)] struct Params { factory_address: String, @@ -32,7 +27,7 @@ pub fn map_pools_created( get_pools(&block, &mut new_pools, ¶ms); - let tycho_block: Block = block.into(); + let tycho_block: Block = (&block).into(); Ok(BlockChanges { block: Some(tycho_block), changes: new_pools }) } diff --git a/substreams/ethereum-uniswap-v2/src/modules/2_store_pools.rs b/substreams/ethereum-uniswap-v2/src/modules/2_store_pools.rs index b0d8256..6b85384 100644 --- a/substreams/ethereum-uniswap-v2/src/modules/2_store_pools.rs +++ b/substreams/ethereum-uniswap-v2/src/modules/2_store_pools.rs @@ -1,9 +1,7 @@ use substreams::store::{StoreNew, StoreSetIfNotExists, StoreSetIfNotExistsProto}; -use crate::{ - pb::tycho::evm::v1::{BlockChanges, ProtocolComponent}, - store_key::StoreKey, -}; +use crate::store_key::StoreKey; +use tycho_substreams::prelude::*; #[substreams::handlers::store] pub fn store_pools( diff --git a/substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs b/substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs index c4e902d..2e6b264 100644 --- a/substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs +++ b/substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs @@ -5,18 +5,8 @@ use substreams_ethereum::pb::eth::v2::{self as eth}; use substreams_helper::{event_handler::EventHandler, hex::Hexable}; -use crate::{ - abi::pool::events::Sync, - pb::tycho::evm::{ - v1, - v1::{ - Attribute, BalanceChange, BlockChanges, ChangeType, EntityChanges, ProtocolComponent, - TransactionChanges, - }, - }, - store_key::StoreKey, - traits::PoolAddresser, -}; +use crate::{abi::pool::events::Sync, store_key::StoreKey, traits::PoolAddresser}; +use tycho_substreams::prelude::*; // Auxiliary struct to serve as a key for the HashMaps. #[derive(Clone, Hash, Eq, PartialEq)] @@ -33,7 +23,7 @@ impl ComponentKey { #[derive(Clone)] struct PartialChanges { - transaction: v1::Transaction, + transaction: Transaction, entity_changes: HashMap, Attribute>, balance_changes: HashMap>, BalanceChange>, } diff --git a/substreams/ethereum-uniswap-v2/src/pb/mod.rs b/substreams/ethereum-uniswap-v2/src/pb/mod.rs index f868620..c200c9b 100644 --- a/substreams/ethereum-uniswap-v2/src/pb/mod.rs +++ b/substreams/ethereum-uniswap-v2/src/pb/mod.rs @@ -8,10 +8,5 @@ pub mod tycho { // @@protoc_insertion_point(tycho.evm.uniswap.v2) } } - // @@protoc_insertion_point(attribute:tycho.evm.v1) - pub mod v1 { - include!("tycho.evm.v1.rs"); - // @@protoc_insertion_point(tycho.evm.v1) - } } } diff --git a/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.v1.rs b/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.v1.rs deleted file mode 100644 index 6dac022..0000000 --- a/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.v1.rs +++ /dev/null @@ -1,307 +0,0 @@ -// @generated -// This file contains the proto definitions for Substreams common to all integrations. - -/// A struct describing a block. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Block { - /// The blocks hash. - #[prost(bytes="vec", tag="1")] - pub hash: ::prost::alloc::vec::Vec, - /// The parent blocks hash. - #[prost(bytes="vec", tag="2")] - pub parent_hash: ::prost::alloc::vec::Vec, - /// The block number. - #[prost(uint64, tag="3")] - pub number: u64, - /// The block timestamp. - #[prost(uint64, tag="4")] - pub ts: u64, -} -/// A struct describing a transaction. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Transaction { - /// The transaction hash. - #[prost(bytes="vec", tag="1")] - pub hash: ::prost::alloc::vec::Vec, - /// The sender of the transaction. - #[prost(bytes="vec", tag="2")] - pub from: ::prost::alloc::vec::Vec, - /// The receiver of the transaction. - #[prost(bytes="vec", tag="3")] - pub to: ::prost::alloc::vec::Vec, - /// The transactions index within the block. - #[prost(uint64, tag="4")] - pub index: u64, -} -/// A custom struct representing an arbitrary attribute of a protocol component. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Attribute { - /// The name of the attribute. - #[prost(string, tag="1")] - pub name: ::prost::alloc::string::String, - /// The value of the attribute. - #[prost(bytes="vec", tag="2")] - pub value: ::prost::alloc::vec::Vec, - /// The type of change the attribute underwent. - #[prost(enumeration="ChangeType", tag="3")] - pub change: i32, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ProtocolType { - #[prost(string, tag="1")] - pub name: ::prost::alloc::string::String, - #[prost(enumeration="FinancialType", tag="2")] - pub financial_type: i32, - #[prost(message, repeated, tag="3")] - pub attribute_schema: ::prost::alloc::vec::Vec, - #[prost(enumeration="ImplementationType", tag="4")] - pub implementation_type: i32, -} -/// A struct describing a part of the protocol. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ProtocolComponent { - /// A unique identifier for the component within the protocol. - /// Can be a stringified address or a string describing the trading pair. - #[prost(string, tag="1")] - pub id: ::prost::alloc::string::String, - /// Addresses of the ERC20 tokens used by the component. - #[prost(bytes="vec", repeated, tag="2")] - pub tokens: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - /// Addresses of the contracts used by the component. - #[prost(bytes="vec", repeated, tag="3")] - pub contracts: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - /// Attributes of the component. - /// The inner ChangeType of the attribute has to match the ChangeType of the ProtocolComponent. - #[prost(message, repeated, tag="4")] - pub static_att: ::prost::alloc::vec::Vec, - /// Type of change the component underwent. - #[prost(enumeration="ChangeType", tag="5")] - pub change: i32, - /// / Represents the functionality of the component. - #[prost(message, optional, tag="6")] - pub protocol_type: ::core::option::Option, - /// Transaction where this component was created - #[prost(message, optional, tag="7")] - pub tx: ::core::option::Option, -} -/// A struct for following the changes of Total Value Locked (TVL) of a protocol component. -/// Note that if the ProtocolComponent contains multiple contracts, the TVL is tracked for the component as a whole. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BalanceChange { - /// The address of the ERC20 token whose balance changed. - #[prost(bytes="vec", tag="1")] - pub token: ::prost::alloc::vec::Vec, - /// The new balance of the token. - #[prost(bytes="vec", tag="2")] - pub balance: ::prost::alloc::vec::Vec, - /// The id of the component whose TVL is tracked. Note: This MUST be utf8 encoded. - #[prost(bytes="vec", tag="3")] - pub component_id: ::prost::alloc::vec::Vec, -} -// Native entities - -/// A component is a set of attributes that are associated with a custom entity. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EntityChanges { - /// A unique identifier of the entity within the protocol. - #[prost(string, tag="1")] - pub component_id: ::prost::alloc::string::String, - /// The set of attributes that are associated with the entity. - #[prost(message, repeated, tag="2")] - pub attributes: ::prost::alloc::vec::Vec, -} -// VM entities - -/// A key value entry into contract storage. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ContractSlot { - /// A contract's storage slot. - #[prost(bytes="vec", tag="2")] - pub slot: ::prost::alloc::vec::Vec, - /// The new value for this storage slot. - #[prost(bytes="vec", tag="3")] - pub value: ::prost::alloc::vec::Vec, -} -/// Changes made to a single contract's state. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ContractChange { - /// The contract's address - #[prost(bytes="vec", tag="1")] - pub address: ::prost::alloc::vec::Vec, - /// The new balance of the contract, empty bytes indicates no change. - #[prost(bytes="vec", tag="2")] - pub balance: ::prost::alloc::vec::Vec, - /// The new code of the contract, empty bytes indicates no change. - #[prost(bytes="vec", tag="3")] - pub code: ::prost::alloc::vec::Vec, - /// The changes to this contract's slots, empty sequence indicates no change. - #[prost(message, repeated, tag="4")] - pub slots: ::prost::alloc::vec::Vec, - /// Whether this is an update, a creation or a deletion. - #[prost(enumeration="ChangeType", tag="5")] - pub change: i32, -} -// Aggregate entities - -/// A set of changes aggregated by transaction. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct TransactionChanges { - /// The transaction instance that results in the changes. - #[prost(message, optional, tag="1")] - pub tx: ::core::option::Option, - /// Contains the changes induced by the above transaction, aggregated on a per-contract basis. - /// Contains the contract changes induced by the above transaction, usually for tracking VM components. - #[prost(message, repeated, tag="2")] - pub contract_changes: ::prost::alloc::vec::Vec, - /// Contains the entity changes induced by the above transaction. - /// Usually for tracking native components or used for VM extensions (plugins). - #[prost(message, repeated, tag="3")] - pub entity_changes: ::prost::alloc::vec::Vec, - /// An array of newly added components. - #[prost(message, repeated, tag="4")] - pub component_changes: ::prost::alloc::vec::Vec, - /// An array of balance changes to components. - #[prost(message, repeated, tag="5")] - pub balance_changes: ::prost::alloc::vec::Vec, -} -/// A set of transaction changes within a single block. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockChanges { - /// The block for which these changes are collectively computed. - #[prost(message, optional, tag="1")] - pub block: ::core::option::Option, - /// The set of transaction changes observed in the specified block. - #[prost(message, repeated, tag="2")] - pub changes: ::prost::alloc::vec::Vec, -} -/// Enum to specify the type of a change. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum ChangeType { - Unspecified = 0, - Update = 1, - Creation = 2, - Deletion = 3, -} -impl ChangeType { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - ChangeType::Unspecified => "CHANGE_TYPE_UNSPECIFIED", - ChangeType::Update => "CHANGE_TYPE_UPDATE", - ChangeType::Creation => "CHANGE_TYPE_CREATION", - ChangeType::Deletion => "CHANGE_TYPE_DELETION", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "CHANGE_TYPE_UNSPECIFIED" => Some(Self::Unspecified), - "CHANGE_TYPE_UPDATE" => Some(Self::Update), - "CHANGE_TYPE_CREATION" => Some(Self::Creation), - "CHANGE_TYPE_DELETION" => Some(Self::Deletion), - _ => None, - } - } -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum FinancialType { - Swap = 0, - Lend = 1, - Leverage = 2, - Psm = 3, -} -impl FinancialType { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - FinancialType::Swap => "SWAP", - FinancialType::Lend => "LEND", - FinancialType::Leverage => "LEVERAGE", - FinancialType::Psm => "PSM", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "SWAP" => Some(Self::Swap), - "LEND" => Some(Self::Lend), - "LEVERAGE" => Some(Self::Leverage), - "PSM" => Some(Self::Psm), - _ => None, - } - } -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum ImplementationType { - Vm = 0, - Custom = 1, -} -impl ImplementationType { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - ImplementationType::Vm => "VM", - ImplementationType::Custom => "CUSTOM", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "VM" => Some(Self::Vm), - "CUSTOM" => Some(Self::Custom), - _ => None, - } - } -} -// WARNING: DEPRECATED. Please use common.proto's TransactionChanges and BlockChanges instead. -// This file contains the definition for the native integration of Substreams. - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct TransactionEntityChanges { - #[prost(message, optional, tag="1")] - pub tx: ::core::option::Option, - #[prost(message, repeated, tag="2")] - pub entity_changes: ::prost::alloc::vec::Vec, - /// An array of newly added components. - #[prost(message, repeated, tag="3")] - pub component_changes: ::prost::alloc::vec::Vec, - /// An array of balance changes to components. - #[prost(message, repeated, tag="4")] - pub balance_changes: ::prost::alloc::vec::Vec, -} -/// A set of transaction changes within a single block. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockEntityChanges { - /// The block for which these changes are collectively computed. - #[prost(message, optional, tag="1")] - pub block: ::core::option::Option, - /// The set of transaction changes observed in the specified block. - #[prost(message, repeated, tag="2")] - pub changes: ::prost::alloc::vec::Vec, -} -// @@protoc_insertion_point(module) diff --git a/substreams/ethereum-uniswap-v2/src/traits.rs b/substreams/ethereum-uniswap-v2/src/traits.rs index 9edd693..b3aa69d 100644 --- a/substreams/ethereum-uniswap-v2/src/traits.rs +++ b/substreams/ethereum-uniswap-v2/src/traits.rs @@ -1,13 +1,11 @@ use ethabi::ethereum_types::Address; use substreams::store::{StoreGet, StoreGetProto}; -use substreams_ethereum::pb::eth::v2::{self as eth}; use substreams_helper::{common::HasAddresser, hex::Hexable}; -use crate::{ - pb::tycho::evm::v1::{Block, ProtocolComponent, Transaction}, - store_key::StoreKey, -}; +use tycho_substreams::prelude::*; + +use crate::store_key::StoreKey; pub struct PoolAddresser<'a> { pub store: &'a StoreGetProto, @@ -22,30 +20,3 @@ impl<'a> HasAddresser for PoolAddresser<'a> { pool.is_some() } } - -impl From for Block { - fn from(block: eth::Block) -> Self { - Self { - hash: block.hash.clone(), - parent_hash: block - .header - .as_ref() - .expect("Block header not present") - .parent_hash - .clone(), - number: block.number, - ts: block.timestamp_seconds(), - } - } -} - -impl From<ð::TransactionTrace> for Transaction { - fn from(tx: ð::TransactionTrace) -> Self { - Self { - hash: tx.hash.clone(), - from: tx.from.clone(), - to: tx.to.clone(), - index: tx.index.into(), - } - } -} From 050ae59a83f846203829cde4072a8351426ab666 Mon Sep 17 00:00:00 2001 From: zizou <111426680+flopell@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:18:22 +0200 Subject: [PATCH 3/8] refactor(substreams): refactor uniswapv3 to use `tycho-substreams` --- substreams/Cargo.lock | 1 + substreams/ethereum-uniswap-v3/Cargo.toml | 1 + .../arbitrum-uniswap-v3.yaml | 3 - substreams/ethereum-uniswap-v3/buf.gen.yaml | 12 + .../ethereum-uniswap-v3.yaml | 3 - .../ethereum-uniswap-v3/src/events/burn.rs | 3 +- .../ethereum-uniswap-v3/src/events/collect.rs | 3 +- .../src/events/collect_fee_protocol.rs | 6 +- .../ethereum-uniswap-v3/src/events/flash.rs | 3 +- .../src/events/initialize.rs | 3 +- .../ethereum-uniswap-v3/src/events/mint.rs | 6 +- .../ethereum-uniswap-v3/src/events/mod.rs | 6 +- .../src/events/set_fee_protocol.rs | 3 +- .../ethereum-uniswap-v3/src/events/swap.rs | 3 +- substreams/ethereum-uniswap-v3/src/lib.rs | 1 - .../src/modules/1_map_pool_created.rs | 11 +- .../src/modules/2_store_pools.rs | 4 +- .../src/modules/5_map_pool_events.rs | 9 +- substreams/ethereum-uniswap-v3/src/pb/mod.rs | 9 - .../src/pb/tycho.evm.v1.rs | 307 ---------- .../src/pb/uniswap.types.v1.rs | 541 ------------------ .../src/storage/pool_storage.rs | 6 +- substreams/ethereum-uniswap-v3/src/traits.rs | 30 - 23 files changed, 46 insertions(+), 928 deletions(-) create mode 100644 substreams/ethereum-uniswap-v3/buf.gen.yaml delete mode 100644 substreams/ethereum-uniswap-v3/src/pb/tycho.evm.v1.rs delete mode 100644 substreams/ethereum-uniswap-v3/src/pb/uniswap.types.v1.rs delete mode 100644 substreams/ethereum-uniswap-v3/src/traits.rs diff --git a/substreams/Cargo.lock b/substreams/Cargo.lock index 84e0b41..fc205c4 100644 --- a/substreams/Cargo.lock +++ b/substreams/Cargo.lock @@ -1070,6 +1070,7 @@ dependencies = [ "substreams-ethereum", "substreams-helper", "tiny-keccak", + "tycho-substreams", ] [[package]] diff --git a/substreams/ethereum-uniswap-v3/Cargo.toml b/substreams/ethereum-uniswap-v3/Cargo.toml index e4c9359..101914d 100644 --- a/substreams/ethereum-uniswap-v3/Cargo.toml +++ b/substreams/ethereum-uniswap-v3/Cargo.toml @@ -15,6 +15,7 @@ ethabi.workspace = true anyhow = { workspace = true, features = [] } hex-literal.workspace = true substreams-helper.workspace = true +tycho-substreams.workspace = true num-bigint = "0.4.4" hex.workspace = true tiny-keccak = "2.0" diff --git a/substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml b/substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml index 1d3f0f7..4937787 100644 --- a/substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml +++ b/substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml @@ -5,12 +5,9 @@ package: protobuf: files: - - tycho/evm/v1/common.proto - - tycho/evm/v1/entity.proto - uniswap.proto importPaths: - ./proto/v1 - - ../../proto/ binaries: default: diff --git a/substreams/ethereum-uniswap-v3/buf.gen.yaml b/substreams/ethereum-uniswap-v3/buf.gen.yaml new file mode 100644 index 0000000..d2e6544 --- /dev/null +++ b/substreams/ethereum-uniswap-v3/buf.gen.yaml @@ -0,0 +1,12 @@ + +version: v1 +plugins: +- plugin: buf.build/community/neoeinstein-prost:v0.2.2 + out: src/pb + opt: + - file_descriptor_set=false + +- plugin: buf.build/community/neoeinstein-prost-crate:v0.3.1 + out: src/pb + opt: + - no_features diff --git a/substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml b/substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml index aeda329..e26388c 100644 --- a/substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml +++ b/substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml @@ -5,12 +5,9 @@ package: protobuf: files: - - tycho/evm/v1/common.proto - - tycho/evm/v1/entity.proto - uniswap.proto importPaths: - ./proto/v1 - - ../../proto/ binaries: default: diff --git a/substreams/ethereum-uniswap-v3/src/events/burn.rs b/substreams/ethereum-uniswap-v3/src/events/burn.rs index 2152244..7ed16a2 100644 --- a/substreams/ethereum-uniswap-v3/src/events/burn.rs +++ b/substreams/ethereum-uniswap-v3/src/events/burn.rs @@ -3,11 +3,12 @@ use substreams_helper::storage_change::StorageChangesFilter; use crate::{ abi::pool::events::Burn, - pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + pb::uniswap::v3::Pool, storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, }; use super::{BalanceDelta, EventTrait}; +use tycho_substreams::prelude::Attribute; impl EventTrait for Burn { fn get_changed_attributes( diff --git a/substreams/ethereum-uniswap-v3/src/events/collect.rs b/substreams/ethereum-uniswap-v3/src/events/collect.rs index 8969d67..835505d 100644 --- a/substreams/ethereum-uniswap-v3/src/events/collect.rs +++ b/substreams/ethereum-uniswap-v3/src/events/collect.rs @@ -2,10 +2,11 @@ use substreams_ethereum::pb::eth::v2::StorageChange; use crate::{ abi::pool::events::Collect, - pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + pb::uniswap::v3::Pool, storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, }; use substreams_helper::storage_change::StorageChangesFilter; +use tycho_substreams::prelude::Attribute; use super::{BalanceDelta, EventTrait}; diff --git a/substreams/ethereum-uniswap-v3/src/events/collect_fee_protocol.rs b/substreams/ethereum-uniswap-v3/src/events/collect_fee_protocol.rs index 87fec7e..7467986 100644 --- a/substreams/ethereum-uniswap-v3/src/events/collect_fee_protocol.rs +++ b/substreams/ethereum-uniswap-v3/src/events/collect_fee_protocol.rs @@ -1,13 +1,13 @@ use substreams_ethereum::pb::eth::v2::StorageChange; use substreams_helper::storage_change::StorageChangesFilter; +use super::{BalanceDelta, EventTrait}; use crate::{ abi::pool::events::CollectProtocol, - pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + pb::uniswap::v3::Pool, storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, }; - -use super::{BalanceDelta, EventTrait}; +use tycho_substreams::prelude::Attribute; impl EventTrait for CollectProtocol { fn get_changed_attributes( diff --git a/substreams/ethereum-uniswap-v3/src/events/flash.rs b/substreams/ethereum-uniswap-v3/src/events/flash.rs index ccc08d4..dc3b6a4 100644 --- a/substreams/ethereum-uniswap-v3/src/events/flash.rs +++ b/substreams/ethereum-uniswap-v3/src/events/flash.rs @@ -2,10 +2,11 @@ use substreams_ethereum::pb::eth::v2::StorageChange; use crate::{ abi::pool::events::Flash, - pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + pb::uniswap::v3::Pool, storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, }; use substreams_helper::storage_change::StorageChangesFilter; +use tycho_substreams::prelude::Attribute; use super::{BalanceDelta, EventTrait}; diff --git a/substreams/ethereum-uniswap-v3/src/events/initialize.rs b/substreams/ethereum-uniswap-v3/src/events/initialize.rs index cc1b9ef..7b94bba 100644 --- a/substreams/ethereum-uniswap-v3/src/events/initialize.rs +++ b/substreams/ethereum-uniswap-v3/src/events/initialize.rs @@ -2,10 +2,11 @@ use substreams_ethereum::pb::eth::v2::StorageChange; use crate::{ abi::pool::events::Initialize, - pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + pb::uniswap::v3::Pool, storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, }; use substreams_helper::storage_change::StorageChangesFilter; +use tycho_substreams::prelude::Attribute; use super::{BalanceDelta, EventTrait}; diff --git a/substreams/ethereum-uniswap-v3/src/events/mint.rs b/substreams/ethereum-uniswap-v3/src/events/mint.rs index f5374da..082f66a 100644 --- a/substreams/ethereum-uniswap-v3/src/events/mint.rs +++ b/substreams/ethereum-uniswap-v3/src/events/mint.rs @@ -1,13 +1,13 @@ use substreams_ethereum::pb::eth::v2::StorageChange; +use super::{BalanceDelta, EventTrait}; use crate::{ abi::pool::events::Mint, - pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + pb::uniswap::v3::Pool, storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, }; use substreams_helper::storage_change::StorageChangesFilter; - -use super::{BalanceDelta, EventTrait}; +use tycho_substreams::prelude::Attribute; impl EventTrait for Mint { fn get_changed_attributes( diff --git a/substreams/ethereum-uniswap-v3/src/events/mod.rs b/substreams/ethereum-uniswap-v3/src/events/mod.rs index 9d1f14c..1b69a47 100644 --- a/substreams/ethereum-uniswap-v3/src/events/mod.rs +++ b/substreams/ethereum-uniswap-v3/src/events/mod.rs @@ -7,11 +7,9 @@ use crate::{ abi::pool::events::{ Burn, Collect, CollectProtocol, Flash, Initialize, Mint, SetFeeProtocol, Swap, }, - pb::{ - tycho::evm::v1::Attribute, - uniswap::v3::{BalanceDelta, Pool}, - }, + pb::uniswap::v3::{BalanceDelta, Pool}, }; +use tycho_substreams::prelude::Attribute; pub mod burn; pub mod collect; diff --git a/substreams/ethereum-uniswap-v3/src/events/set_fee_protocol.rs b/substreams/ethereum-uniswap-v3/src/events/set_fee_protocol.rs index 80e394d..89d106f 100644 --- a/substreams/ethereum-uniswap-v3/src/events/set_fee_protocol.rs +++ b/substreams/ethereum-uniswap-v3/src/events/set_fee_protocol.rs @@ -2,10 +2,11 @@ use substreams_ethereum::pb::eth::v2::StorageChange; use crate::{ abi::pool::events::SetFeeProtocol, - pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + pb::uniswap::v3::Pool, storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, }; use substreams_helper::storage_change::StorageChangesFilter; +use tycho_substreams::prelude::Attribute; use super::{BalanceDelta, EventTrait}; diff --git a/substreams/ethereum-uniswap-v3/src/events/swap.rs b/substreams/ethereum-uniswap-v3/src/events/swap.rs index d69bcb4..d45e914 100644 --- a/substreams/ethereum-uniswap-v3/src/events/swap.rs +++ b/substreams/ethereum-uniswap-v3/src/events/swap.rs @@ -5,9 +5,10 @@ use substreams_helper::storage_change::StorageChangesFilter; use crate::{ abi::pool::events::Swap, - pb::{tycho::evm::v1::Attribute, uniswap::v3::Pool}, + pb::uniswap::v3::Pool, storage::{constants::TRACKED_SLOTS, pool_storage::UniswapPoolStorage}, }; +use tycho_substreams::prelude::Attribute; use super::{BalanceDelta, EventTrait}; diff --git a/substreams/ethereum-uniswap-v3/src/lib.rs b/substreams/ethereum-uniswap-v3/src/lib.rs index 77428d7..c363134 100644 --- a/substreams/ethereum-uniswap-v3/src/lib.rs +++ b/substreams/ethereum-uniswap-v3/src/lib.rs @@ -8,4 +8,3 @@ mod storage; pub use modules::*; mod events; -mod traits; diff --git a/substreams/ethereum-uniswap-v3/src/modules/1_map_pool_created.rs b/substreams/ethereum-uniswap-v3/src/modules/1_map_pool_created.rs index 4688ee2..fbe4e60 100644 --- a/substreams/ethereum-uniswap-v3/src/modules/1_map_pool_created.rs +++ b/substreams/ethereum-uniswap-v3/src/modules/1_map_pool_created.rs @@ -6,13 +6,8 @@ use substreams_ethereum::pb::eth::v2::{self as eth}; use substreams_helper::{event_handler::EventHandler, hex::Hexable}; -use crate::{ - abi::factory::events::PoolCreated, - pb::tycho::evm::v1::{ - Attribute, BlockChanges, ChangeType, EntityChanges, FinancialType, ImplementationType, - ProtocolComponent, ProtocolType, Transaction, TransactionChanges, - }, -}; +use crate::abi::factory::events::PoolCreated; +use tycho_substreams::prelude::*; #[substreams::handlers::map] pub fn map_pools_created( @@ -24,7 +19,7 @@ pub fn map_pools_created( get_new_pools(&block, &mut new_pools, factory_address); - Ok(BlockChanges { block: Some(block.into()), changes: new_pools }) + Ok(BlockChanges { block: Some((&block).into()), changes: new_pools }) } // Extract new pools from PoolCreated events diff --git a/substreams/ethereum-uniswap-v3/src/modules/2_store_pools.rs b/substreams/ethereum-uniswap-v3/src/modules/2_store_pools.rs index e70f64e..f4bafff 100644 --- a/substreams/ethereum-uniswap-v3/src/modules/2_store_pools.rs +++ b/substreams/ethereum-uniswap-v3/src/modules/2_store_pools.rs @@ -2,7 +2,9 @@ use std::str; use substreams::store::{StoreNew, StoreSetIfNotExists, StoreSetIfNotExistsProto}; -use crate::pb::{tycho::evm::v1::BlockChanges, uniswap::v3::Pool}; +use crate::pb::uniswap::v3::Pool; + +use tycho_substreams::prelude::BlockChanges; #[substreams::handlers::store] pub fn store_pools(pools_created: BlockChanges, store: StoreSetIfNotExistsProto) { diff --git a/substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs b/substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs index c31b3b2..008201e 100644 --- a/substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs +++ b/substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs @@ -6,12 +6,11 @@ use substreams_helper::hex::Hexable; use crate::{ events::{get_log_changed_attributes, get_log_changed_balances}, - pb::{ - tycho::evm::v1::{BalanceChange, Block, BlockChanges, EntityChanges, TransactionChanges}, - uniswap::v3::Pool, - }, + pb::uniswap::v3::Pool, }; +use tycho_substreams::prelude::*; + #[substreams::handlers::map] pub fn map_pool_events( block: eth::Block, @@ -110,7 +109,7 @@ pub fn map_pool_events( // Make a list of all HashMap values: let tx_entity_changes: Vec = tx_changes_map.into_values().collect(); - let tycho_block: Block = block.into(); + let tycho_block: Block = (&block).into(); let block_entity_changes = BlockChanges { block: Some(tycho_block), changes: tx_entity_changes }; diff --git a/substreams/ethereum-uniswap-v3/src/pb/mod.rs b/substreams/ethereum-uniswap-v3/src/pb/mod.rs index 39167a4..83e1f97 100644 --- a/substreams/ethereum-uniswap-v3/src/pb/mod.rs +++ b/substreams/ethereum-uniswap-v3/src/pb/mod.rs @@ -1,13 +1,4 @@ // @generated -pub mod tycho { - pub mod evm { - // @@protoc_insertion_point(attribute:tycho.evm.v1) - pub mod v1 { - include!("tycho.evm.v1.rs"); - // @@protoc_insertion_point(tycho.evm.v1) - } - } -} pub mod uniswap { // @@protoc_insertion_point(attribute:uniswap.v3) pub mod v3 { diff --git a/substreams/ethereum-uniswap-v3/src/pb/tycho.evm.v1.rs b/substreams/ethereum-uniswap-v3/src/pb/tycho.evm.v1.rs deleted file mode 100644 index 6dac022..0000000 --- a/substreams/ethereum-uniswap-v3/src/pb/tycho.evm.v1.rs +++ /dev/null @@ -1,307 +0,0 @@ -// @generated -// This file contains the proto definitions for Substreams common to all integrations. - -/// A struct describing a block. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Block { - /// The blocks hash. - #[prost(bytes="vec", tag="1")] - pub hash: ::prost::alloc::vec::Vec, - /// The parent blocks hash. - #[prost(bytes="vec", tag="2")] - pub parent_hash: ::prost::alloc::vec::Vec, - /// The block number. - #[prost(uint64, tag="3")] - pub number: u64, - /// The block timestamp. - #[prost(uint64, tag="4")] - pub ts: u64, -} -/// A struct describing a transaction. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Transaction { - /// The transaction hash. - #[prost(bytes="vec", tag="1")] - pub hash: ::prost::alloc::vec::Vec, - /// The sender of the transaction. - #[prost(bytes="vec", tag="2")] - pub from: ::prost::alloc::vec::Vec, - /// The receiver of the transaction. - #[prost(bytes="vec", tag="3")] - pub to: ::prost::alloc::vec::Vec, - /// The transactions index within the block. - #[prost(uint64, tag="4")] - pub index: u64, -} -/// A custom struct representing an arbitrary attribute of a protocol component. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Attribute { - /// The name of the attribute. - #[prost(string, tag="1")] - pub name: ::prost::alloc::string::String, - /// The value of the attribute. - #[prost(bytes="vec", tag="2")] - pub value: ::prost::alloc::vec::Vec, - /// The type of change the attribute underwent. - #[prost(enumeration="ChangeType", tag="3")] - pub change: i32, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ProtocolType { - #[prost(string, tag="1")] - pub name: ::prost::alloc::string::String, - #[prost(enumeration="FinancialType", tag="2")] - pub financial_type: i32, - #[prost(message, repeated, tag="3")] - pub attribute_schema: ::prost::alloc::vec::Vec, - #[prost(enumeration="ImplementationType", tag="4")] - pub implementation_type: i32, -} -/// A struct describing a part of the protocol. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ProtocolComponent { - /// A unique identifier for the component within the protocol. - /// Can be a stringified address or a string describing the trading pair. - #[prost(string, tag="1")] - pub id: ::prost::alloc::string::String, - /// Addresses of the ERC20 tokens used by the component. - #[prost(bytes="vec", repeated, tag="2")] - pub tokens: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - /// Addresses of the contracts used by the component. - #[prost(bytes="vec", repeated, tag="3")] - pub contracts: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - /// Attributes of the component. - /// The inner ChangeType of the attribute has to match the ChangeType of the ProtocolComponent. - #[prost(message, repeated, tag="4")] - pub static_att: ::prost::alloc::vec::Vec, - /// Type of change the component underwent. - #[prost(enumeration="ChangeType", tag="5")] - pub change: i32, - /// / Represents the functionality of the component. - #[prost(message, optional, tag="6")] - pub protocol_type: ::core::option::Option, - /// Transaction where this component was created - #[prost(message, optional, tag="7")] - pub tx: ::core::option::Option, -} -/// A struct for following the changes of Total Value Locked (TVL) of a protocol component. -/// Note that if the ProtocolComponent contains multiple contracts, the TVL is tracked for the component as a whole. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BalanceChange { - /// The address of the ERC20 token whose balance changed. - #[prost(bytes="vec", tag="1")] - pub token: ::prost::alloc::vec::Vec, - /// The new balance of the token. - #[prost(bytes="vec", tag="2")] - pub balance: ::prost::alloc::vec::Vec, - /// The id of the component whose TVL is tracked. Note: This MUST be utf8 encoded. - #[prost(bytes="vec", tag="3")] - pub component_id: ::prost::alloc::vec::Vec, -} -// Native entities - -/// A component is a set of attributes that are associated with a custom entity. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EntityChanges { - /// A unique identifier of the entity within the protocol. - #[prost(string, tag="1")] - pub component_id: ::prost::alloc::string::String, - /// The set of attributes that are associated with the entity. - #[prost(message, repeated, tag="2")] - pub attributes: ::prost::alloc::vec::Vec, -} -// VM entities - -/// A key value entry into contract storage. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ContractSlot { - /// A contract's storage slot. - #[prost(bytes="vec", tag="2")] - pub slot: ::prost::alloc::vec::Vec, - /// The new value for this storage slot. - #[prost(bytes="vec", tag="3")] - pub value: ::prost::alloc::vec::Vec, -} -/// Changes made to a single contract's state. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ContractChange { - /// The contract's address - #[prost(bytes="vec", tag="1")] - pub address: ::prost::alloc::vec::Vec, - /// The new balance of the contract, empty bytes indicates no change. - #[prost(bytes="vec", tag="2")] - pub balance: ::prost::alloc::vec::Vec, - /// The new code of the contract, empty bytes indicates no change. - #[prost(bytes="vec", tag="3")] - pub code: ::prost::alloc::vec::Vec, - /// The changes to this contract's slots, empty sequence indicates no change. - #[prost(message, repeated, tag="4")] - pub slots: ::prost::alloc::vec::Vec, - /// Whether this is an update, a creation or a deletion. - #[prost(enumeration="ChangeType", tag="5")] - pub change: i32, -} -// Aggregate entities - -/// A set of changes aggregated by transaction. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct TransactionChanges { - /// The transaction instance that results in the changes. - #[prost(message, optional, tag="1")] - pub tx: ::core::option::Option, - /// Contains the changes induced by the above transaction, aggregated on a per-contract basis. - /// Contains the contract changes induced by the above transaction, usually for tracking VM components. - #[prost(message, repeated, tag="2")] - pub contract_changes: ::prost::alloc::vec::Vec, - /// Contains the entity changes induced by the above transaction. - /// Usually for tracking native components or used for VM extensions (plugins). - #[prost(message, repeated, tag="3")] - pub entity_changes: ::prost::alloc::vec::Vec, - /// An array of newly added components. - #[prost(message, repeated, tag="4")] - pub component_changes: ::prost::alloc::vec::Vec, - /// An array of balance changes to components. - #[prost(message, repeated, tag="5")] - pub balance_changes: ::prost::alloc::vec::Vec, -} -/// A set of transaction changes within a single block. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockChanges { - /// The block for which these changes are collectively computed. - #[prost(message, optional, tag="1")] - pub block: ::core::option::Option, - /// The set of transaction changes observed in the specified block. - #[prost(message, repeated, tag="2")] - pub changes: ::prost::alloc::vec::Vec, -} -/// Enum to specify the type of a change. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum ChangeType { - Unspecified = 0, - Update = 1, - Creation = 2, - Deletion = 3, -} -impl ChangeType { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - ChangeType::Unspecified => "CHANGE_TYPE_UNSPECIFIED", - ChangeType::Update => "CHANGE_TYPE_UPDATE", - ChangeType::Creation => "CHANGE_TYPE_CREATION", - ChangeType::Deletion => "CHANGE_TYPE_DELETION", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "CHANGE_TYPE_UNSPECIFIED" => Some(Self::Unspecified), - "CHANGE_TYPE_UPDATE" => Some(Self::Update), - "CHANGE_TYPE_CREATION" => Some(Self::Creation), - "CHANGE_TYPE_DELETION" => Some(Self::Deletion), - _ => None, - } - } -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum FinancialType { - Swap = 0, - Lend = 1, - Leverage = 2, - Psm = 3, -} -impl FinancialType { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - FinancialType::Swap => "SWAP", - FinancialType::Lend => "LEND", - FinancialType::Leverage => "LEVERAGE", - FinancialType::Psm => "PSM", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "SWAP" => Some(Self::Swap), - "LEND" => Some(Self::Lend), - "LEVERAGE" => Some(Self::Leverage), - "PSM" => Some(Self::Psm), - _ => None, - } - } -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum ImplementationType { - Vm = 0, - Custom = 1, -} -impl ImplementationType { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - ImplementationType::Vm => "VM", - ImplementationType::Custom => "CUSTOM", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "VM" => Some(Self::Vm), - "CUSTOM" => Some(Self::Custom), - _ => None, - } - } -} -// WARNING: DEPRECATED. Please use common.proto's TransactionChanges and BlockChanges instead. -// This file contains the definition for the native integration of Substreams. - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct TransactionEntityChanges { - #[prost(message, optional, tag="1")] - pub tx: ::core::option::Option, - #[prost(message, repeated, tag="2")] - pub entity_changes: ::prost::alloc::vec::Vec, - /// An array of newly added components. - #[prost(message, repeated, tag="3")] - pub component_changes: ::prost::alloc::vec::Vec, - /// An array of balance changes to components. - #[prost(message, repeated, tag="4")] - pub balance_changes: ::prost::alloc::vec::Vec, -} -/// A set of transaction changes within a single block. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockEntityChanges { - /// The block for which these changes are collectively computed. - #[prost(message, optional, tag="1")] - pub block: ::core::option::Option, - /// The set of transaction changes observed in the specified block. - #[prost(message, repeated, tag="2")] - pub changes: ::prost::alloc::vec::Vec, -} -// @@protoc_insertion_point(module) diff --git a/substreams/ethereum-uniswap-v3/src/pb/uniswap.types.v1.rs b/substreams/ethereum-uniswap-v3/src/pb/uniswap.types.v1.rs deleted file mode 100644 index 80dc421..0000000 --- a/substreams/ethereum-uniswap-v3/src/pb/uniswap.types.v1.rs +++ /dev/null @@ -1,541 +0,0 @@ -// @generated -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Erc20Tokens { - #[prost(message, repeated, tag="1")] - pub tokens: ::prost::alloc::vec::Vec, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Erc20Token { - #[prost(string, tag="1")] - pub address: ::prost::alloc::string::String, - #[prost(string, tag="2")] - pub name: ::prost::alloc::string::String, - #[prost(string, tag="3")] - pub symbol: ::prost::alloc::string::String, - #[prost(uint64, tag="4")] - pub decimals: u64, - #[prost(string, tag="5")] - pub total_supply: ::prost::alloc::string::String, - #[prost(string, repeated, tag="6")] - pub whitelist_pools: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Liquidity { - #[prost(string, tag="1")] - pub pool_address: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="2")] - pub value: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Pools { - #[prost(message, repeated, tag="1")] - pub pools: ::prost::alloc::vec::Vec, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Pool { - #[prost(string, tag="1")] - pub address: ::prost::alloc::string::String, - #[prost(uint64, tag="3")] - pub created_at_timestamp: u64, - #[prost(uint64, tag="4")] - pub created_at_block_number: u64, - #[prost(message, optional, tag="5")] - pub token0: ::core::option::Option, - #[prost(message, optional, tag="6")] - pub token1: ::core::option::Option, - /// Integer - #[prost(string, tag="7")] - pub fee_tier: ::prost::alloc::string::String, - /// internals - #[prost(int32, tag="30")] - pub tick_spacing: i32, - #[prost(uint64, tag="31")] - pub log_ordinal: u64, - #[prost(string, tag="32")] - pub transaction_id: ::prost::alloc::string::String, - #[prost(bool, tag="33")] - pub ignore_pool: bool, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Events { - #[prost(message, repeated, tag="1")] - pub pool_sqrt_prices: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="2")] - pub pool_liquidities: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="7")] - pub fee_growth_global_updates: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="10")] - pub fee_growth_inside_updates: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="11")] - pub fee_growth_outside_updates: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="3")] - pub pool_events: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="4")] - pub transactions: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="6")] - pub flashes: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="8")] - pub ticks_created: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="9")] - pub ticks_updated: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="20")] - pub created_positions: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="21")] - pub increase_liquidity_positions: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="22")] - pub decrease_liquidity_positions: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="23")] - pub collect_positions: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="24")] - pub transfer_positions: ::prost::alloc::vec::Vec, -} -/// Nested message and enum types in `Events`. -pub mod events { - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct FeeGrowthGlobal { - #[prost(string, tag="1")] - pub pool_address: ::prost::alloc::string::String, - #[prost(uint64, tag="2")] - pub ordinal: u64, - #[prost(int32, tag="3")] - pub token_idx: i32, - /// Integer - #[prost(string, tag="4")] - pub new_value: ::prost::alloc::string::String, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct FeeGrowthInside { - #[prost(string, tag="1")] - pub pool_address: ::prost::alloc::string::String, - #[prost(int32, tag="2")] - pub tick_idx: i32, - #[prost(uint64, tag="3")] - pub ordinal: u64, - /// Integer - #[prost(string, tag="4")] - pub new_value: ::prost::alloc::string::String, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct FeeGrowthOutside { - #[prost(string, tag="1")] - pub pool_address: ::prost::alloc::string::String, - #[prost(int32, tag="2")] - pub tick_lower: i32, - #[prost(int32, tag="3")] - pub tick_upper: i32, - #[prost(uint64, tag="4")] - pub ordinal: u64, - /// Integer - #[prost(string, tag="5")] - pub new_value: ::prost::alloc::string::String, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct TickCreated { - #[prost(string, tag="1")] - pub pool_address: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="2")] - pub idx: ::prost::alloc::string::String, - #[prost(uint64, tag="3")] - pub log_ordinal: u64, - #[prost(uint64, tag="4")] - pub created_at_timestamp: u64, - #[prost(uint64, tag="5")] - pub created_at_block_number: u64, - /// Decimal - #[prost(string, tag="6")] - pub price0: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="7")] - pub price1: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="8")] - pub amount: ::prost::alloc::string::String, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct TickUpdated { - #[prost(string, tag="1")] - pub pool_address: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="2")] - pub idx: ::prost::alloc::string::String, - #[prost(uint64, tag="3")] - pub log_ordinal: u64, - /// Integer - #[prost(string, tag="4")] - pub fee_growth_outside_0x_128: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="5")] - pub fee_growth_outside_1x_128: ::prost::alloc::string::String, - #[prost(uint64, tag="6")] - pub timestamp: u64, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct PoolSqrtPrice { - #[prost(string, tag="1")] - pub pool_address: ::prost::alloc::string::String, - #[prost(uint64, tag="2")] - pub ordinal: u64, - /// Integer - #[prost(string, tag="3")] - pub sqrt_price: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="4")] - pub tick: ::prost::alloc::string::String, - #[prost(bool, tag="5")] - pub initialized: bool, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct PoolEvent { - #[prost(uint64, tag="100")] - pub log_ordinal: u64, - #[prost(uint64, tag="101")] - pub log_index: u64, - #[prost(string, tag="102")] - pub pool_address: ::prost::alloc::string::String, - #[prost(string, tag="103")] - pub token0: ::prost::alloc::string::String, - #[prost(string, tag="104")] - pub token1: ::prost::alloc::string::String, - #[prost(string, tag="105")] - pub fee: ::prost::alloc::string::String, - #[prost(string, tag="106")] - pub transaction_id: ::prost::alloc::string::String, - #[prost(uint64, tag="107")] - pub timestamp: u64, - #[prost(uint64, tag="108")] - pub created_at_block_number: u64, - #[prost(oneof="pool_event::Type", tags="1, 2, 3")] - pub r#type: ::core::option::Option, - } - /// Nested message and enum types in `PoolEvent`. - pub mod pool_event { - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct Swap { - #[prost(string, tag="1")] - pub sender: ::prost::alloc::string::String, - #[prost(string, tag="2")] - pub recipient: ::prost::alloc::string::String, - #[prost(string, tag="3")] - pub origin: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="4")] - pub amount_0: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="5")] - pub amount_1: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="6")] - pub sqrt_price: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="7")] - pub liquidity: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="8")] - pub tick: ::prost::alloc::string::String, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct Burn { - #[prost(string, tag="1")] - pub owner: ::prost::alloc::string::String, - #[prost(string, tag="2")] - pub origin: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="3")] - pub amount: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="4")] - pub amount_0: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="5")] - pub amount_1: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="6")] - pub tick_lower: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="7")] - pub tick_upper: ::prost::alloc::string::String, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct Mint { - #[prost(string, tag="1")] - pub owner: ::prost::alloc::string::String, - #[prost(string, tag="2")] - pub sender: ::prost::alloc::string::String, - #[prost(string, tag="3")] - pub origin: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="4")] - pub amount_0: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="5")] - pub amount_1: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="6")] - pub tick_lower: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="7")] - pub tick_upper: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="8")] - pub amount: ::prost::alloc::string::String, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Type { - #[prost(message, tag="1")] - Swap(Swap), - #[prost(message, tag="2")] - Burn(Burn), - #[prost(message, tag="3")] - Mint(Mint), - } - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct PoolLiquidity { - #[prost(string, tag="1")] - pub pool_address: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="2")] - pub liquidity: ::prost::alloc::string::String, - #[prost(string, tag="3")] - pub token0: ::prost::alloc::string::String, - #[prost(string, tag="4")] - pub token1: ::prost::alloc::string::String, - /// internals - #[prost(uint64, tag="30")] - pub log_ordinal: u64, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct Flash { - #[prost(string, tag="1")] - pub pool_address: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="2")] - pub fee_growth_global_0x_128: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="3")] - pub fee_growth_global_1x_128: ::prost::alloc::string::String, - #[prost(uint64, tag="4")] - pub log_ordinal: u64, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct Transaction { - #[prost(string, tag="1")] - pub id: ::prost::alloc::string::String, - #[prost(uint64, tag="2")] - pub block_number: u64, - #[prost(uint64, tag="3")] - pub timestamp: u64, - #[prost(uint64, tag="4")] - pub gas_used: u64, - /// Integer - #[prost(string, tag="5")] - pub gas_price: ::prost::alloc::string::String, - #[prost(uint64, tag="6")] - pub log_ordinal: u64, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct PositionEvent { - #[prost(oneof="position_event::Type", tags="1, 2, 3, 4, 5")] - pub r#type: ::core::option::Option, - } - /// Nested message and enum types in `PositionEvent`. - pub mod position_event { - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Type { - #[prost(message, tag="1")] - CreatedPosition(super::CreatedPosition), - #[prost(message, tag="2")] - IncreaseLiquidityPosition(super::IncreaseLiquidityPosition), - #[prost(message, tag="3")] - DecreaseLiquidityPosition(super::DecreaseLiquidityPosition), - #[prost(message, tag="4")] - CollectPosition(super::CollectPosition), - #[prost(message, tag="5")] - TransferPosition(super::TransferPosition), - } - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct CreatedPosition { - #[prost(string, tag="1")] - pub token_id: ::prost::alloc::string::String, - #[prost(string, tag="2")] - pub pool: ::prost::alloc::string::String, - #[prost(string, tag="3")] - pub token0: ::prost::alloc::string::String, - #[prost(string, tag="4")] - pub token1: ::prost::alloc::string::String, - #[prost(string, tag="5")] - pub tick_lower: ::prost::alloc::string::String, - #[prost(string, tag="6")] - pub tick_upper: ::prost::alloc::string::String, - #[prost(string, tag="7")] - pub transaction: ::prost::alloc::string::String, - #[prost(uint64, tag="8")] - pub log_ordinal: u64, - #[prost(uint64, tag="9")] - pub timestamp: u64, - #[prost(uint64, tag="10")] - pub block_number: u64, - /// BigInt - #[prost(string, optional, tag="11")] - pub fee_growth_inside0_last_x128: ::core::option::Option<::prost::alloc::string::String>, - /// BigInt - #[prost(string, optional, tag="12")] - pub fee_growth_inside1_last_x128: ::core::option::Option<::prost::alloc::string::String>, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct IncreaseLiquidityPosition { - #[prost(string, tag="1")] - pub token_id: ::prost::alloc::string::String, - /// BigInt - #[prost(string, tag="2")] - pub liquidity: ::prost::alloc::string::String, - /// BigDecimal - #[prost(string, tag="3")] - pub deposited_token0: ::prost::alloc::string::String, - /// BigDecimal - #[prost(string, tag="4")] - pub deposited_token1: ::prost::alloc::string::String, - /// BigInt - #[prost(string, optional, tag="5")] - pub fee_growth_inside0_last_x128: ::core::option::Option<::prost::alloc::string::String>, - /// BigInt - #[prost(string, optional, tag="6")] - pub fee_growth_inside1_last_x128: ::core::option::Option<::prost::alloc::string::String>, - #[prost(uint64, tag="10")] - pub log_ordinal: u64, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct DecreaseLiquidityPosition { - #[prost(string, tag="1")] - pub token_id: ::prost::alloc::string::String, - /// BigInt - #[prost(string, tag="2")] - pub liquidity: ::prost::alloc::string::String, - /// BigDecimal - #[prost(string, tag="3")] - pub withdrawn_token0: ::prost::alloc::string::String, - /// BigDecimal - #[prost(string, tag="4")] - pub withdrawn_token1: ::prost::alloc::string::String, - /// BigInt - #[prost(string, optional, tag="5")] - pub fee_growth_inside0_last_x128: ::core::option::Option<::prost::alloc::string::String>, - /// BigInt - #[prost(string, optional, tag="6")] - pub fee_growth_inside1_last_x128: ::core::option::Option<::prost::alloc::string::String>, - #[prost(uint64, tag="10")] - pub log_ordinal: u64, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct CollectPosition { - #[prost(string, tag="1")] - pub token_id: ::prost::alloc::string::String, - /// BigInt - #[prost(string, tag="2")] - pub collected_fees_token0: ::prost::alloc::string::String, - /// BigInt - #[prost(string, tag="3")] - pub collected_fees_token1: ::prost::alloc::string::String, - /// BigInt - #[prost(string, optional, tag="5")] - pub fee_growth_inside0_last_x128: ::core::option::Option<::prost::alloc::string::String>, - /// BigInt - #[prost(string, optional, tag="6")] - pub fee_growth_inside1_last_x128: ::core::option::Option<::prost::alloc::string::String>, - #[prost(uint64, tag="10")] - pub log_ordinal: u64, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct TransferPosition { - #[prost(string, tag="1")] - pub token_id: ::prost::alloc::string::String, - #[prost(string, tag="2")] - pub owner: ::prost::alloc::string::String, - #[prost(uint64, tag="10")] - pub log_ordinal: u64, - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct SnapshotPositions { - #[prost(message, repeated, tag="1")] - pub snapshot_positions: ::prost::alloc::vec::Vec, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct SnapshotPosition { - #[prost(string, tag="1")] - pub pool: ::prost::alloc::string::String, - /// the token_id of the position - #[prost(string, tag="2")] - pub position: ::prost::alloc::string::String, - #[prost(uint64, tag="3")] - pub block_number: u64, - #[prost(string, tag="4")] - pub owner: ::prost::alloc::string::String, - #[prost(uint64, tag="6")] - pub timestamp: u64, - /// Decimal - #[prost(string, tag="7")] - pub liquidity: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="8")] - pub deposited_token0: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="9")] - pub deposited_token1: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="10")] - pub withdrawn_token0: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="11")] - pub withdrawn_token1: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="12")] - pub collected_fees_token0: ::prost::alloc::string::String, - /// Decimal - #[prost(string, tag="13")] - pub collected_fees_token1: ::prost::alloc::string::String, - #[prost(string, tag="14")] - pub transaction: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="15")] - pub fee_growth_inside_0_last_x_128: ::prost::alloc::string::String, - /// Integer - #[prost(string, tag="16")] - pub fee_growth_inside_1_last_x_128: ::prost::alloc::string::String, - /// internal - #[prost(uint64, tag="17")] - pub log_ordinal: u64, -} -// @@protoc_insertion_point(module) diff --git a/substreams/ethereum-uniswap-v3/src/storage/pool_storage.rs b/substreams/ethereum-uniswap-v3/src/storage/pool_storage.rs index a0997f9..5569ef5 100644 --- a/substreams/ethereum-uniswap-v3/src/storage/pool_storage.rs +++ b/substreams/ethereum-uniswap-v3/src/storage/pool_storage.rs @@ -1,7 +1,5 @@ -use crate::{ - pb::tycho::evm::v1::{Attribute, ChangeType}, - storage::utils, -}; +use crate::storage::utils; +use tycho_substreams::prelude::{Attribute, ChangeType}; use substreams::scalar::BigInt; use substreams_ethereum::pb::eth::v2::StorageChange; diff --git a/substreams/ethereum-uniswap-v3/src/traits.rs b/substreams/ethereum-uniswap-v3/src/traits.rs deleted file mode 100644 index 4f573b8..0000000 --- a/substreams/ethereum-uniswap-v3/src/traits.rs +++ /dev/null @@ -1,30 +0,0 @@ -use substreams_ethereum::pb::eth::v2::{self as eth}; - -use crate::pb::tycho::evm::v1::{Block, Transaction}; - -impl From for Block { - fn from(block: eth::Block) -> Self { - Self { - hash: block.hash.clone(), - parent_hash: block - .header - .as_ref() - .expect("Block header not present") - .parent_hash - .clone(), - number: block.number, - ts: block.timestamp_seconds(), - } - } -} - -impl From<ð::TransactionTrace> for Transaction { - fn from(tx: ð::TransactionTrace) -> Self { - Self { - hash: tx.hash.clone(), - from: tx.from.clone(), - to: tx.to.clone(), - index: tx.index.into(), - } - } -} From aff76f8cc77e8767629b938df175e44027fd22e1 Mon Sep 17 00:00:00 2001 From: zizou <111426680+flopell@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:24:55 +0200 Subject: [PATCH 4/8] chore: fix CI --- substreams/crates/substreams-helper/src/common.rs | 1 + .../crates/substreams-helper/src/event_handler.rs | 2 +- substreams/ethereum-ambient/src/lib.rs | 13 ++++++++----- .../ethereum-uniswap-v2/proto/v1/uniswap.proto | 8 ++++---- .../src/modules/3_map_pool_events.rs | 3 ++- .../src/pb/tycho.evm.uniswap.v2.rs | 8 ++++---- .../src/modules/5_map_pool_events.rs | 2 +- substreams/rustfmt.toml | 2 ++ 8 files changed, 23 insertions(+), 16 deletions(-) diff --git a/substreams/crates/substreams-helper/src/common.rs b/substreams/crates/substreams-helper/src/common.rs index cdc12d8..e0a83ac 100644 --- a/substreams/crates/substreams-helper/src/common.rs +++ b/substreams/crates/substreams-helper/src/common.rs @@ -8,6 +8,7 @@ use substreams::store::{ use crate::hex::Hexable; /// HasAddresser is a trait that a few functionalities in this crate depend on. +/// /// Every time we need to filter something by address (events emmited by a set of addresses, /// storage changes occurring on certain contracts, etc) you'll likely need /// to provide a HasAddresser. diff --git a/substreams/crates/substreams-helper/src/event_handler.rs b/substreams/crates/substreams-helper/src/event_handler.rs index 8a91342..2b550c2 100644 --- a/substreams/crates/substreams-helper/src/event_handler.rs +++ b/substreams/crates/substreams-helper/src/event_handler.rs @@ -100,4 +100,4 @@ impl<'a> EventHandler<'a> { } } } -} \ No newline at end of file +} diff --git a/substreams/ethereum-ambient/src/lib.rs b/substreams/ethereum-ambient/src/lib.rs index e279ea5..5537a72 100644 --- a/substreams/ethereum-ambient/src/lib.rs +++ b/substreams/ethereum-ambient/src/lib.rs @@ -52,13 +52,15 @@ impl From for tycho::ContractChange { /// Extracts all contract changes relevant to vm simulations /// -/// This is the main logic of the substreams integration. It takes a raw ethereum block on input and extracts the BlockContractChanges stream. It includes tracking: +/// This is the main logic of the substreams integration. It takes a raw ethereum block on input and +/// extracts the BlockContractChanges stream. It includes tracking: /// - new pool initializations /// - all storage slot changes for the Ambient contract /// - all ERC20 balance changes for the Ambient pools /// - all code changes and balance updates of the Ambient contract -/// -/// Generally we detect all changes in transactions sequentially and detect if it is a CREATE or UPDATE change based on already present data. +/// +/// Generally we detect all changes in transactions sequentially and detect if it is a CREATE or +/// UPDATE change based on already present data. #[substreams::handlers::map] fn map_changes( block: eth::v2::Block, @@ -83,7 +85,8 @@ fn map_changes( for block_tx in block.transactions() { // Extract storage changes for all contracts relevant to this protocol system. - // Ambient is a protocol system consisting of many ProtocolComponents (one for each pool), but they all share the same AMBIENT_CONTRACT contract. + // Ambient is a protocol system consisting of many ProtocolComponents (one for each pool), + // but they all share the same AMBIENT_CONTRACT contract. let mut storage_changes = block_tx .calls .iter() @@ -105,7 +108,7 @@ fn map_changes( .collect::>(); // Detect all pool initializations - // Official documentation: https://docs.ambient.finance/developers/dex-contract-interface/pool-initialization + // Official documentation: https://docs.ambient.finance/developers/dex-contract-interface/pool-initialization for call in ambient_calls { if call.input.len() < 4 { continue; diff --git a/substreams/ethereum-uniswap-v2/proto/v1/uniswap.proto b/substreams/ethereum-uniswap-v2/proto/v1/uniswap.proto index 3f26fd1..962d9fe 100644 --- a/substreams/ethereum-uniswap-v2/proto/v1/uniswap.proto +++ b/substreams/ethereum-uniswap-v2/proto/v1/uniswap.proto @@ -21,10 +21,10 @@ message Events { message Event { oneof type { - DepositEvent deposit_type = 10; - WithdrawEvent withdraw_type = 20; - SyncEvent sync_type = 30; - SwapEvent swap_type = 40; + DepositEvent deposit = 10; + WithdrawEvent withdraw = 20; + SyncEvent sync = 30; + SwapEvent swap = 40; } string hash = 100; diff --git a/substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs b/substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs index 2e6b264..5c428fa 100644 --- a/substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs +++ b/substreams/ethereum-uniswap-v2/src/modules/3_map_pool_events.rs @@ -141,8 +141,9 @@ fn handle_sync( /// handle_sync /// - block_entity_changes: The BlockChanges struct that will be updated with the changes from the /// sync events. +/// /// This HashMap comes pre-filled with the changes for the create_pool events, mapped in -/// 1_map_pool_created. +/// 1_map_pool_created. /// /// This function is called after the handle_sync function, and it is expected that /// block_entity_changes will be complete after this function ends. diff --git a/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.uniswap.v2.rs b/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.uniswap.v2.rs index c814ee4..26362b2 100644 --- a/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.uniswap.v2.rs +++ b/substreams/ethereum-uniswap-v2/src/pb/tycho.evm.uniswap.v2.rs @@ -51,13 +51,13 @@ pub mod event { #[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Type { #[prost(message, tag="10")] - DepositType(super::DepositEvent), + Deposit(super::DepositEvent), #[prost(message, tag="20")] - WithdrawType(super::WithdrawEvent), + Withdraw(super::WithdrawEvent), #[prost(message, tag="30")] - SyncType(super::SyncEvent), + Sync(super::SyncEvent), #[prost(message, tag="40")] - SwapType(super::SwapEvent), + Swap(super::SwapEvent), } } #[allow(clippy::derive_partial_eq_without_eq)] diff --git a/substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs b/substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs index 008201e..32cee7f 100644 --- a/substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs +++ b/substreams/ethereum-uniswap-v3/src/modules/5_map_pool_events.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, usize, vec}; +use std::{collections::HashMap, vec}; use substreams::store::{StoreGet, StoreGetBigInt, StoreGetProto}; use substreams_ethereum::pb::eth::v2::{self as eth, TransactionTrace}; diff --git a/substreams/rustfmt.toml b/substreams/rustfmt.toml index c0b67b2..1f8b922 100644 --- a/substreams/rustfmt.toml +++ b/substreams/rustfmt.toml @@ -12,4 +12,6 @@ ignore = [ "crates/tycho-substreams/src/pb", "ethereum-balancer/src/abi", "ethereum-curve/src/abi", + "ethereum-uniswap-v2/src/abi", + "ethereum-uniswap-v3/src/abi", ] From 42f2f45aa7187d5dee03f93e11cd2d8bdd314ec0 Mon Sep 17 00:00:00 2001 From: zizou <111426680+flopell@users.noreply.github.com> Date: Mon, 14 Oct 2024 18:09:17 +0200 Subject: [PATCH 5/8] refactor(substreams): Update ambient Substreams --- proto/buf.lock => buf.lock | 2 +- buf.yaml | 19 + proto/buf.yaml | 7 - substreams/Cargo.lock | 6 +- substreams/crates/tycho-substreams/Readme.md | 11 +- .../crates/tycho-substreams/buf.gen.yaml | 10 +- .../crates/tycho-substreams/src/models.rs | 2 +- .../crates/tycho-substreams/src/pb/mod.rs | 7 + .../src/pb/tycho.ambient.v1.rs | 33 ++ .../tycho-substreams/src/pb/tycho.evm.v1.rs | 4 +- substreams/ethereum-ambient/Cargo.lock | 108 +---- substreams/ethereum-ambient/Cargo.toml | 20 +- substreams/ethereum-ambient/Readme.md | 35 ++ substreams/ethereum-ambient/buf.gen.yaml | 18 +- .../ethereum-ambient/proto/ambient.proto | 27 ++ .../src/contracts/hotproxy.rs | 83 ++++ .../src/contracts/knockout.rs | 106 +++++ .../ethereum-ambient/src/contracts/main.rs | 170 ++++++++ .../src/contracts/micropaths.rs | 307 ++++++++++++++ .../ethereum-ambient/src/contracts/mod.rs | 16 + .../src/contracts/warmpath.rs | 87 ++++ substreams/ethereum-ambient/src/lib.rs | 391 +----------------- .../src/modules/1_map_pool_changes.rs | 127 ++++++ .../src/modules/2_store_pool_balances.rs | 18 + .../src/modules/2_store_pools.rs | 9 + .../src/modules/3_map_changes.rs | 367 ++++++++++++++++ .../ethereum-ambient/src/modules/mod.rs | 16 + substreams/ethereum-ambient/src/pb/mod.rs | 10 - .../ethereum-ambient/src/pb/tycho.evm.v1.rs | 183 -------- substreams/ethereum-ambient/src/utils.rs | 55 +++ substreams/ethereum-ambient/substreams.yaml | 37 +- 31 files changed, 1567 insertions(+), 724 deletions(-) rename proto/buf.lock => buf.lock (73%) create mode 100644 buf.yaml delete mode 100644 proto/buf.yaml create mode 100644 substreams/crates/tycho-substreams/src/pb/tycho.ambient.v1.rs create mode 100644 substreams/ethereum-ambient/Readme.md create mode 100644 substreams/ethereum-ambient/proto/ambient.proto create mode 100644 substreams/ethereum-ambient/src/contracts/hotproxy.rs create mode 100644 substreams/ethereum-ambient/src/contracts/knockout.rs create mode 100644 substreams/ethereum-ambient/src/contracts/main.rs create mode 100644 substreams/ethereum-ambient/src/contracts/micropaths.rs create mode 100644 substreams/ethereum-ambient/src/contracts/mod.rs create mode 100644 substreams/ethereum-ambient/src/contracts/warmpath.rs create mode 100644 substreams/ethereum-ambient/src/modules/1_map_pool_changes.rs create mode 100644 substreams/ethereum-ambient/src/modules/2_store_pool_balances.rs create mode 100644 substreams/ethereum-ambient/src/modules/2_store_pools.rs create mode 100644 substreams/ethereum-ambient/src/modules/3_map_changes.rs create mode 100644 substreams/ethereum-ambient/src/modules/mod.rs delete mode 100644 substreams/ethereum-ambient/src/pb/mod.rs delete mode 100644 substreams/ethereum-ambient/src/pb/tycho.evm.v1.rs create mode 100644 substreams/ethereum-ambient/src/utils.rs diff --git a/proto/buf.lock b/buf.lock similarity index 73% rename from proto/buf.lock rename to buf.lock index c91b581..4f98143 100644 --- a/proto/buf.lock +++ b/buf.lock @@ -1,2 +1,2 @@ # Generated by buf. DO NOT EDIT. -version: v1 +version: v2 diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..6ee1fb4 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,19 @@ +version: v2 +modules: + - path: proto + excludes: + - proto/sf + - path: substreams/ethereum-ambient/proto +lint: + use: + - BASIC + except: + - FIELD_NOT_REQUIRED + - PACKAGE_NO_IMPORT_CYCLE + disallow_comment_ignores: true +breaking: + use: + - FILE + except: + - EXTENSION_NO_DELETE + - FIELD_SAME_DEFAULT diff --git a/proto/buf.yaml b/proto/buf.yaml deleted file mode 100644 index d4ff52d..0000000 --- a/proto/buf.yaml +++ /dev/null @@ -1,7 +0,0 @@ -version: v1 -breaking: - use: - - FILE -lint: - use: - - BASIC diff --git a/substreams/Cargo.lock b/substreams/Cargo.lock index fc205c4..b7055cb 100644 --- a/substreams/Cargo.lock +++ b/substreams/Cargo.lock @@ -991,16 +991,20 @@ dependencies = [ [[package]] name = "substreams-ethereum-ambient" -version = "0.3.0" +version = "0.5.0" dependencies = [ "anyhow", "bytes", "ethabi 18.0.0", "hex", "hex-literal 0.4.1", + "num-bigint", "prost 0.11.9", + "quote", "substreams", "substreams-ethereum", + "tiny-keccak", + "tycho-substreams", ] [[package]] diff --git a/substreams/crates/tycho-substreams/Readme.md b/substreams/crates/tycho-substreams/Readme.md index 20f868b..44394fc 100644 --- a/substreams/crates/tycho-substreams/Readme.md +++ b/substreams/crates/tycho-substreams/Readme.md @@ -4,15 +4,12 @@ Some shared functionality that is used to create tycho substream packages. ## Protobuf Models -Protobuf models are manually synced from the `tycho-indexer` repository whenever they +Protobuf models are manually synced from the `tycho-indexer` repository whenever they changed. -To generate the rust structs run the following command from within the `./proto` +To generate the rust structs run the following command from within the root directory: ```bash -buf generate \ - --path tycho \ - --template ../substreams/crates/tycho-substreams/buf.gen.yaml \ - --output ../substreams/crates/tycho-substreams/ -``` \ No newline at end of file +buf generate --template substreams/crates/tycho-substreams/buf.gen.yaml --output substreams/crates/tycho-substreams/ +``` diff --git a/substreams/crates/tycho-substreams/buf.gen.yaml b/substreams/crates/tycho-substreams/buf.gen.yaml index 07f4f81..bc6db15 100644 --- a/substreams/crates/tycho-substreams/buf.gen.yaml +++ b/substreams/crates/tycho-substreams/buf.gen.yaml @@ -1,12 +1,10 @@ -version: v1 +version: v2 plugins: - - plugin: buf.build/community/neoeinstein-prost:v0.2.2 + - remote: buf.build/community/neoeinstein-prost:v0.2.2 out: src/pb opt: - file_descriptor_set=false - type_attribute=.tycho.evm.v1.Transaction=#[derive(Eq\, Hash)] - - - plugin: buf.build/community/neoeinstein-prost-crate:v0.3.1 + - remote: buf.build/community/neoeinstein-prost-crate:v0.3.1 out: src/pb - opt: - - no_features + opt: no_features diff --git a/substreams/crates/tycho-substreams/src/models.rs b/substreams/crates/tycho-substreams/src/models.rs index 333f172..359b2af 100644 --- a/substreams/crates/tycho-substreams/src/models.rs +++ b/substreams/crates/tycho-substreams/src/models.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use substreams_ethereum::pb::eth::v2::{self as sf, StorageChange}; // re-export the protobuf types here. -pub use crate::pb::tycho::evm::v1::*; +pub use crate::pb::tycho::{ambient::v1::*, evm::v1::*}; impl TransactionContractChanges { /// Creates a new empty `TransactionContractChanges` instance. diff --git a/substreams/crates/tycho-substreams/src/pb/mod.rs b/substreams/crates/tycho-substreams/src/pb/mod.rs index 43d8838..d6a992a 100644 --- a/substreams/crates/tycho-substreams/src/pb/mod.rs +++ b/substreams/crates/tycho-substreams/src/pb/mod.rs @@ -1,5 +1,12 @@ // @generated pub mod tycho { + pub mod ambient { + // @@protoc_insertion_point(attribute:tycho.ambient.v1) + pub mod v1 { + include!("tycho.ambient.v1.rs"); + // @@protoc_insertion_point(tycho.ambient.v1) + } + } pub mod evm { // @@protoc_insertion_point(attribute:tycho.evm.v1) pub mod v1 { diff --git a/substreams/crates/tycho-substreams/src/pb/tycho.ambient.v1.rs b/substreams/crates/tycho-substreams/src/pb/tycho.ambient.v1.rs new file mode 100644 index 0000000..5bfe909 --- /dev/null +++ b/substreams/crates/tycho-substreams/src/pb/tycho.ambient.v1.rs @@ -0,0 +1,33 @@ +// @generated +/// A change to a pool's balance. Ambient specific. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AmbientBalanceDelta { + /// The address of the ERC20 token whose balance changed. + #[prost(bytes="vec", tag="1")] + pub pool_hash: ::prost::alloc::vec::Vec, + /// The token type: it can be base or quote. + #[prost(string, tag="2")] + pub token_type: ::prost::alloc::string::String, + /// The delta of the token. + #[prost(bytes="vec", tag="3")] + pub token_delta: ::prost::alloc::vec::Vec, + /// Used to determine the order of the balance changes. Necessary for the balance store. + #[prost(uint64, tag="4")] + pub ordinal: u64, + /// Transaction where the balance changed. + #[prost(message, optional, tag="5")] + pub tx: ::core::option::Option, +} +/// Ambient pool changes within a single block +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockPoolChanges { + /// New protocol components added in this block + #[prost(message, repeated, tag="1")] + pub protocol_components: ::prost::alloc::vec::Vec, + /// Balance changes to pools in this block + #[prost(message, repeated, tag="2")] + pub balance_deltas: ::prost::alloc::vec::Vec, +} +// @@protoc_insertion_point(module) diff --git a/substreams/crates/tycho-substreams/src/pb/tycho.evm.v1.rs b/substreams/crates/tycho-substreams/src/pb/tycho.evm.v1.rs index 39cbcb3..7f8c737 100644 --- a/substreams/crates/tycho-substreams/src/pb/tycho.evm.v1.rs +++ b/substreams/crates/tycho-substreams/src/pb/tycho.evm.v1.rs @@ -84,7 +84,8 @@ pub struct ProtocolComponent { /// Usually it is a single contract, but some protocols use multiple contracts. #[prost(bytes="vec", repeated, tag="3")] pub contracts: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - /// Attributes of the component. Used mainly be the native integration. + /// Static attributes of the component. + /// These attributes MUST be immutable. If it can ever change, it should be given as an EntityChanges for this component id. /// The inner ChangeType of the attribute has to match the ChangeType of the ProtocolComponent. #[prost(message, repeated, tag="4")] pub static_att: ::prost::alloc::vec::Vec, @@ -186,6 +187,7 @@ pub struct TransactionChanges { pub balance_changes: ::prost::alloc::vec::Vec, } /// A set of transaction changes within a single block. +/// This message must be the output of your substreams module. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct BlockChanges { diff --git a/substreams/ethereum-ambient/Cargo.lock b/substreams/ethereum-ambient/Cargo.lock index 03629cf..6d24354 100644 --- a/substreams/ethereum-ambient/Cargo.lock +++ b/substreams/ethereum-ambient/Cargo.lock @@ -180,24 +180,7 @@ version = "17.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4966fba78396ff92db3b817ee71143eccd98acf0f876b8d600e585a670c5d1b" dependencies = [ - "ethereum-types 0.13.1", - "hex", - "once_cell", - "regex", - "serde", - "serde_json", - "sha3", - "thiserror", - "uint", -] - -[[package]] -name = "ethabi" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" -dependencies = [ - "ethereum-types 0.14.1", + "ethereum-types", "hex", "once_cell", "regex", @@ -215,22 +198,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11da94e443c60508eb62cf256243a64da87304c2802ac2528847f79d750007ef" dependencies = [ "crunchy", - "fixed-hash 0.7.0", + "fixed-hash", "impl-rlp", - "impl-serde 0.3.2", - "tiny-keccak", -] - -[[package]] -name = "ethbloom" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" -dependencies = [ - "crunchy", - "fixed-hash 0.8.0", - "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "tiny-keccak", ] @@ -240,25 +210,11 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2827b94c556145446fcce834ca86b7abf0c39a805883fe20e72c5bfdb5a0dc6" dependencies = [ - "ethbloom 0.12.1", - "fixed-hash 0.7.0", + "ethbloom", + "fixed-hash", "impl-rlp", - "impl-serde 0.3.2", - "primitive-types 0.11.1", - "uint", -] - -[[package]] -name = "ethereum-types" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" -dependencies = [ - "ethbloom 0.13.0", - "fixed-hash 0.8.0", - "impl-rlp", - "impl-serde 0.4.0", - "primitive-types 0.12.2", + "impl-serde", + "primitive-types", "uint", ] @@ -280,18 +236,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" -dependencies = [ - "byteorder", - "rand", - "rustc-hex", - "static_assertions", -] - [[package]] name = "fixedbitset" version = "0.4.2" @@ -391,15 +335,6 @@ dependencies = [ "serde", ] -[[package]] -name = "impl-serde" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" -dependencies = [ - "serde", -] - [[package]] name = "impl-trait-for-tuples" version = "0.2.2" @@ -584,23 +519,10 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" dependencies = [ - "fixed-hash 0.7.0", + "fixed-hash", "impl-codec", "impl-rlp", - "impl-serde 0.3.2", - "uint", -] - -[[package]] -name = "primitive-types" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" -dependencies = [ - "fixed-hash 0.8.0", - "impl-codec", - "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "uint", ] @@ -883,7 +805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a176f39a6e09553c17a287edacd1854e5686fd20ffea3c9655dfc44d94b35e" dependencies = [ "anyhow", - "ethabi 17.2.0", + "ethabi", "heck", "hex", "prettyplease", @@ -895,12 +817,8 @@ dependencies = [ [[package]] name = "substreams-ethereum-ambient" -version = "0.3.0" +version = "0.4.0" dependencies = [ - "anyhow", - "bytes", - "ethabi 18.0.0", - "hex", "hex-literal 0.4.1", "prost", "substreams", @@ -914,7 +832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db4700cfe408b75634a3c6b3a0caf7bddba4879601d2085c811485ea54cbde2d" dependencies = [ "bigdecimal", - "ethabi 17.2.0", + "ethabi", "getrandom", "num-bigint", "prost", @@ -929,7 +847,7 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40d6d278d926fe3f0775d996ee2b5e1dc822c1b4bf4f7bf07c7fbb5bce6c79a9" dependencies = [ - "ethabi 17.2.0", + "ethabi", "heck", "hex", "num-bigint", diff --git a/substreams/ethereum-ambient/Cargo.toml b/substreams/ethereum-ambient/Cargo.toml index f7d246b..45da3f0 100644 --- a/substreams/ethereum-ambient/Cargo.toml +++ b/substreams/ethereum-ambient/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substreams-ethereum-ambient" -version = "0.3.0" +version = "0.5.0" edition = "2021" [lib] @@ -8,11 +8,15 @@ name = "substreams_ethereum_ambient" crate-type = ["cdylib"] [dependencies] -substreams = "0.5" -substreams-ethereum = "0.9" -prost = "0.11" -hex-literal = "0.4.1" -ethabi = "18.0.0" -hex = "0.4.2" +tycho-substreams.workspace = true +substreams.workspace = true +substreams-ethereum.workspace = true +prost.workspace = true +hex-literal.workspace = true +ethabi.workspace = true +hex.workspace = true bytes = "1.5.0" -anyhow = "1.0.75" +anyhow.workspace = true +tiny-keccak = "2.0.2" +num-bigint = { version = "0.4.4", features = [] } +quote = "1.0.33" diff --git a/substreams/ethereum-ambient/Readme.md b/substreams/ethereum-ambient/Readme.md new file mode 100644 index 0000000..af70922 --- /dev/null +++ b/substreams/ethereum-ambient/Readme.md @@ -0,0 +1,35 @@ +Substreams Ethereum Ambient Module +================================== + +Modules Description +------------------- + +### `map_pool_changes` + +* **Type**: Map +* **Purpose**: This module detects new pools within the Ethereum blockchain and balance changes. +* **Inputs**: Ethereum block data (`sf.ethereum.type.v2.Block`). +* **Output**: Emits data of type `proto:tycho.evm.state.v1.BlockPoolChanges`. + +### `store_pools_balances` + +* **Type**: Store +* **Purpose**: Accumulates and stores the balances of pools detected by `map_pool_changes`. It uses an additive update policy, implying that new values are added to existing balances. +* **Inputs**: Data mapped by `map_pool_changes`. + +### `store_pools` + +* **Type**: Store +* **Purpose**: Maintains a store of pool information using the `ProtocolComponent` data structure. This store is updated whenever `map_pool_changes` emits new pool data. +* **Inputs**: Data mapped by `map_pool_changes`. + +### `map_changes` + +* **Type**: Map +* **Purpose**: This module integrates all the processed information to generate comprehensive `BlockContractChanges`. It considers new pools, balance changes and contract changes. +* **Inputs**: + * Ethereum block data (`sf.ethereum.type.v2.Block`). + * Data from `map_pool_changes`. + * Data from `store_pools_balances`. + * Data from `store_pools`. +* **Output**: Emits `proto:tycho.evm.state.v1.BlockContractChanges`. diff --git a/substreams/ethereum-ambient/buf.gen.yaml b/substreams/ethereum-ambient/buf.gen.yaml index d2e6544..b028921 100644 --- a/substreams/ethereum-ambient/buf.gen.yaml +++ b/substreams/ethereum-ambient/buf.gen.yaml @@ -1,12 +1,8 @@ - -version: v1 +version: v2 plugins: -- plugin: buf.build/community/neoeinstein-prost:v0.2.2 - out: src/pb - opt: - - file_descriptor_set=false - -- plugin: buf.build/community/neoeinstein-prost-crate:v0.3.1 - out: src/pb - opt: - - no_features + - remote: buf.build/community/neoeinstein-prost:v0.2.2 + out: src/pb + opt: file_descriptor_set=false + - remote: buf.build/community/neoeinstein-prost-crate:v0.3.1 + out: src/pb + opt: no_features diff --git a/substreams/ethereum-ambient/proto/ambient.proto b/substreams/ethereum-ambient/proto/ambient.proto new file mode 100644 index 0000000..22afbbf --- /dev/null +++ b/substreams/ethereum-ambient/proto/ambient.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package tycho.ambient.v1; + +import "tycho/evm/v1/common.proto"; + +// A change to a pool's balance. Ambient specific. +message AmbientBalanceDelta { + // The address of the ERC20 token whose balance changed. + bytes pool_hash = 1; + // The token type: it can be base or quote. + string token_type = 2; + // The delta of the token. + bytes token_delta = 3; + // Used to determine the order of the balance changes. Necessary for the balance store. + uint64 ordinal = 4; + // Transaction where the balance changed. + tycho.evm.v1.Transaction tx = 5; +} + +// Ambient pool changes within a single block +message BlockPoolChanges { + // New protocol components added in this block + repeated tycho.evm.v1.ProtocolComponent protocol_components = 1; + // Balance changes to pools in this block + repeated AmbientBalanceDelta balance_deltas = 2; +} \ No newline at end of file diff --git a/substreams/ethereum-ambient/src/contracts/hotproxy.rs b/substreams/ethereum-ambient/src/contracts/hotproxy.rs new file mode 100644 index 0000000..c084ea1 --- /dev/null +++ b/substreams/ethereum-ambient/src/contracts/hotproxy.rs @@ -0,0 +1,83 @@ +use anyhow::{anyhow, bail}; + +use crate::utils::{decode_flows_from_output, encode_pool_hash}; +use ethabi::{decode, ParamType}; +use hex_literal::hex; +use substreams_ethereum::pb::eth::v2::Call; + +pub const AMBIENT_HOTPROXY_CONTRACT: [u8; 20] = hex!("37e00522Ce66507239d59b541940F99eA19fF81F"); +pub const USER_CMD_HOTPROXY_FN_SIG: [u8; 4] = hex!("f96dc788"); + +pub const SWAP_ABI_HOTPROXY_INPUT: &[ParamType] = &[ + ParamType::Address, // base + ParamType::Address, // quote + ParamType::Uint(256), // pool index + // isBuy - if true the direction of the swap is for the user to send base + // tokens and receive back quote tokens. + ParamType::Bool, + ParamType::Bool, // inBaseQty + ParamType::Uint(128), //qty + ParamType::Uint(16), // poolTip + ParamType::Uint(128), // limitPrice + ParamType::Uint(128), // minOut + ParamType::Uint(8), // reserveFlags +]; +const USER_CMD_EXTERNAL_ABI: &[ParamType] = &[ + ParamType::Bytes, // userCmd +]; + +pub fn decode_direct_swap_hotproxy_call( + call: &Call, +) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> { + if let Ok(external_cmd) = decode(USER_CMD_EXTERNAL_ABI, &call.input[4..]) { + let input_bytes = external_cmd[0] + .to_owned() + .into_bytes() // Convert Bytes32 to Vec + .ok_or_else(|| anyhow!("Failed to hotproxy userCmd input data.".to_string()))?; + + if let Ok(input_params) = decode(SWAP_ABI_HOTPROXY_INPUT, &input_bytes) { + let base_token = input_params[0] + .to_owned() + .into_address() + .ok_or_else(|| { + anyhow!( + "Failed to convert base token to address for direct swap hotproxy call: {:?}", + &input_params[0] + ) + })? + .to_fixed_bytes() + .to_vec(); + + let quote_token = input_params[1] + .to_owned() + .into_address() + .ok_or_else(|| { + anyhow!( + "Failed to convert quote token to address for direct swap hotproxy call: {:?}", + &input_params[1] + ) + })? + .to_fixed_bytes() + .to_vec(); + + let mut pool_index_buf = [0u8; 32]; + input_params[2] + .to_owned() + .into_uint() + .ok_or_else(|| { + anyhow!("Failed to convert pool index to u32 for direct swap hotproxy call" + .to_string()) + })? + .to_big_endian(&mut pool_index_buf); + let pool_index = pool_index_buf.to_vec(); + + let (base_flow, quote_flow) = decode_flows_from_output(call)?; + let pool_hash = encode_pool_hash(base_token, quote_token, pool_index); + Ok((pool_hash, base_flow, quote_flow)) + } else { + bail!("Failed to decode hotproxy swap call internap inputs.".to_string()); + } + } else { + bail!("Failed to decode hotproxy swap call external input.".to_string()); + } +} diff --git a/substreams/ethereum-ambient/src/contracts/knockout.rs b/substreams/ethereum-ambient/src/contracts/knockout.rs new file mode 100644 index 0000000..7a5234d --- /dev/null +++ b/substreams/ethereum-ambient/src/contracts/knockout.rs @@ -0,0 +1,106 @@ +use anyhow::{anyhow, bail}; + +use crate::utils::{decode_flows_from_output, encode_pool_hash}; +use ethabi::{decode, ParamType}; +use hex_literal::hex; +use substreams_ethereum::pb::eth::v2::Call; + +pub const AMBIENT_KNOCKOUT_CONTRACT: [u8; 20] = hex!("7F5D75AdE75646919c923C98D53E9Cc7Be7ea794"); +pub const USER_CMD_KNOCKOUT_FN_SIG: [u8; 4] = hex!("f96dc788"); + +// Represents the ABI of any cmd which is not mint or burn +const KNOCKOUT_INTERNAL_OTHER_CMD_ABI: &[ParamType] = &[ + ParamType::Uint(8), + ParamType::Address, // base + ParamType::Address, // quote + ParamType::Uint(256), // poolIdx + ParamType::Int(24), + ParamType::Int(24), + ParamType::Bool, + ParamType::Uint(8), + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(32), +]; +const KNOCKOUT_INTERNAL_MINT_BURN_ABI: &[ParamType] = &[ + ParamType::Uint(8), + ParamType::Address, // base + ParamType::Address, // quote + ParamType::Uint(256), // poolIdx + ParamType::Int(24), + ParamType::Int(24), + ParamType::Bool, + ParamType::Uint(8), + ParamType::Uint(256), + ParamType::Uint(256), + ParamType::Uint(128), + ParamType::Bool, +]; + +const KNOCKOUT_EXTERNAL_ABI: &[ParamType] = &[ + ParamType::Bytes, // userCmd +]; + +pub fn decode_knockout_call( + call: &Call, +) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> { + if let Ok(external_cmd) = decode(KNOCKOUT_EXTERNAL_ABI, &call.input[4..]) { + let input_data = external_cmd[0] + .to_owned() + .into_bytes() // Convert Bytes32 to Vec + .ok_or_else(|| anyhow!("Failed to Knockout userCmd input data.".to_string()))?; + + let code = input_data[31]; + let is_mint = code == 91; + let is_burn = code == 92; + + let abi = if is_mint || is_burn { + KNOCKOUT_INTERNAL_MINT_BURN_ABI + } else { + KNOCKOUT_INTERNAL_OTHER_CMD_ABI + }; + + if let Ok(mint_burn_inputs) = decode(abi, &input_data) { + let base_token = mint_burn_inputs[1] + .to_owned() + .into_address() + .ok_or_else(|| { + anyhow!( + "Failed to convert base token to address for knockout call: {:?}", + &mint_burn_inputs[1] + ) + })? + .to_fixed_bytes() + .to_vec(); + let quote_token = mint_burn_inputs[2] + .to_owned() + .into_address() + .ok_or_else(|| { + anyhow!( + "Failed to convert quote token to address for knockout call: {:?}", + &mint_burn_inputs[2] + ) + })? + .to_fixed_bytes() + .to_vec(); + + let mut pool_index_buf = [0u8; 32]; + mint_burn_inputs[3] + .to_owned() + .into_uint() + .ok_or_else(|| { + anyhow!("Failed to convert pool index to bytes for knockout call".to_string()) + })? + .to_big_endian(&mut pool_index_buf); + let pool_index = pool_index_buf.to_vec(); + + let (base_flow, quote_flow) = decode_flows_from_output(call)?; + let pool_hash = encode_pool_hash(base_token, quote_token, pool_index); + Ok((pool_hash, base_flow, quote_flow)) + } else { + bail!("Failed to decode knockout call outputs.".to_string()); + } + } else { + bail!("Failed to decode inputs for knockout call.".to_string()); + } +} diff --git a/substreams/ethereum-ambient/src/contracts/main.rs b/substreams/ethereum-ambient/src/contracts/main.rs new file mode 100644 index 0000000..2b273b5 --- /dev/null +++ b/substreams/ethereum-ambient/src/contracts/main.rs @@ -0,0 +1,170 @@ +use anyhow::{anyhow, bail}; +use tycho_substreams::models::{ + Attribute, ChangeType, FinancialType, ImplementationType, ProtocolComponent, ProtocolType, + Transaction, +}; + +use crate::utils::{decode_flows_from_output, encode_pool_hash}; +use ethabi::{decode, ParamType}; +use hex_literal::hex; +use substreams_ethereum::pb::eth::v2::Call; + +pub const AMBIENT_CONTRACT: [u8; 20] = hex!("aaaaaaaaa24eeeb8d57d431224f73832bc34f688"); +pub const USER_CMD_FN_SIG: [u8; 4] = hex!("a15112f9"); + +const USER_CMD_EXTERNAL_ABI: &[ParamType] = &[ + // index of the proxy sidecar the command is being called on + ParamType::Uint(16), + // call data for internal UserCmd method + ParamType::Bytes, +]; +const USER_CMD_INTERNAL_ABI: &[ParamType] = &[ + ParamType::Uint(8), // command + ParamType::Address, // base + ParamType::Address, // quote + ParamType::Uint(256), // pool index + ParamType::Uint(128), // price +]; + +pub const INIT_POOL_CODE: u8 = 71; + +pub const SWAP_ABI_INPUT: &[ParamType] = &[ + ParamType::Address, // base + ParamType::Address, // quote + ParamType::Uint(256), // pool index + // isBuy - if true the direction of the swap is for the user to send base + // tokens and receive back quote tokens. + ParamType::Bool, + ParamType::Bool, // inBaseQty + ParamType::Uint(128), //qty + ParamType::Uint(16), // poolTip + ParamType::Uint(128), // limitPrice + ParamType::Uint(128), // minOut + ParamType::Uint(8), // reserveFlags +]; + +// MicroPaths fn sigs +pub const SWAP_FN_SIG: [u8; 4] = hex!("3d719cd9"); + +pub fn decode_direct_swap_call( + call: &Call, +) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> { + if let Ok(external_input_params) = decode(SWAP_ABI_INPUT, &call.input[4..]) { + let base_token = external_input_params[0] + .to_owned() + .into_address() + .ok_or_else(|| { + anyhow!( + "Failed to convert base token to address for direct swap call: {:?}", + &external_input_params[0] + ) + })? + .to_fixed_bytes() + .to_vec(); + + let quote_token = external_input_params[1] + .to_owned() + .into_address() + .ok_or_else(|| { + anyhow!( + "Failed to convert quote token to address for direct swap call: {:?}", + &external_input_params[1] + ) + })? + .to_fixed_bytes() + .to_vec(); + + let mut pool_index_buf = [0u8; 32]; + external_input_params[2] + .to_owned() + .into_uint() + .ok_or_else(|| { + anyhow!("Failed to convert pool index to u32 for direct swap call".to_string()) + })? + .to_big_endian(&mut pool_index_buf); + let pool_index = pool_index_buf.to_vec(); + + let (base_flow, quote_flow) = decode_flows_from_output(call)?; + let pool_hash = encode_pool_hash(base_token, quote_token, pool_index); + Ok((pool_hash, base_flow, quote_flow)) + } else { + bail!("Failed to decode swap call inputs.".to_string()); + } +} +pub fn decode_pool_init( + call: &Call, + tx: Transaction, +) -> Result, anyhow::Error> { + // Decode external call to UserCmd + if let Ok(external_params) = decode(USER_CMD_EXTERNAL_ABI, &call.input[4..]) { + let cmd_bytes = external_params[1] + .to_owned() + .into_bytes() + .ok_or_else(|| anyhow!("Failed to convert to bytes: {:?}", &external_params[1]))?; + + // Call data is structured differently depending on the cmd code, so only + // decode if this is an init pool code. + if cmd_bytes[31] == INIT_POOL_CODE { + // Decode internal call to UserCmd + if let Ok(internal_params) = decode(USER_CMD_INTERNAL_ABI, &cmd_bytes) { + let base = internal_params[1] + .to_owned() + .into_address() + .ok_or_else(|| { + anyhow!("Failed to convert to address: {:?}", &internal_params[1]) + })? + .to_fixed_bytes() + .to_vec(); + + let quote = internal_params[2] + .to_owned() + .into_address() + .ok_or_else(|| { + anyhow!("Failed to convert to address: {:?}", &internal_params[2]) + })? + .to_fixed_bytes() + .to_vec(); + + let mut pool_index_buf = [0u8; 32]; + internal_params[3] + .to_owned() + .into_uint() + .ok_or_else(|| anyhow!("Failed to convert to u32".to_string()))? + .to_big_endian(&mut pool_index_buf); + let pool_index = pool_index_buf.to_vec(); + let pool_hash = encode_pool_hash(base.clone(), quote.clone(), pool_index.clone()); + + let static_attribute = Attribute { + name: String::from("pool_index"), + value: pool_index, + change: ChangeType::Creation.into(), + }; + + let mut tokens: Vec> = vec![base.clone(), quote.clone()]; + tokens.sort(); + + let new_component = ProtocolComponent { + id: hex::encode(pool_hash), + tokens, + contracts: vec![AMBIENT_CONTRACT.to_vec()], + static_att: vec![static_attribute], + change: ChangeType::Creation.into(), + protocol_type: Some(ProtocolType { + name: "ambient_pool".to_string(), + attribute_schema: vec![], + financial_type: FinancialType::Swap.into(), + implementation_type: ImplementationType::Vm.into(), + }), + tx: Some(tx.clone()), + }; + Ok(Some(new_component)) + } else { + bail!("Failed to decode ABI internal call.".to_string()); + } + } else { + Ok(None) + } + } else { + bail!("Failed to decode ABI external call.".to_string()); + } +} diff --git a/substreams/ethereum-ambient/src/contracts/micropaths.rs b/substreams/ethereum-ambient/src/contracts/micropaths.rs new file mode 100644 index 0000000..4835b94 --- /dev/null +++ b/substreams/ethereum-ambient/src/contracts/micropaths.rs @@ -0,0 +1,307 @@ +use anyhow::{anyhow, bail}; + +use ethabi::{decode, ParamType}; +use hex_literal::hex; +use substreams_ethereum::pb::eth::v2::Call; + +pub const AMBIENT_MICROPATHS_CONTRACT: [u8; 20] = hex!("f241bEf0Ea64020655C70963ef81Fea333752367"); + +pub const SWEEP_SWAP_FN_SIG: [u8; 4] = hex!("7b370fc2"); + +pub const MINT_AMBIENT_FN_SIG: [u8; 4] = hex!("2ee11587"); +pub const MINT_RANGE_FN_SIG: [u8; 4] = hex!("2370632b"); +pub const BURN_AMBIENT_FN_SIG: [u8; 4] = hex!("2a6f0864"); +pub const BURN_RANGE_FN_SIG: [u8; 4] = hex!("7c6dfe3d"); + +// ABI for the mintAmbient function with return values +pub const MINT_AMBIENT_RETURN_ABI: &[ParamType] = &[ + ParamType::Int(128), // int128 baseFlow + ParamType::Int(128), // int128 quoteFlow + ParamType::Uint(128), // uint128 seedOut +]; + +// ABI for the mintAmbient function parameters +const MINT_AMBIENT_ABI: &[ParamType] = &[ + ParamType::Uint(128), // uint128 price + ParamType::Uint(128), // uint128 seed + ParamType::Uint(128), // uint128 conc + ParamType::Uint(64), // uint64 seedGrowth + ParamType::Uint(64), // uint64 concGrowth + ParamType::Uint(128), // uint128 liq + ParamType::FixedBytes(32), // bytes32 poolHash +]; + +// ABI for the burnRange function +const BURN_RANGE_ABI: &[ParamType] = &[ + ParamType::Uint(128), // price + ParamType::Int(24), // priceTick + ParamType::Uint(128), // seed + ParamType::Uint(128), // conc + ParamType::Uint(64), // seedGrowth + ParamType::Uint(64), // concGrowth + ParamType::Int(24), // lowTick + ParamType::Int(24), // highTick + ParamType::Uint(128), // liq + ParamType::FixedBytes(32), // poolHash +]; + +const BURN_RANGE_RETURN_ABI: &[ParamType] = &[ + ParamType::Int(128), // baseFlow + ParamType::Int(128), // quoteFlow + ParamType::Uint(128), // seedOut + ParamType::Uint(128), // concOut +]; + +// ABI for the burnAmbient function with return values +const BURN_AMBIENT_RETURN_ABI: &[ParamType] = &[ + ParamType::Int(128), // int128 baseFlow + ParamType::Int(128), // int128 quoteFlow + ParamType::Uint(128), // uint128 seedOut +]; + +// ABI for the burnAmbient function +const BURN_AMBIENT_ABI: &[ParamType] = &[ + ParamType::Uint(128), // uint128 price + ParamType::Uint(128), // uint128 seed + ParamType::Uint(128), // uint128 conc + ParamType::Uint(64), // uint64 seedGrowth + ParamType::Uint(64), // uint64 concGrowth + ParamType::Uint(128), // uint128 liq + ParamType::FixedBytes(32), // bytes32 poolHash +]; + +// ABI for the mintRange function parameters +const MINT_RANGE_ABI: &[ParamType] = &[ + ParamType::Uint(128), // price + ParamType::Int(24), // priceTick + ParamType::Uint(128), // seed + ParamType::Uint(128), // conc + ParamType::Uint(64), // seedGrowth + ParamType::Uint(64), // concGrowth + ParamType::Int(24), // lowTick + ParamType::Int(24), // highTick + ParamType::Uint(128), // liq + ParamType::FixedBytes(32), // poolHash +]; + +// ABI for the mintRange function with return values +const MINT_RANGE_RETURN_ABI: &[ParamType] = &[ + ParamType::Int(128), // baseFlow + ParamType::Int(128), // quoteFlow + ParamType::Uint(128), // seedOut + ParamType::Uint(128), // concOut +]; +pub fn decode_mint_range_call( + call: &Call, +) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> { + if let Ok(mint_range) = decode(MINT_RANGE_ABI, &call.input[4..]) { + let pool_hash: [u8; 32] = mint_range[9] + .to_owned() + .into_fixed_bytes() + .ok_or_else(|| anyhow!("Failed to convert pool hash to fixed bytes".to_string()))? + .try_into() + .unwrap(); + + if let Ok(external_outputs) = decode(MINT_RANGE_RETURN_ABI, &call.return_data) { + let base_flow = external_outputs[0] + .to_owned() + .into_int() // Needs conversion into bytes for next step + .ok_or_else(|| anyhow!("Failed to convert base flow to i128".to_string()))?; + + let quote_flow = external_outputs[1] + .to_owned() + .into_int() // Needs conversion into bytes for next step + .ok_or_else(|| anyhow!("Failed to convert quote flow to i128".to_string()))?; + Ok((pool_hash, base_flow, quote_flow)) + } else { + bail!("Failed to decode swap call outputs.".to_string()); + } + } else { + bail!("Failed to decode inputs for WarmPath userCmd call.".to_string()); + } +} + +pub fn decode_burn_ambient_call( + call: &Call, +) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> { + if let Ok(burn_ambient) = decode(BURN_AMBIENT_ABI, &call.input[4..]) { + let pool_hash: [u8; 32] = burn_ambient[6] + .to_owned() + .into_fixed_bytes() + .ok_or_else(|| anyhow!("Failed to convert pool hash to bytes".to_string()))? + .try_into() + .unwrap(); + + if let Ok(external_outputs) = decode(BURN_AMBIENT_RETURN_ABI, &call.return_data) { + let base_flow = external_outputs[0] + .to_owned() + .into_int() + .ok_or_else(|| anyhow!("Failed to convert base flow to i128".to_string()))?; + + let quote_flow = external_outputs[1] + .to_owned() + .into_int() + .ok_or_else(|| anyhow!("Failed to convert quote flow to i128".to_string()))?; + + Ok((pool_hash, base_flow, quote_flow)) + } else { + bail!("Failed to decode burnAmbient call outputs.".to_string()); + } + } else { + bail!("Failed to decode inputs for burnAmbient call.".to_string()); + } +} + +pub fn decode_mint_ambient_call( + call: &Call, +) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> { + if let Ok(mint_ambient) = decode(MINT_AMBIENT_ABI, &call.input[4..]) { + let pool_hash: [u8; 32] = mint_ambient[6] + .to_owned() + .into_fixed_bytes() + .ok_or_else(|| anyhow!("Failed to convert pool hash to bytes".to_string()))? + .try_into() + .unwrap(); + + if let Ok(external_outputs) = decode(MINT_AMBIENT_RETURN_ABI, &call.return_data) { + let base_flow = external_outputs[0] + .to_owned() + .into_int() + .ok_or_else(|| anyhow!("Failed to convert base flow to i128".to_string()))?; + + let quote_flow = external_outputs[1] + .to_owned() + .into_int() + .ok_or_else(|| anyhow!("Failed to convert quote flow to i128".to_string()))?; + + Ok((pool_hash, base_flow, quote_flow)) + } else { + bail!("Failed to decode mintAmbient call outputs.".to_string()); + } + } else { + bail!("Failed to decode inputs for mintAmbient call.".to_string()); + } +} + +pub fn decode_burn_range_call( + call: &Call, +) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> { + if let Ok(burn_range) = decode(BURN_RANGE_ABI, &call.input[4..]) { + let pool_hash: [u8; 32] = burn_range[9] + .to_owned() + .into_fixed_bytes() // Convert Bytes32 to Vec + .ok_or_else(|| anyhow!("Failed to convert pool hash to bytes".to_string()))? + .try_into() + .unwrap(); + + if let Ok(external_outputs) = decode(BURN_RANGE_RETURN_ABI, &call.return_data) { + let base_flow = external_outputs[0] + .to_owned() + .into_int() + .ok_or_else(|| anyhow!("Failed to convert base flow to i128".to_string()))?; + + let quote_flow = external_outputs[1] + .to_owned() + .into_int() + .ok_or_else(|| anyhow!("Failed to convert quote flow to i128".to_string()))?; + + Ok((pool_hash, base_flow, quote_flow)) + } else { + bail!("Failed to decode burnRange call outputs.".to_string()); + } + } else { + bail!("Failed to decode inputs for burnRange call.".to_string()); + } +} + +pub fn decode_sweep_swap_call( + call: &Call, +) -> Result<([u8; 32], ethabi::Int, ethabi::Int), anyhow::Error> { + let sweep_swap_abi: &[ParamType] = &[ + ParamType::Tuple(vec![ + ParamType::Uint(128), + ParamType::Uint(128), + ParamType::Uint(128), + ParamType::Uint(64), + ParamType::Uint(64), + ]), // CurveState + ParamType::Int(24), // midTick + ParamType::Tuple(vec![ + ParamType::Bool, + ParamType::Bool, + ParamType::Uint(8), + ParamType::Uint(128), + ParamType::Uint(128), + ]), // SwapDirective + ParamType::Tuple(vec![ + ParamType::Tuple(vec![ + ParamType::Uint(8), // schema + ParamType::Uint(16), // feeRate + ParamType::Uint(8), // protocolTake + ParamType::Uint(16), // tickSize + ParamType::Uint(8), // jitThresh + ParamType::Uint(8), // knockoutBits + ParamType::Uint(8), // oracleFlags + ]), + ParamType::FixedBytes(32), // poolHash + ParamType::Address, + ]), // PoolCursor + ]; + let sweep_swap_abi_output: &[ParamType] = &[ + ParamType::Tuple(vec![ + ParamType::Int(128), // baseFlow + ParamType::Int(128), // quoteFlow + ParamType::Uint(128), + ParamType::Uint(128), + ]), // Chaining.PairFlow memory accum + ParamType::Uint(128), // priceOut + ParamType::Uint(128), // seedOut + ParamType::Uint(128), // concOut + ParamType::Uint(64), // ambientOut + ParamType::Uint(64), // concGrowthOut + ]; + if let Ok(sweep_swap_input) = decode(sweep_swap_abi, &call.input[4..]) { + let pool_cursor = sweep_swap_input[3] + .to_owned() + .into_tuple() + .ok_or_else(|| { + anyhow!("Failed to convert pool cursor to tuple for sweepSwap call".to_string()) + })?; + let pool_hash: [u8; 32] = pool_cursor[1] + .to_owned() + .into_fixed_bytes() + .ok_or_else(|| { + anyhow!("Failed to convert pool hash to fixed bytes for sweepSwap call".to_string()) + })? + .try_into() + .unwrap(); + if let Ok(sweep_swap_output) = decode(sweep_swap_abi_output, &call.return_data) { + let pair_flow = sweep_swap_output[0] + .to_owned() + .into_tuple() + .ok_or_else(|| { + anyhow!("Failed to convert pair flow to tuple for sweepSwap call".to_string()) + })?; + + let base_flow = pair_flow[0] + .to_owned() + .into_int() // Needs conversion into bytes for next step + .ok_or_else(|| { + anyhow!("Failed to convert base flow to i128 for sweepSwap call".to_string()) + })?; + + let quote_flow = pair_flow[1] + .to_owned() + .into_int() // Needs conversion into bytes for next step + .ok_or_else(|| { + anyhow!("Failed to convert quote flow to i128 for sweepSwap call".to_string()) + })?; + + Ok((pool_hash, base_flow, quote_flow)) + } else { + bail!("Failed to decode sweepSwap outputs.".to_string()); + } + } else { + bail!("Failed to decode sweepSwap inputs.".to_string()); + } +} diff --git a/substreams/ethereum-ambient/src/contracts/mod.rs b/substreams/ethereum-ambient/src/contracts/mod.rs new file mode 100644 index 0000000..ecae537 --- /dev/null +++ b/substreams/ethereum-ambient/src/contracts/mod.rs @@ -0,0 +1,16 @@ +// @generated +pub mod warmpath { + include!("warmpath.rs"); +} +pub mod micropaths { + include!("micropaths.rs"); +} +pub mod knockout { + include!("knockout.rs"); +} +pub mod hotproxy { + include!("hotproxy.rs"); +} +pub mod main { + include!("main.rs"); +} diff --git a/substreams/ethereum-ambient/src/contracts/warmpath.rs b/substreams/ethereum-ambient/src/contracts/warmpath.rs new file mode 100644 index 0000000..29a680b --- /dev/null +++ b/substreams/ethereum-ambient/src/contracts/warmpath.rs @@ -0,0 +1,87 @@ +use anyhow::{anyhow, bail}; + +use crate::utils::{decode_flows_from_output, encode_pool_hash}; +use ethabi::{decode, ParamType}; +use hex_literal::hex; +use substreams_ethereum::pb::eth::v2::Call; + +pub const AMBIENT_WARMPATH_CONTRACT: [u8; 20] = hex!("d268767BE4597151Ce2BB4a70A9E368ff26cB195"); +pub const USER_CMD_WARMPATH_FN_SIG: [u8; 4] = hex!("f96dc788"); +const USER_CMD_EXTERNAL_ABI: &[ParamType] = &[ + ParamType::Bytes, // userCmd +]; + +const LIQUIDITY_CHANGE_ABI: &[ParamType] = &[ + ParamType::Uint(8), + ParamType::Address, // base + ParamType::Address, // quote + ParamType::Uint(256), // pool index + ParamType::Int(256), + ParamType::Uint(128), + ParamType::Uint(128), + ParamType::Uint(128), + ParamType::Uint(8), + ParamType::Address, +]; +pub fn decode_warm_path_user_cmd_call( + call: &Call, +) -> Result, anyhow::Error> { + if let Ok(external_cmd) = decode(USER_CMD_EXTERNAL_ABI, &call.input[4..]) { + let input_bytes = external_cmd[0] + .to_owned() + .into_bytes() // Convert Bytes32 to Vec + .ok_or_else(|| anyhow!("Failed to hotproxy userCmd input data.".to_string()))?; + + let code = input_bytes[31]; + let is_mint = [1, 11, 12, 3, 31, 32].contains(&code); + let is_burn = [2, 21, 22, 4, 41, 42].contains(&code); + let is_harvest = code == 5; + if is_mint || is_burn || is_harvest { + if let Ok(liquidity_change_calldata) = decode(LIQUIDITY_CHANGE_ABI, &input_bytes) { + let base_token = liquidity_change_calldata[1] + .to_owned() + .into_address() + .ok_or_else(|| { + anyhow!( + "Failed to convert base token to address for WarmPath userCmd call: {:?}", + &liquidity_change_calldata[1] + ) + })? + .to_fixed_bytes() + .to_vec(); + let quote_token = liquidity_change_calldata[2] + .to_owned() + .into_address() + .ok_or_else(|| { + anyhow!( + "Failed to convert quote token to address for WarmPath userCmd call: {:?}", + &liquidity_change_calldata[2] + ) + })? + .to_fixed_bytes() + .to_vec(); + + let mut pool_index_buf = [0u8; 32]; + liquidity_change_calldata[3] + .to_owned() + .into_uint() + .ok_or_else(|| { + anyhow!("Failed to convert pool index to bytes for WarmPath userCmd call" + .to_string()) + })? + .to_big_endian(&mut pool_index_buf); + let pool_index = pool_index_buf.to_vec(); + + let (base_flow, quote_flow) = decode_flows_from_output(call)?; + let pool_hash = encode_pool_hash(base_token, quote_token, pool_index); + Ok(Some((pool_hash, base_flow, quote_flow))) + } else { + bail!("Failed to decode inputs for WarmPath userCmd call.".to_string()); + } + } else { + Ok(None) + } + } else { + bail!("Failed to decode WarmPath call external input.".to_string()); + } +} diff --git a/substreams/ethereum-ambient/src/lib.rs b/substreams/ethereum-ambient/src/lib.rs index 5537a72..810581c 100644 --- a/substreams/ethereum-ambient/src/lib.rs +++ b/substreams/ethereum-ambient/src/lib.rs @@ -1,388 +1,5 @@ -use std::collections::{hash_map::Entry, HashMap}; +mod contracts; -use anyhow::{anyhow, bail}; -use ethabi::{decode, ParamType}; -use hex_literal::hex; -use substreams_ethereum::pb::eth::{self}; - -use pb::tycho::evm::v1::{self as tycho}; - -mod pb; - -const AMBIENT_CONTRACT: [u8; 20] = hex!("aaaaaaaaa24eeeb8d57d431224f73832bc34f688"); -const INIT_POOL_CODE: u8 = 71; -const USER_CMD_FN_SIG: [u8; 4] = [0xA1, 0x51, 0x12, 0xF9]; - -struct SlotValue { - new_value: Vec, - start_value: Vec, -} - -impl SlotValue { - fn has_changed(&self) -> bool { - self.start_value != self.new_value - } -} - -// Uses a map for slots, protobuf does not allow bytes in hashmap keys -struct InterimContractChange { - address: Vec, - balance: Vec, - code: Vec, - slots: HashMap, SlotValue>, - change: tycho::ChangeType, -} - -impl From for tycho::ContractChange { - fn from(value: InterimContractChange) -> Self { - tycho::ContractChange { - address: value.address, - balance: value.balance, - code: value.code, - slots: value - .slots - .into_iter() - .filter(|(_, value)| value.has_changed()) - .map(|(slot, value)| tycho::ContractSlot { slot, value: value.new_value }) - .collect(), - change: value.change.into(), - } - } -} - -/// Extracts all contract changes relevant to vm simulations -/// -/// This is the main logic of the substreams integration. It takes a raw ethereum block on input and -/// extracts the BlockContractChanges stream. It includes tracking: -/// - new pool initializations -/// - all storage slot changes for the Ambient contract -/// - all ERC20 balance changes for the Ambient pools -/// - all code changes and balance updates of the Ambient contract -/// -/// Generally we detect all changes in transactions sequentially and detect if it is a CREATE or -/// UPDATE change based on already present data. -#[substreams::handlers::map] -fn map_changes( - block: eth::v2::Block, -) -> Result { - let mut block_changes = tycho::BlockContractChanges::default(); - - let mut tx_change = tycho::TransactionContractChanges::default(); - - let mut changed_contracts: HashMap, InterimContractChange> = HashMap::new(); - - // Collect all accounts created in this block - let created_accounts: HashMap<_, _> = block - .transactions() - .flat_map(|tx| { - tx.calls.iter().flat_map(|call| { - call.account_creations - .iter() - .map(|ac| (&ac.account, ac.ordinal)) - }) - }) - .collect(); - - for block_tx in block.transactions() { - // Extract storage changes for all contracts relevant to this protocol system. - // Ambient is a protocol system consisting of many ProtocolComponents (one for each pool), - // but they all share the same AMBIENT_CONTRACT contract. - let mut storage_changes = block_tx - .calls - .iter() - .filter(|call| !call.state_reverted) - .flat_map(|call| { - call.storage_changes - .iter() - .filter(|c| c.address == AMBIENT_CONTRACT) - }) - .collect::>(); - storage_changes.sort_unstable_by_key(|change| change.ordinal); - - // Detect all call to the Ambient contracts, even inner calls - let ambient_calls = block_tx - .calls - .iter() - .filter(|call| !call.state_reverted) - .filter(|call| call.address == AMBIENT_CONTRACT) - .collect::>(); - - // Detect all pool initializations - // Official documentation: https://docs.ambient.finance/developers/dex-contract-interface/pool-initialization - for call in ambient_calls { - if call.input.len() < 4 { - continue; - } - if call.input[0..4] == USER_CMD_FN_SIG { - let user_cmd_external_abi_types = &[ - // index of the proxy sidecar the command is being called on - ParamType::Uint(16), - // call data for internal UserCmd method - ParamType::Bytes, - ]; - let user_cmd_internal_abi_types = &[ - ParamType::Uint(8), // command - ParamType::Address, // base - ParamType::Address, // quote - ParamType::Uint(256), // pool index - ParamType::Uint(128), // price - ]; - - // Decode external call to UserCmd - if let Ok(external_params) = decode(user_cmd_external_abi_types, &call.input[4..]) { - let cmd_bytes = external_params[1] - .to_owned() - .into_bytes() - .ok_or_else(|| { - anyhow!("Failed to convert to bytes: {:?}", &external_params[1]) - })?; - - // Call data is structured differently depending on the cmd code, so only - // decode if this is an init pool code. - if cmd_bytes[31] == INIT_POOL_CODE { - // Decode internal call to UserCmd - if let Ok(internal_params) = decode(user_cmd_internal_abi_types, &cmd_bytes) - { - let base = internal_params[1] - .to_owned() - .into_address() - .ok_or_else(|| { - anyhow!( - "Failed to convert to address: {:?}", - &internal_params[1] - ) - })? - .to_fixed_bytes() - .to_vec(); - - let quote = internal_params[2] - .to_owned() - .into_address() - .ok_or_else(|| { - anyhow!( - "Failed to convert to address: {:?}", - &internal_params[2] - ) - })? - .to_fixed_bytes() - .to_vec(); - - let pool_index = internal_params[3] - .to_owned() - .into_uint() - .ok_or_else(|| anyhow!("Failed to convert to u32".to_string()))? - .as_u32(); - - let static_attribute = tycho::Attribute { - name: String::from("pool_index"), - value: pool_index.to_be_bytes().to_vec(), - change: tycho::ChangeType::Creation.into(), - }; - - let mut tokens: Vec> = vec![base.clone(), quote.clone()]; - tokens.sort(); - - let new_component = tycho::ProtocolComponent { - id: format!( - "{}{}{}", - hex::encode(base.clone()), - hex::encode(quote.clone()), - pool_index - ), - tokens, - contracts: vec![AMBIENT_CONTRACT.to_vec()], - static_att: vec![static_attribute], - change: tycho::ChangeType::Creation.into(), - }; - tx_change - .component_changes - .push(new_component); - } else { - bail!("Failed to decode ABI internal call.".to_string()); - } - } - } else { - bail!("Failed to decode ABI external call.".to_string()); - } - } - } - - // Extract all contract changes. - // We cache the data in a general interim contract > slot > value data structure. - // Note: some contracts change slot values and change them back to their - // original value before the transactions ends we remember the initial - // value before the first change and in the end filter found deltas - // that ended up not actually changing anything. - for storage_change in storage_changes.iter() { - match changed_contracts.entry(storage_change.address.clone()) { - // We have already an entry recording a change about this contract - // only append the change about this storage slot - Entry::Occupied(mut e) => { - let contract_change = e.get_mut(); - match contract_change - .slots - .entry(storage_change.key.clone()) - { - // The storage slot was already changed before, simply - // update new_value - Entry::Occupied(mut v) => { - let slot_value = v.get_mut(); - slot_value - .new_value - .copy_from_slice(&storage_change.new_value); - } - // The storage slots is being initialised for the first time - Entry::Vacant(v) => { - v.insert(SlotValue { - new_value: storage_change.new_value.clone(), - start_value: storage_change.old_value.clone(), - }); - } - } - } - // Intialise a new contract change after observing a storage change - Entry::Vacant(e) => { - let mut slots = HashMap::new(); - slots.insert( - storage_change.key.clone(), - SlotValue { - new_value: storage_change.new_value.clone(), - start_value: storage_change.old_value.clone(), - }, - ); - e.insert(InterimContractChange { - address: storage_change.address.clone(), - balance: Vec::new(), - code: Vec::new(), - slots, - change: if created_accounts.contains_key(&storage_change.address) { - tycho::ChangeType::Creation - } else { - tycho::ChangeType::Update - }, - }); - } - } - } - - // Extract balance changes - let mut balance_changes = block_tx - .calls - .iter() - .filter(|call| !call.state_reverted) - .flat_map(|call| { - call.balance_changes - .iter() - .filter(|c| c.address == AMBIENT_CONTRACT) - }) - .collect::>(); - balance_changes.sort_unstable_by_key(|change| change.ordinal); - - for balance_change in balance_changes.iter() { - match changed_contracts.entry(balance_change.address.clone()) { - Entry::Occupied(mut e) => { - let contract_change = e.get_mut(); - if let Some(new_balance) = &balance_change.new_value { - contract_change.balance.clear(); - contract_change - .balance - .extend_from_slice(&new_balance.bytes); - } - } - Entry::Vacant(e) => { - if let Some(new_balance) = &balance_change.new_value { - e.insert(InterimContractChange { - address: balance_change.address.clone(), - balance: new_balance.bytes.clone(), - code: Vec::new(), - slots: HashMap::new(), - change: if created_accounts.contains_key(&balance_change.address) { - tycho::ChangeType::Creation - } else { - tycho::ChangeType::Update - }, - }); - } - } - } - } - - // Extract code changes - let mut code_changes = block_tx - .calls - .iter() - .filter(|call| !call.state_reverted) - .flat_map(|call| { - call.code_changes - .iter() - .filter(|c| c.address == AMBIENT_CONTRACT) - }) - .collect::>(); - code_changes.sort_unstable_by_key(|change| change.ordinal); - - for code_change in code_changes.iter() { - match changed_contracts.entry(code_change.address.clone()) { - Entry::Occupied(mut e) => { - let contract_change = e.get_mut(); - contract_change.code.clear(); - contract_change - .code - .extend_from_slice(&code_change.new_code); - } - Entry::Vacant(e) => { - e.insert(InterimContractChange { - address: code_change.address.clone(), - balance: Vec::new(), - code: code_change.new_code.clone(), - slots: HashMap::new(), - change: if created_accounts.contains_key(&code_change.address) { - tycho::ChangeType::Creation - } else { - tycho::ChangeType::Update - }, - }); - } - } - } - - // If there were any changes, add transaction and push the changes - if !storage_changes.is_empty() || !balance_changes.is_empty() || !code_changes.is_empty() { - tx_change.tx = Some(tycho::Transaction { - hash: block_tx.hash.clone(), - from: block_tx.from.clone(), - to: block_tx.to.clone(), - index: block_tx.index as u64, - }); - - // reuse changed_contracts hash map by draining it, next iteration - // will start empty. This avoids a costly reallocation - for (_, change) in changed_contracts.drain() { - tx_change - .contract_changes - .push(change.into()) - } - - block_changes - .changes - .push(tx_change.clone()); - - // clear out the interim contract changes after we pushed those. - tx_change.tx = None; - tx_change.contract_changes.clear(); - } - } - - block_changes.block = Some(tycho::Block { - number: block.number, - hash: block.hash.clone(), - parent_hash: block - .header - .as_ref() - .expect("Block header not present") - .parent_hash - .clone(), - ts: block.timestamp_seconds(), - }); - - Ok(block_changes) -} +pub use modules::*; +mod modules; +mod utils; diff --git a/substreams/ethereum-ambient/src/modules/1_map_pool_changes.rs b/substreams/ethereum-ambient/src/modules/1_map_pool_changes.rs new file mode 100644 index 0000000..741a162 --- /dev/null +++ b/substreams/ethereum-ambient/src/modules/1_map_pool_changes.rs @@ -0,0 +1,127 @@ +use substreams_ethereum::pb::eth::{self}; + +use crate::{ + contracts::{ + hotproxy::{ + decode_direct_swap_hotproxy_call, AMBIENT_HOTPROXY_CONTRACT, USER_CMD_HOTPROXY_FN_SIG, + }, + knockout::{decode_knockout_call, AMBIENT_KNOCKOUT_CONTRACT, USER_CMD_KNOCKOUT_FN_SIG}, + main::{ + decode_direct_swap_call, decode_pool_init, AMBIENT_CONTRACT, SWAP_FN_SIG, + USER_CMD_FN_SIG, + }, + micropaths::{ + decode_burn_ambient_call, decode_burn_range_call, decode_mint_ambient_call, + decode_mint_range_call, decode_sweep_swap_call, AMBIENT_MICROPATHS_CONTRACT, + BURN_AMBIENT_FN_SIG, BURN_RANGE_FN_SIG, MINT_AMBIENT_FN_SIG, MINT_RANGE_FN_SIG, + SWEEP_SWAP_FN_SIG, + }, + warmpath::{ + decode_warm_path_user_cmd_call, AMBIENT_WARMPATH_CONTRACT, USER_CMD_WARMPATH_FN_SIG, + }, + }, + utils::from_u256_to_vec, +}; +use tycho_substreams::{ + models::{AmbientBalanceDelta, BlockPoolChanges}, + prelude::Transaction, +}; + +#[substreams::handlers::map] +fn map_pool_changes(block: eth::v2::Block) -> Result { + let mut protocol_components = Vec::new(); + let mut balance_deltas = Vec::new(); + for block_tx in block.transactions() { + let tx = Transaction { + hash: block_tx.hash.clone(), + from: block_tx.from.clone(), + to: block_tx.to.clone(), + index: block_tx.index as u64, + }; + // extract storage changes + let mut storage_changes = block_tx + .calls + .iter() + .filter(|call| !call.state_reverted) + .flat_map(|call| { + call.storage_changes + .iter() + .filter(|c| c.address == AMBIENT_CONTRACT) + }) + .collect::>(); + storage_changes.sort_unstable_by_key(|change| change.ordinal); + + let block_calls = block_tx + .calls + .iter() + .filter(|call| !call.state_reverted) + .collect::>(); + + for call in block_calls { + if call.input.len() < 4 { + continue; + } + let selector: [u8; 4] = call.input[0..4].try_into().unwrap(); + let address: [u8; 20] = call.address.clone().try_into().unwrap(); + + if call.address == AMBIENT_CONTRACT && selector == USER_CMD_FN_SIG { + // Extract pool creations + if let Some(protocol_component) = decode_pool_init(call, tx.clone())? { + protocol_components.push(protocol_component); + } + } + + // Extract TVL changes + let result = match (address, selector) { + (AMBIENT_CONTRACT, SWAP_FN_SIG) => Some(decode_direct_swap_call(call)?), + (AMBIENT_HOTPROXY_CONTRACT, USER_CMD_HOTPROXY_FN_SIG) => { + Some(decode_direct_swap_hotproxy_call(call)?) + } + (AMBIENT_MICROPATHS_CONTRACT, SWEEP_SWAP_FN_SIG) => { + Some(decode_sweep_swap_call(call)?) + } + (AMBIENT_WARMPATH_CONTRACT, USER_CMD_WARMPATH_FN_SIG) => { + decode_warm_path_user_cmd_call(call)? + } + (AMBIENT_MICROPATHS_CONTRACT, MINT_RANGE_FN_SIG) => { + Some(decode_mint_range_call(call)?) + } + (AMBIENT_MICROPATHS_CONTRACT, MINT_AMBIENT_FN_SIG) => { + Some(decode_mint_ambient_call(call)?) + } + (AMBIENT_MICROPATHS_CONTRACT, BURN_RANGE_FN_SIG) => { + Some(decode_burn_range_call(call)?) + } + (AMBIENT_MICROPATHS_CONTRACT, BURN_AMBIENT_FN_SIG) => { + Some(decode_burn_ambient_call(call)?) + } + (AMBIENT_KNOCKOUT_CONTRACT, USER_CMD_KNOCKOUT_FN_SIG) => { + Some(decode_knockout_call(call)?) + } + _ => None, + }; + let (pool_hash, base_flow, quote_flow) = match result { + Some((pool_hash, base_flow, quote_flow)) => (pool_hash, base_flow, quote_flow), + None => continue, + }; + let base_balance_delta = AmbientBalanceDelta { + pool_hash: Vec::from(pool_hash), + token_type: "base".to_string(), + token_delta: from_u256_to_vec(base_flow), + ordinal: call.index as u64, + tx: Some(tx.clone()), + }; + let quote_balance_delta = AmbientBalanceDelta { + pool_hash: Vec::from(pool_hash), + token_type: "quote".to_string(), + token_delta: from_u256_to_vec(quote_flow), + ordinal: call.index as u64, + tx: Some(tx.clone()), + }; + balance_deltas.extend([base_balance_delta.clone(), quote_balance_delta.clone()]); + } + } + balance_deltas.sort_by_key(|delta| (delta.ordinal, delta.token_type.clone())); + let pool_changes = BlockPoolChanges { protocol_components, balance_deltas }; + Ok(pool_changes) +} diff --git a/substreams/ethereum-ambient/src/modules/2_store_pool_balances.rs b/substreams/ethereum-ambient/src/modules/2_store_pool_balances.rs new file mode 100644 index 0000000..28fd494 --- /dev/null +++ b/substreams/ethereum-ambient/src/modules/2_store_pool_balances.rs @@ -0,0 +1,18 @@ +use substreams::{ + scalar::BigInt, + store::{StoreAdd, StoreAddBigInt, StoreNew}, +}; +use tycho_substreams::models::BlockPoolChanges; + +#[substreams::handlers::store] +pub fn store_pool_balances(changes: BlockPoolChanges, balance_store: StoreAddBigInt) { + let deltas = changes.balance_deltas.clone(); + for balance_delta in deltas { + let pool_hash_hex = hex::encode(&balance_delta.pool_hash); + balance_store.add( + balance_delta.ordinal, + format!("{}:{}", pool_hash_hex, balance_delta.token_type), + BigInt::from_signed_bytes_be(&balance_delta.token_delta), + ); + } +} diff --git a/substreams/ethereum-ambient/src/modules/2_store_pools.rs b/substreams/ethereum-ambient/src/modules/2_store_pools.rs new file mode 100644 index 0000000..8b30721 --- /dev/null +++ b/substreams/ethereum-ambient/src/modules/2_store_pools.rs @@ -0,0 +1,9 @@ +use substreams::store::{StoreNew, StoreSet, StoreSetProto}; +use tycho_substreams::models::{BlockPoolChanges, ProtocolComponent}; + +#[substreams::handlers::store] +pub fn store_pools(changes: BlockPoolChanges, component_store: StoreSetProto) { + for component in changes.protocol_components { + component_store.set(0, component.id.clone(), &component); + } +} diff --git a/substreams/ethereum-ambient/src/modules/3_map_changes.rs b/substreams/ethereum-ambient/src/modules/3_map_changes.rs new file mode 100644 index 0000000..70c62d1 --- /dev/null +++ b/substreams/ethereum-ambient/src/modules/3_map_changes.rs @@ -0,0 +1,367 @@ +use num_bigint::BigInt; +use std::{ + collections::{hash_map::Entry, HashMap}, + str::FromStr, +}; +use substreams::pb::substreams::StoreDeltas; + +use substreams_ethereum::pb::eth::{self}; + +use crate::contracts::main::AMBIENT_CONTRACT; +use substreams::store::{StoreGet, StoreGetProto}; +use tycho_substreams::prelude::*; + +struct SlotValue { + new_value: Vec, + start_value: Vec, +} + +impl SlotValue { + fn has_changed(&self) -> bool { + self.start_value != self.new_value + } +} + +// uses a map for slots, protobuf does not +// allow bytes in hashmap keys +struct InterimContractChange { + address: Vec, + balance: Vec, + code: Vec, + slots: HashMap, SlotValue>, + change: ChangeType, +} + +impl From for ContractChange { + fn from(value: InterimContractChange) -> Self { + ContractChange { + address: value.address, + balance: value.balance, + code: value.code, + slots: value + .slots + .into_iter() + .filter(|(_, value)| value.has_changed()) + .map(|(slot, value)| ContractSlot { slot, value: value.new_value }) + .collect(), + change: value.change.into(), + } + } +} + +/// Extracts all contract changes relevant to vm simulations +/// +/// This implementation has currently two major limitations: +/// 1. It is hardwired to only care about changes to the ambient main contract, this is ok for this +/// particular use case but for a more general purpose implementation this is not ideal +/// 2. Changes are processed separately, this means that if there are any side effects between each +/// other (e.g. if account is deleted and then created again in ethereum all the storage is set +/// to 0. So there is a side effect between account creation and contract storage.) these might +/// not be properly accounted for. Most of the time this should not be a major issue but may lead +/// to wrong results so consume this implementation with care. See example below for a concrete +/// case where this is problematic. +/// +/// ## A very contrived example: +/// 1. Some existing contract receives a transaction that changes it state, the state is updated +/// 2. Next, this contract has self destruct called on itself +/// 3. The contract is created again using CREATE2 at the same address +/// 4. The contract receives a transaction that changes it state +/// 5. We would emit this as as contract creation with slots set from 1 and from 4, although we +/// should only emit the slots changed from 4. +#[substreams::handlers::map] +fn map_changes( + block: eth::v2::Block, + block_pool_changes: BlockPoolChanges, + balance_store: StoreDeltas, + pool_store: StoreGetProto, +) -> Result { + let mut block_changes = BlockContractChanges::default(); + + let mut tx_change = TransactionContractChanges::default(); + + let mut changed_contracts: HashMap, InterimContractChange> = HashMap::new(); + + let created_accounts: HashMap<_, _> = block + .transactions() + .flat_map(|tx| { + tx.calls.iter().flat_map(|call| { + call.account_creations + .iter() + .map(|ac| (&ac.account, ac.ordinal)) + }) + }) + .collect(); + + for block_tx in block.transactions() { + // extract storage changes + let mut storage_changes = block_tx + .calls + .iter() + .filter(|call| !call.state_reverted) + .flat_map(|call| { + call.storage_changes + .iter() + .filter(|c| c.address == AMBIENT_CONTRACT) + }) + .collect::>(); + storage_changes.sort_unstable_by_key(|change| change.ordinal); + + // Note: some contracts change slot values and change them back to their + // original value before the transactions ends we remember the initial + // value before the first change and in the end filter found deltas + // that ended up not actually changing anything. + for storage_change in storage_changes.iter() { + match changed_contracts.entry(storage_change.address.clone()) { + // We have already an entry recording a change about this contract + // only append the change about this storage slot + Entry::Occupied(mut e) => { + let contract_change = e.get_mut(); + match contract_change + .slots + .entry(storage_change.key.clone()) + { + // The storage slot was already changed before, simply + // update new_value + Entry::Occupied(mut v) => { + let slot_value = v.get_mut(); + slot_value + .new_value + .copy_from_slice(&storage_change.new_value); + } + // The storage slots is being initialised for the first time + Entry::Vacant(v) => { + v.insert(SlotValue { + new_value: storage_change.new_value.clone(), + start_value: storage_change.old_value.clone(), + }); + } + } + } + // Intialise a new contract change after observing a storage change + Entry::Vacant(e) => { + let mut slots = HashMap::new(); + slots.insert( + storage_change.key.clone(), + SlotValue { + new_value: storage_change.new_value.clone(), + start_value: storage_change.old_value.clone(), + }, + ); + e.insert(InterimContractChange { + address: storage_change.address.clone(), + balance: Vec::new(), + code: Vec::new(), + slots, + change: if created_accounts.contains_key(&storage_change.address) { + ChangeType::Creation + } else { + ChangeType::Update + }, + }); + } + } + } + + // extract balance changes + let mut balance_changes = block_tx + .calls + .iter() + .filter(|call| !call.state_reverted) + .flat_map(|call| { + call.balance_changes + .iter() + .filter(|c| c.address == AMBIENT_CONTRACT) + }) + .collect::>(); + balance_changes.sort_unstable_by_key(|change| change.ordinal); + + for balance_change in balance_changes.iter() { + match changed_contracts.entry(balance_change.address.clone()) { + Entry::Occupied(mut e) => { + let contract_change = e.get_mut(); + if let Some(new_balance) = &balance_change.new_value { + contract_change.balance.clear(); + contract_change + .balance + .extend_from_slice(&new_balance.bytes); + } + } + Entry::Vacant(e) => { + if let Some(new_balance) = &balance_change.new_value { + e.insert(InterimContractChange { + address: balance_change.address.clone(), + balance: new_balance.bytes.clone(), + code: Vec::new(), + slots: HashMap::new(), + change: if created_accounts.contains_key(&balance_change.address) { + ChangeType::Creation + } else { + ChangeType::Update + }, + }); + } + } + } + } + + // extract code changes + let mut code_changes = block_tx + .calls + .iter() + .filter(|call| !call.state_reverted) + .flat_map(|call| { + call.code_changes + .iter() + .filter(|c| c.address == AMBIENT_CONTRACT) + }) + .collect::>(); + code_changes.sort_unstable_by_key(|change| change.ordinal); + + for code_change in code_changes.iter() { + match changed_contracts.entry(code_change.address.clone()) { + Entry::Occupied(mut e) => { + let contract_change = e.get_mut(); + contract_change.code.clear(); + contract_change + .code + .extend_from_slice(&code_change.new_code); + } + Entry::Vacant(e) => { + e.insert(InterimContractChange { + address: code_change.address.clone(), + balance: Vec::new(), + code: code_change.new_code.clone(), + slots: HashMap::new(), + change: if created_accounts.contains_key(&code_change.address) { + ChangeType::Creation + } else { + ChangeType::Update + }, + }); + } + } + } + + // if there were any changes, add transaction and push the changes + if !storage_changes.is_empty() || !balance_changes.is_empty() || !code_changes.is_empty() { + tx_change.tx = Some(Transaction { + hash: block_tx.hash.clone(), + from: block_tx.from.clone(), + to: block_tx.to.clone(), + index: block_tx.index as u64, + }); + + // reuse changed_contracts hash map by draining it, next iteration + // will start empty. This avoids a costly reallocation + for (_, change) in changed_contracts.drain() { + tx_change + .contract_changes + .push(change.into()) + } + + block_changes + .changes + .push(tx_change.clone()); + + // clear out the interim contract changes after we pushed those. + tx_change.tx = None; + tx_change.contract_changes.clear(); + } + } + let mut grouped_components = HashMap::new(); + for component in &block_pool_changes.protocol_components { + let tx_hash = component + .tx + .clone() + .expect("Transaction is missing") + .hash; + grouped_components + .entry(tx_hash) + .or_insert_with(Vec::new) + .push(component.clone()); + } + + for (tx_hash, components) in grouped_components { + if let Some(tx_change) = block_changes + .changes + .iter_mut() + // TODO: be better than this (quadratic complexity) + .find(|tx_change| { + tx_change + .tx + .as_ref() + .map_or(false, |tx| tx.hash == tx_hash) + }) + { + tx_change + .component_changes + .extend(components); + } + } + let mut balance_changes = HashMap::new(); + balance_store + .deltas + .into_iter() + .zip(block_pool_changes.balance_deltas) + .for_each(|(store_delta, balance_delta)| { + let pool_hash_hex = hex::encode(balance_delta.pool_hash); + let pool = match pool_store.get_last(pool_hash_hex.clone()) { + Some(pool) => pool, + None => panic!("Pool not found in store for given hash: {}", pool_hash_hex), + }; + let token_type = substreams::key::segment_at(&store_delta.key, 1); + let token_index = if token_type == "quote" { 1 } else { 0 }; + + // store_delta.new_value is an ASCII string representing an integer + let ascii_string = + String::from_utf8(store_delta.new_value.clone()).expect("Invalid UTF-8 sequence"); + let balance = BigInt::from_str(&ascii_string).expect("Failed to parse integer"); + let big_endian_bytes_balance = balance.to_bytes_be().1; + + let balance_change = BalanceChange { + component_id: pool_hash_hex.as_bytes().to_vec(), + token: pool.tokens[token_index].clone(), + balance: big_endian_bytes_balance.to_vec(), + }; + let tx_hash = balance_delta + .tx + .expect("Transaction is missing") + .hash; + balance_changes + .entry(tx_hash) + .or_insert_with(Vec::new) + .push(balance_change); + }); + + for (tx_hash, grouped_balance_changes) in balance_changes { + if let Some(tx_change) = block_changes + .changes + .iter_mut() + // TODO: be better than this (quadratic complexity) + .find(|tx_change| { + tx_change + .tx + .as_ref() + .map_or(false, |tx| tx.hash == tx_hash) + }) + { + tx_change + .balance_changes + .extend(grouped_balance_changes); + } + } + + block_changes.block = Some(Block { + number: block.number, + hash: block.hash.clone(), + parent_hash: block + .header + .as_ref() + .expect("Block header not present") + .parent_hash + .clone(), + ts: block.timestamp_seconds(), + }); + + Ok(block_changes) +} diff --git a/substreams/ethereum-ambient/src/modules/mod.rs b/substreams/ethereum-ambient/src/modules/mod.rs new file mode 100644 index 0000000..0c5d9d5 --- /dev/null +++ b/substreams/ethereum-ambient/src/modules/mod.rs @@ -0,0 +1,16 @@ +pub use map_changes::map_changes; +pub use map_pool_changes::map_pool_changes; +pub use store_pool_balances::store_pool_balances; +pub use store_pools::store_pools; + +#[path = "1_map_pool_changes.rs"] +mod map_pool_changes; + +#[path = "2_store_pools.rs"] +mod store_pools; + +#[path = "2_store_pool_balances.rs"] +mod store_pool_balances; + +#[path = "3_map_changes.rs"] +mod map_changes; diff --git a/substreams/ethereum-ambient/src/pb/mod.rs b/substreams/ethereum-ambient/src/pb/mod.rs deleted file mode 100644 index 43d8838..0000000 --- a/substreams/ethereum-ambient/src/pb/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -// @generated -pub mod tycho { - pub mod evm { - // @@protoc_insertion_point(attribute:tycho.evm.v1) - pub mod v1 { - include!("tycho.evm.v1.rs"); - // @@protoc_insertion_point(tycho.evm.v1) - } - } -} diff --git a/substreams/ethereum-ambient/src/pb/tycho.evm.v1.rs b/substreams/ethereum-ambient/src/pb/tycho.evm.v1.rs deleted file mode 100644 index b59b5f7..0000000 --- a/substreams/ethereum-ambient/src/pb/tycho.evm.v1.rs +++ /dev/null @@ -1,183 +0,0 @@ -// @generated -// This file contains the proto definitions for Substreams common to all integrations. - -/// A struct describing a block. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Block { - /// The blocks hash. - #[prost(bytes="vec", tag="1")] - pub hash: ::prost::alloc::vec::Vec, - /// The parent blocks hash. - #[prost(bytes="vec", tag="2")] - pub parent_hash: ::prost::alloc::vec::Vec, - /// The block number. - #[prost(uint64, tag="3")] - pub number: u64, - /// The block timestamp. - #[prost(uint64, tag="4")] - pub ts: u64, -} -/// A struct describing a transaction. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Transaction { - /// The transaction hash. - #[prost(bytes="vec", tag="1")] - pub hash: ::prost::alloc::vec::Vec, - /// The sender of the transaction. - #[prost(bytes="vec", tag="2")] - pub from: ::prost::alloc::vec::Vec, - /// The receiver of the transaction. - #[prost(bytes="vec", tag="3")] - pub to: ::prost::alloc::vec::Vec, - /// The transactions index within the block. - #[prost(uint64, tag="4")] - pub index: u64, -} -/// A custom struct representing an arbitrary attribute of a protocol component. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Attribute { - /// The name of the attribute. - #[prost(string, tag="1")] - pub name: ::prost::alloc::string::String, - /// The value of the attribute. - #[prost(bytes="vec", tag="2")] - pub value: ::prost::alloc::vec::Vec, - /// The type of change the attribute underwent. - #[prost(enumeration="ChangeType", tag="3")] - pub change: i32, -} -/// A struct describing a part of the protocol. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ProtocolComponent { - /// A unique identifier for the component within the protocol. - /// Can be a stringified address or a string describing the trading pair. - #[prost(string, tag="1")] - pub id: ::prost::alloc::string::String, - /// Addresses of the ERC20 tokens used by the component. - #[prost(bytes="vec", repeated, tag="2")] - pub tokens: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - /// Addresses of the contracts used by the component. - #[prost(bytes="vec", repeated, tag="3")] - pub contracts: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - /// Attributes of the component. - /// The inner ChangeType of the attribute has to match the ChangeType of the ProtocolComponent. - #[prost(message, repeated, tag="4")] - pub static_att: ::prost::alloc::vec::Vec, - /// Type of change the component underwent. - #[prost(enumeration="ChangeType", tag="5")] - pub change: i32, -} -/// A struct for following the changes of Total Value Locked (TVL) of a protocol component. -/// Note that if the ProtocolComponent contains multiple contracts, the TVL is tracked for the component as a whole. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BalanceChange { - /// The address of the ERC20 token whose balance changed. - #[prost(bytes="vec", tag="1")] - pub token: ::prost::alloc::vec::Vec, - /// The new balance of the token. - #[prost(bytes="vec", tag="2")] - pub balance: ::prost::alloc::vec::Vec, - /// The id of the component whose TVL is tracked. - #[prost(bytes="vec", tag="3")] - pub component_id: ::prost::alloc::vec::Vec, -} -/// Enum to specify the type of a change. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum ChangeType { - Unspecified = 0, - Update = 1, - Creation = 2, - Deletion = 3, -} -impl ChangeType { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - ChangeType::Unspecified => "CHANGE_TYPE_UNSPECIFIED", - ChangeType::Update => "CHANGE_TYPE_UPDATE", - ChangeType::Creation => "CHANGE_TYPE_CREATION", - ChangeType::Deletion => "CHANGE_TYPE_DELETION", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "CHANGE_TYPE_UNSPECIFIED" => Some(Self::Unspecified), - "CHANGE_TYPE_UPDATE" => Some(Self::Update), - "CHANGE_TYPE_CREATION" => Some(Self::Creation), - "CHANGE_TYPE_DELETION" => Some(Self::Deletion), - _ => None, - } - } -} -// This file contains proto definitions specific to the VM integration. - -/// A key value entry into contract storage. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ContractSlot { - /// A contract's storage slot. - #[prost(bytes="vec", tag="2")] - pub slot: ::prost::alloc::vec::Vec, - /// The new value for this storage slot. - #[prost(bytes="vec", tag="3")] - pub value: ::prost::alloc::vec::Vec, -} -/// Changes made to a single contract's state. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ContractChange { - /// The contract's address - #[prost(bytes="vec", tag="1")] - pub address: ::prost::alloc::vec::Vec, - /// The new balance of the contract, empty bytes indicates no change. - #[prost(bytes="vec", tag="2")] - pub balance: ::prost::alloc::vec::Vec, - /// The new code of the contract, empty bytes indicates no change. - #[prost(bytes="vec", tag="3")] - pub code: ::prost::alloc::vec::Vec, - /// The changes to this contract's slots, empty sequence indicates no change. - #[prost(message, repeated, tag="4")] - pub slots: ::prost::alloc::vec::Vec, - /// Whether this is an update, a creation or a deletion. - #[prost(enumeration="ChangeType", tag="5")] - pub change: i32, -} -/// A set of changes aggregated by transaction. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct TransactionContractChanges { - /// The transaction instance that results in the changes. - #[prost(message, optional, tag="1")] - pub tx: ::core::option::Option, - /// Contains the changes induced by the above transaction, aggregated on a per-contract basis. - #[prost(message, repeated, tag="2")] - pub contract_changes: ::prost::alloc::vec::Vec, - /// An array of newly added components. - #[prost(message, repeated, tag="3")] - pub component_changes: ::prost::alloc::vec::Vec, - /// An array of balance changes to components. - #[prost(message, repeated, tag="4")] - pub balance_changes: ::prost::alloc::vec::Vec, -} -/// A set of transaction changes within a single block. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockContractChanges { - /// The block for which these changes are collectively computed. - #[prost(message, optional, tag="1")] - pub block: ::core::option::Option, - /// The set of transaction changes observed in the specified block. - #[prost(message, repeated, tag="2")] - pub changes: ::prost::alloc::vec::Vec, -} -// @@protoc_insertion_point(module) diff --git a/substreams/ethereum-ambient/src/utils.rs b/substreams/ethereum-ambient/src/utils.rs new file mode 100644 index 0000000..67e072a --- /dev/null +++ b/substreams/ethereum-ambient/src/utils.rs @@ -0,0 +1,55 @@ +use anyhow::{anyhow, bail}; +use ethabi::{decode, ethereum_types::U256, ParamType, Token, Uint}; +use substreams_ethereum::pb::eth::v2::Call; +use tiny_keccak::{Hasher, Keccak}; + +pub fn encode_pool_hash(token_x: Vec, token_y: Vec, pool_idx: Vec) -> [u8; 32] { + let base_address = ethabi::Address::from_slice(&token_x); + let quote_address = ethabi::Address::from_slice(&token_y); + let pool_idx_uint = Uint::from_big_endian(&pool_idx); + + let encoded = ethabi::encode(&[ + Token::Address(base_address), + Token::Address(quote_address), + Token::Uint(pool_idx_uint), + ]); + + let mut hasher = Keccak::v256(); + hasher.update(&encoded); + let mut output = [0u8; 32]; + hasher.finalize(&mut output); + + output +} + +pub fn decode_flows_from_output(call: &Call) -> Result<(ethabi::Int, ethabi::Int), anyhow::Error> { + if let Ok(external_outputs) = decode(BASE_QUOTE_FLOW_OUTPUT, &call.return_data) { + let base_flow = external_outputs[0] + .to_owned() + .into_int() // Needs conversion into bytes for next step + .ok_or_else(|| anyhow!("Failed to convert base flow to i128".to_string()))?; + + let quote_flow = external_outputs[1] + .to_owned() + .into_int() // Needs conversion into bytes for next step + .ok_or_else(|| anyhow!("Failed to convert quote flow to i128".to_string()))?; + Ok((base_flow, quote_flow)) + } else { + bail!("Failed to decode swap call outputs.".to_string()); + } +} + +const BASE_QUOTE_FLOW_OUTPUT: &[ParamType] = &[ + // The token base and quote token flows associated with this swap action. + // Negative indicates a credit paid to the user (token balance of pool + // decreases), positive a debit collected from the user (token balance of pool + // increases). + ParamType::Int(128), // baseFlow + ParamType::Int(128), // quoteFlow +]; + +pub fn from_u256_to_vec(src: U256) -> Vec { + let mut buf = [0u8; 32]; + src.to_big_endian(&mut buf); + buf.to_vec() +} diff --git a/substreams/ethereum-ambient/substreams.yaml b/substreams/ethereum-ambient/substreams.yaml index 8007c2d..3541139 100644 --- a/substreams/ethereum-ambient/substreams.yaml +++ b/substreams/ethereum-ambient/substreams.yaml @@ -1,14 +1,13 @@ specVersion: v0.1.0 package: name: "substreams_ethereum_ambient" - version: v0.3.0 + version: v0.5.0 protobuf: files: - - vm.proto - - common.proto + - ambient.proto importPaths: - - ../../proto/tycho/evm/v1/ + - ./proto binaries: default: @@ -16,9 +15,35 @@ binaries: file: ../../target/wasm32-unknown-unknown/substreams/substreams_ethereum_ambient.wasm modules: - - name: map_changes + - name: map_pool_changes kind: map + initialBlock: 17361664 inputs: - source: sf.ethereum.type.v2.Block output: - type: proto:tycho.evm.state.v1.BlockContractChanges + type: proto:tycho.evm.v1.BlockPoolChanges + - name: store_pools + kind: store + initialBlock: 17361664 + updatePolicy: set + valueType: proto:tycho.evm.v1.ProtocolComponent + inputs: + - map: map_pool_changes + - name: store_pool_balances + kind: store + initialBlock: 17361664 + updatePolicy: add + valueType: bigint + inputs: + - map: map_pool_changes + - name: map_changes + kind: map + initialBlock: 17361664 + inputs: + - source: sf.ethereum.type.v2.Block + - map: map_pool_changes + - store: store_pool_balances + mode: deltas + - store: store_pools + output: + type: proto:tycho.evm.v1.BlockContractChanges From 6f4c24500b0ac8f29ff68915599bae560d169cc7 Mon Sep 17 00:00:00 2001 From: zizou <111426680+flopell@users.noreply.github.com> Date: Mon, 14 Oct 2024 18:11:53 +0200 Subject: [PATCH 6/8] chore(substreams): bump versions and update yaml files --- substreams/Cargo.lock | 6 +++--- substreams/ethereum-ambient/Cargo.toml | 2 +- substreams/ethereum-ambient/substreams.yaml | 7 +++++-- substreams/ethereum-uniswap-v2/Cargo.toml | 2 +- substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml | 5 ++++- substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml | 5 ++++- substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml | 5 ++++- substreams/ethereum-uniswap-v3/Cargo.toml | 2 +- substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml | 5 ++++- substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml | 5 ++++- 10 files changed, 31 insertions(+), 13 deletions(-) diff --git a/substreams/Cargo.lock b/substreams/Cargo.lock index b7055cb..3132472 100644 --- a/substreams/Cargo.lock +++ b/substreams/Cargo.lock @@ -991,7 +991,7 @@ dependencies = [ [[package]] name = "substreams-ethereum-ambient" -version = "0.5.0" +version = "0.5.1" dependencies = [ "anyhow", "bytes", @@ -1041,7 +1041,7 @@ dependencies = [ [[package]] name = "substreams-ethereum-uniswap-v2" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "ethabi 18.0.0", @@ -1060,7 +1060,7 @@ dependencies = [ [[package]] name = "substreams-ethereum-uniswap-v3" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "ethabi 18.0.0", diff --git a/substreams/ethereum-ambient/Cargo.toml b/substreams/ethereum-ambient/Cargo.toml index 45da3f0..4738c67 100644 --- a/substreams/ethereum-ambient/Cargo.toml +++ b/substreams/ethereum-ambient/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substreams-ethereum-ambient" -version = "0.5.0" +version = "0.5.1" edition = "2021" [lib] diff --git a/substreams/ethereum-ambient/substreams.yaml b/substreams/ethereum-ambient/substreams.yaml index 3541139..e3e3805 100644 --- a/substreams/ethereum-ambient/substreams.yaml +++ b/substreams/ethereum-ambient/substreams.yaml @@ -1,13 +1,16 @@ specVersion: v0.1.0 package: name: "substreams_ethereum_ambient" - version: v0.5.0 + version: v0.5.1 protobuf: files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/vm.proto - ambient.proto importPaths: - - ./proto + - ./proto/v1 + - ../../proto/ binaries: default: diff --git a/substreams/ethereum-uniswap-v2/Cargo.toml b/substreams/ethereum-uniswap-v2/Cargo.toml index 6348817..1979ddd 100644 --- a/substreams/ethereum-uniswap-v2/Cargo.toml +++ b/substreams/ethereum-uniswap-v2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substreams-ethereum-uniswap-v2" -version = "0.2.0" +version = "0.2.1" edition = "2021" [lib] diff --git a/substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml b/substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml index 1944e69..8f56fa8 100644 --- a/substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml +++ b/substreams/ethereum-uniswap-v2/ethereum-pancakeswap.yaml @@ -1,13 +1,16 @@ specVersion: v0.1.0 package: name: "ethereum_pancakeswap" - version: v0.2.0 + version: v0.2.1 protobuf: files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/entity.proto - uniswap.proto importPaths: - ./proto/v1 + - ../../proto/ binaries: default: diff --git a/substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml b/substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml index 8a9233c..075a8b6 100644 --- a/substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml +++ b/substreams/ethereum-uniswap-v2/ethereum-sushiswap.yaml @@ -1,13 +1,16 @@ specVersion: v0.1.0 package: name: "ethereum_sushiswap_v2" - version: v0.2.0 + version: v0.2.1 protobuf: files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/entity.proto - uniswap.proto importPaths: - ./proto/v1 + - ../../proto/ binaries: default: diff --git a/substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml b/substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml index c1e8bb1..3c247a6 100644 --- a/substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml +++ b/substreams/ethereum-uniswap-v2/ethereum-uniswap-v2.yaml @@ -1,13 +1,16 @@ specVersion: v0.1.0 package: name: "ethereum_uniswap_v2" - version: v0.2.0 + version: v0.2.1 protobuf: files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/entity.proto - uniswap.proto importPaths: - ./proto/v1 + - ../../proto/ binaries: default: diff --git a/substreams/ethereum-uniswap-v3/Cargo.toml b/substreams/ethereum-uniswap-v3/Cargo.toml index 101914d..edba152 100644 --- a/substreams/ethereum-uniswap-v3/Cargo.toml +++ b/substreams/ethereum-uniswap-v3/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substreams-ethereum-uniswap-v3" -version = "0.2.0" +version = "0.2.1" edition = "2021" [lib] diff --git a/substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml b/substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml index 4937787..5498de4 100644 --- a/substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml +++ b/substreams/ethereum-uniswap-v3/arbitrum-uniswap-v3.yaml @@ -1,13 +1,16 @@ specVersion: v0.1.0 package: name: "substreams_arbitrum_uniswap_v3" - version: v0.2.0 + version: v0.2.1 protobuf: files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/entity.proto - uniswap.proto importPaths: - ./proto/v1 + - ../../proto/ binaries: default: diff --git a/substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml b/substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml index e26388c..db38c86 100644 --- a/substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml +++ b/substreams/ethereum-uniswap-v3/ethereum-uniswap-v3.yaml @@ -1,13 +1,16 @@ specVersion: v0.1.0 package: name: "substreams_ethereum_uniswap_v3" - version: v0.2.0 + version: v0.2.1 protobuf: files: + - tycho/evm/v1/common.proto + - tycho/evm/v1/entity.proto - uniswap.proto importPaths: - ./proto/v1 + - ../../proto/ binaries: default: From 4c448e701a31dbaf4082407bab01ebb66958ab8c Mon Sep 17 00:00:00 2001 From: zizou <111426680+flopell@users.noreply.github.com> Date: Mon, 14 Oct 2024 18:16:18 +0200 Subject: [PATCH 7/8] ci: make clippy happy --- substreams/crates/substreams-helper/src/storage_change.rs | 5 ++--- substreams/ethereum-uniswap-v2/src/traits.rs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/substreams/crates/substreams-helper/src/storage_change.rs b/substreams/crates/substreams-helper/src/storage_change.rs index 0fe484e..bdc0ce8 100644 --- a/substreams/crates/substreams-helper/src/storage_change.rs +++ b/substreams/crates/substreams-helper/src/storage_change.rs @@ -6,9 +6,8 @@ pub trait StorageChangesFilter { impl StorageChangesFilter for Vec { fn filter_by_address(&self, contract_addr: &[u8; 20]) -> Vec<&StorageChange> { - return self - .iter() + self.iter() .filter(|change| change.address == contract_addr) - .collect(); + .collect() } } diff --git a/substreams/ethereum-uniswap-v2/src/traits.rs b/substreams/ethereum-uniswap-v2/src/traits.rs index b3aa69d..15fbcbe 100644 --- a/substreams/ethereum-uniswap-v2/src/traits.rs +++ b/substreams/ethereum-uniswap-v2/src/traits.rs @@ -11,7 +11,7 @@ pub struct PoolAddresser<'a> { pub store: &'a StoreGetProto, } -impl<'a> HasAddresser for PoolAddresser<'a> { +impl HasAddresser for PoolAddresser<'_> { fn has_address(&self, key: Address) -> bool { let pool = self .store From f602e01942b99629acd5f275704fbf784cd2e6f0 Mon Sep 17 00:00:00 2001 From: zizou <111426680+flopell@users.noreply.github.com> Date: Tue, 15 Oct 2024 10:15:38 +0200 Subject: [PATCH 8/8] format(substreams): separate code and imports --- substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs b/substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs index 582706c..236a427 100644 --- a/substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs +++ b/substreams/ethereum-uniswap-v2/src/modules/1_map_pool_created.rs @@ -10,6 +10,7 @@ use substreams_helper::{event_handler::EventHandler, hex::Hexable}; use crate::abi::factory::events::PairCreated; use tycho_substreams::prelude::*; + #[derive(Debug, Deserialize)] struct Params { factory_address: String,