refactor(substreams): improve logic to ignore updates (#96)

* refactor(substreams): ignore transaction if contracts update are ignored.

There are some cases where we ignore contracts updates (for example if the old and new values are the same). In that case if the transaction only contains ignored updates we don't emit it.

* refactor(substreams): ignore deletions for freshly created attributes.

There are cases where an attribute can be created and deleted during the same transaction. To avoid sending a confusing deletion for something that was never created before, we just ignore the deletion in that particular case.

* feat(substreams): Add uniswap V3 logs only module (#98)

* feat(substreams): add uniswapV3 logs only Substreams module

* refactor(substreams): encode everything as big endian

* refactor(substreams): mark changes as creation when a tick liq is updated from 0

This will allow the SDK to detect cases where a tick is created and deleted in the same transaction and ignore it.

* ci(substreams): ignore built files for uniswapv3 logs only module and clean code

* refactor(substreams): update uniswapv3 substreams with new SDK interface

* feat(subtreams): emit default token balances value for uniswapv3

---------

Co-authored-by: zizou <111426680+flopell@users.noreply.github.com>

---------

Co-authored-by: zizou <111426680+flopell@users.noreply.github.com>
This commit is contained in:
Zizou
2024-11-07 06:16:55 +01:00
committed by GitHub
parent aca3bc4f71
commit 9e8e360889
27 changed files with 9300 additions and 43 deletions

92
substreams/Cargo.lock generated
View File

@@ -347,6 +347,26 @@ dependencies = [
"tycho-substreams", "tycho-substreams",
] ]
[[package]]
name = "ethereum-uniswap-v3-logs-only"
version = "0.1.0"
dependencies = [
"anyhow",
"ethabi 18.0.0",
"getrandom",
"hex",
"hex-literal 0.4.1",
"itertools 0.13.0",
"num-bigint",
"prost 0.11.9",
"substreams",
"substreams-entity-change",
"substreams-ethereum",
"substreams-helper",
"tiny-keccak",
"tycho-substreams",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.0.1" version = "2.0.1"
@@ -660,6 +680,51 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pest"
version = "2.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442"
dependencies = [
"memchr",
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "pest_meta"
version = "2.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d"
dependencies = [
"once_cell",
"pest",
"sha2",
]
[[package]] [[package]]
name = "petgraph" name = "petgraph"
version = "0.6.4" version = "0.6.4"
@@ -968,6 +1033,17 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]] [[package]]
name = "sha3" name = "sha3"
version = "0.10.8" version = "0.10.8"
@@ -986,9 +1062,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "substreams" name = "substreams"
version = "0.5.13" version = "0.5.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3520661f782c338f0e3c6cfc001ac790ed5e68d8f28515139e2aa674f8bb54da" checksum = "92e47e531af83a3cbb78c627ee8232a0ac86604f11c89e34bd4b721ec41e03e5"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bigdecimal", "bigdecimal",
@@ -998,6 +1074,8 @@ dependencies = [
"num-integer", "num-integer",
"num-traits", "num-traits",
"pad", "pad",
"pest",
"pest_derive",
"prost 0.11.9", "prost 0.11.9",
"prost-build", "prost-build",
"prost-types 0.11.9", "prost-types 0.11.9",
@@ -1141,9 +1219,9 @@ dependencies = [
[[package]] [[package]]
name = "substreams-macro" name = "substreams-macro"
version = "0.5.13" version = "0.5.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c15595ceab80fece579e462d4823048fe85d67922584c681f5e94305727ad9ee" checksum = "a4ac77f08d723dace35739d65df8ed122f6d04e2a3e47929831d4021e3339240"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1258,6 +1336,12 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ucd-trie"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
[[package]] [[package]]
name = "uint" name = "uint"
version = "0.9.5" version = "0.9.5"

View File

@@ -9,13 +9,14 @@ members = [
"ethereum-uniswap-v3", "ethereum-uniswap-v3",
"ethereum-sfrax", "ethereum-sfrax",
"ethereum-sfraxeth", "ethereum-sfraxeth",
"ethereum-uniswap-v3-logs-only",
] ]
resolver = "2" resolver = "2"
[workspace.dependencies] [workspace.dependencies]
substreams-ethereum = "0.9.9" substreams-ethereum = "0.9.9"
substreams = "0.5" substreams = "0.5.22"
prost = "0.11" prost = "0.11"
prost-types = "0.12.3" prost-types = "0.12.3"
hex-literal = "0.4.1" hex-literal = "0.4.1"

View File

@@ -59,7 +59,7 @@ pub fn extract_contract_changes<F: Fn(&[u8]) -> bool>(
changed_contracts changed_contracts
.clone() .clone()
.into_values() .into_values()
.map(|change| change.into()), .filter_map(|change| change.into()),
); );
}) })
} }

View File

@@ -1,4 +1,4 @@
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use substreams_ethereum::pb::eth::v2::{self as sf, StorageChange}; use substreams_ethereum::pb::eth::v2::{self as sf, StorageChange};
// re-export the protobuf types here. // re-export the protobuf types here.
@@ -114,6 +114,13 @@ impl TransactionChangesBuilder {
.into_iter() .into_iter()
.map(|a| (a.name.clone(), a)) .map(|a| (a.name.clone(), a))
.collect(), .collect(),
created_attributes: change
.attributes
.clone()
.iter()
.filter(|&attr| (attr.change == i32::from(ChangeType::Creation)))
.map(|attr| attr.name.clone())
.collect(),
}); });
} }
@@ -141,24 +148,17 @@ impl TransactionChangesBuilder {
} }
pub fn build(self) -> Option<TransactionChanges> { pub fn build(self) -> Option<TransactionChanges> {
if self.contract_changes.is_empty() && let tx_changes = TransactionChanges {
self.component_changes.is_empty() &&
self.balance_changes.is_empty() &&
self.entity_changes.is_empty()
{
None
} else {
Some(TransactionChanges {
tx: self.tx, tx: self.tx,
contract_changes: self contract_changes: self
.contract_changes .contract_changes
.into_values() .into_values()
.map(|interim| interim.into()) .filter_map(|interim| interim.into())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
entity_changes: self entity_changes: self
.entity_changes .entity_changes
.into_values() .into_values()
.map(|interim| interim.into()) .filter_map(|interim| interim.into())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
component_changes: self component_changes: self
.component_changes .component_changes
@@ -168,7 +168,11 @@ impl TransactionChangesBuilder {
.balance_changes .balance_changes
.into_values() .into_values()
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
}) };
if tx_changes.is_empty() {
None
} else {
Some(tx_changes)
} }
} }
} }
@@ -389,6 +393,8 @@ impl ProtocolComponent {
pub struct InterimEntityChanges { pub struct InterimEntityChanges {
component_id: String, component_id: String,
attributes: HashMap<String, Attribute>, attributes: HashMap<String, Attribute>,
/// A set of created attributes during this transaction
created_attributes: HashSet<String>,
} }
impl InterimEntityChanges { impl InterimEntityChanges {
@@ -397,26 +403,45 @@ impl InterimEntityChanges {
} }
pub fn set_attribute(&mut self, attr: &Attribute) { pub fn set_attribute(&mut self, attr: &Attribute) {
// Add any attribute creation to the map
if attr.change == i32::from(ChangeType::Creation) {
self.created_attributes
.insert(attr.name.clone());
}
if attr.change == i32::from(ChangeType::Deletion) &&
self.created_attributes
.contains(&attr.name)
{
// If a freshly created attribute is deleted, remove the creation.
self.attributes.remove(&attr.name);
} else {
self.attributes self.attributes
.entry(attr.name.clone()) .entry(attr.name.clone())
.and_modify(|existing| *existing = attr.clone()) .and_modify(|existing| *existing = attr.clone())
.or_insert(attr.clone()); .or_insert(attr.clone());
} }
}
} }
impl From<InterimEntityChanges> for EntityChanges { impl From<InterimEntityChanges> for Option<EntityChanges> {
fn from(value: InterimEntityChanges) -> Self { fn from(value: InterimEntityChanges) -> Self {
EntityChanges { let changes = EntityChanges {
component_id: value.component_id.clone(), component_id: value.component_id.clone(),
attributes: value attributes: value
.attributes .attributes
.into_values() .into_values()
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
};
if changes.attributes.is_empty() {
None
} else {
Some(changes)
} }
} }
} }
#[derive(Clone)] #[derive(Clone, Debug)]
struct SlotValue { struct SlotValue {
new_value: Vec<u8>, new_value: Vec<u8>,
start_value: Vec<u8>, start_value: Vec<u8>,
@@ -435,7 +460,7 @@ impl From<&StorageChange> for SlotValue {
} }
// Uses a map for slots, protobuf does not allow bytes in hashmap keys // Uses a map for slots, protobuf does not allow bytes in hashmap keys
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct InterimContractChange { pub struct InterimContractChange {
address: Vec<u8>, address: Vec<u8>,
balance: Vec<u8>, balance: Vec<u8>,
@@ -492,9 +517,9 @@ impl InterimContractChange {
} }
} }
impl From<InterimContractChange> for ContractChange { impl From<InterimContractChange> for Option<ContractChange> {
fn from(value: InterimContractChange) -> Self { fn from(value: InterimContractChange) -> Self {
ContractChange { let contract_change = ContractChange {
address: value.address, address: value.address,
balance: value.balance, balance: value.balance,
code: value.code, code: value.code,
@@ -505,6 +530,87 @@ impl From<InterimContractChange> for ContractChange {
.map(|(slot, value)| ContractSlot { slot, value: value.new_value }) .map(|(slot, value)| ContractSlot { slot, value: value.new_value })
.collect(), .collect(),
change: value.change.into(), change: value.change.into(),
};
if contract_change.is_empty() {
None
} else {
Some(contract_change)
} }
} }
} }
impl ContractChange {
fn is_empty(&self) -> bool {
self.balance.is_empty() &&
self.slots.is_empty() &&
self.code.is_empty() &&
self.change == i32::from(ChangeType::Update)
}
}
impl TransactionChanges {
fn is_empty(&self) -> bool {
self.contract_changes.is_empty() &&
self.component_changes.is_empty() &&
self.balance_changes.is_empty() &&
self.entity_changes.is_empty()
}
}
#[cfg(test)]
mod test {
use substreams_ethereum::pb::eth::v2::StorageChange;
use crate::models::{Attribute, ChangeType, EntityChanges};
use super::{InterimContractChange, TransactionChangesBuilder};
#[test]
fn test_transaction_changes_builder_ignored_contract_changes() {
let mut builder = TransactionChangesBuilder::new(&super::Transaction::default());
let mut contract_changes = InterimContractChange::new(&[1], false);
contract_changes.upsert_slot(&StorageChange {
address: [1].to_vec(),
key: [0].to_vec(),
old_value: [1].to_vec(),
new_value: [1].to_vec(), //Same old and new value, must be ignored
ordinal: 1,
});
builder.add_contract_changes(&contract_changes);
let tx_changes = builder.build();
assert!(tx_changes.is_none());
}
#[test]
fn test_transaction_changes_builder_ignored_deletion() {
let mut builder = TransactionChangesBuilder::new(&super::Transaction::default());
// Create an attribute
let changes = EntityChanges {
component_id: "component".to_string(),
attributes: vec![Attribute {
name: "attribute".to_string(),
value: vec![1],
change: ChangeType::Creation.into(),
}],
};
builder.add_entity_change(&changes);
// Delete the attribute
let changes = EntityChanges {
component_id: "component".to_string(),
attributes: vec![Attribute {
name: "attribute".to_string(),
value: vec![0],
change: ChangeType::Deletion.into(),
}],
};
builder.add_entity_change(&changes);
let tx_changes = builder.build();
assert!(tx_changes.is_none());
}
}

View File

@@ -0,0 +1,30 @@
[package]
name = "ethereum-uniswap-v3-logs-only"
version = "0.1.0"
edition = "2021"
[lib]
name = "ethereum_uniswap_v3_logs_only"
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
tycho-substreams.workspace = true
num-bigint = "0.4.4"
hex.workspace = true
tiny-keccak = "2.0"
substreams-entity-change = "1.3"
itertools = "0.13.0"
[target.wasm32-unknown-unknown.dependencies]
getrandom = { version = "0.2", features = ["custom"] }
[build-dependencies]
anyhow.workspace = true
substreams-ethereum.workspace = true

View File

@@ -0,0 +1,2 @@
build:
cargo build --target wasm32-unknown-unknown --profile substreams

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,125 @@
specVersion: v0.1.0
package:
name: "ethereum_uniswap_v3_logs_only"
version: v0.1.0
protobuf:
files:
- tycho/evm/v1/entity.proto
- tycho/evm/v1/common.proto
- tycho/evm/v1/utils.proto
- uniswap.proto
importPaths:
- ./proto/v1
- ../../proto/
binaries:
default:
type: wasm/rust-v1
file: ../target/wasm32-unknown-unknown/release/ethereum_uniswap_v3_logs_only.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.BlockEntityChanges
- name: store_pools
kind: store
initialBlock: 12369621
updatePolicy: set_if_not_exists
valueType: proto:uniswap.v3.Pool
inputs:
- map: map_pools_created
- name: map_events
kind: map
initialBlock: 12369621
inputs:
- source: sf.ethereum.type.v2.Block
- store: store_pools
output:
type: proto:uniswap.v3.Events
- name: map_balance_changes
kind: map
initialBlock: 12369621
inputs:
- map: map_events
output:
type: proto:tycho.evm.v1.BlockBalanceDeltas
- name: store_pools_balances
kind: store
initialBlock: 12369621
updatePolicy: add
valueType: bigint
inputs:
- map: map_balance_changes
- name: map_ticks_changes
kind: map
initialBlock: 12369621
inputs:
- map: map_events
output:
type: proto:uniswap.v3.TickDeltas
- name: store_ticks_liquidity
kind: store
initialBlock: 12369621
updatePolicy: add
valueType: bigint
inputs:
- map: map_ticks_changes
- name: store_pool_current_tick
kind: store
initialBlock: 12369621
updatePolicy: set
valueType: int64
inputs:
- map: map_events
- name: map_liquidity_changes
kind: map
initialBlock: 12369621
inputs:
- map: map_events
- store: store_pool_current_tick
output:
type: proto:uniswap.v3.LiquidityChanges
- name: store_liquidity
kind: store
initialBlock: 12369621
updatePolicy: set_sum
valueType: bigint
inputs:
- map: map_liquidity_changes
- name: map_protocol_changes
kind: map
initialBlock: 12369621
inputs:
- source: sf.ethereum.type.v2.Block
- map: map_pools_created
- map: map_events
- map: map_balance_changes
- store: store_pools_balances
mode: deltas
- map: map_ticks_changes
- store: store_ticks_liquidity
mode: deltas
- map: map_liquidity_changes
- store: store_liquidity
mode: deltas
output:
type: proto:tycho.evm.v1.BlockChanges
params:
map_pools_created: "1F98431c8aD98523631AE4a59f267346ea31F984"

View File

@@ -0,0 +1,177 @@
syntax = "proto3";
package uniswap.v3;
message Pool {
bytes address = 1;
bytes token0 = 2;
bytes token1 = 3;
bytes created_tx_hash = 4;
}
// A struct describing a transaction.
message Transaction {
// The transaction hash.
bytes hash = 1;
// The sender of the transaction.
bytes from = 2;
// The receiver of the transaction.
bytes to = 3;
// The transactions index within the block.
uint64 index = 4;
}
// A change to a pool's tick.
message TickDelta {
// The address of the pool.
bytes pool_address = 1;
// The index of the tick.
int32 tick_index = 2;
// The liquidity net delta of this tick. Bigint encoded as signed little endian bytes.
bytes liquidity_net_delta = 3;
// Used to determine the order of the balance changes. Necessary for the balance store.
uint64 ordinal = 4;
Transaction transaction = 5;
}
// A group of TickDelta
message TickDeltas {
repeated TickDelta deltas = 1;
}
// A change to a pool's liquidity.
message LiquidityChange {
// The address of the pool.
bytes pool_address = 1;
// The liquidity changed amount. Bigint encoded as signed little endian bytes.
bytes value = 2;
// The type of update, can be absolute or delta.
LiquidityChangeType change_type = 3;
// Used to determine the order of the balance changes. Necessary for the balance store.
uint64 ordinal = 4;
Transaction transaction = 5;
}
// A group of LiquidityChange
message LiquidityChanges {
repeated LiquidityChange changes = 1;
}
enum LiquidityChangeType {
DELTA = 0;
ABSOLUTE = 1;
}
message Events {
repeated PoolEvent pool_events = 3;
message PoolEvent {
oneof type {
Initialize initialize = 1;
Mint mint = 2;
Collect collect = 3;
Burn burn = 4;
Swap swap = 5;
Flash flash = 6;
SetFeeProtocol set_fee_protocol = 7;
CollectProtocol collect_protocol = 8;
}
uint64 log_ordinal = 100;
string pool_address = 102;
string token0 = 103;
string token1 = 104;
Transaction transaction = 105;
message Initialize {
// Unsigned
string sqrt_price = 1;
int32 tick = 2;
}
message Mint {
string sender = 1;
string owner = 2;
// Signed
int32 tick_lower = 3;
// Signed
int32 tick_upper = 4;
// Unsigned
string amount = 5;
// Unsigned
string amount_0 = 6;
// Unsigned
string amount_1 = 7;
}
message Collect {
string owner = 1;
string recipient = 2;
int32 tick_lower = 3;
int32 tick_upper = 4;
// Unsigned
string amount_0 = 5;
// Unsigned
string amount_1 = 6;
}
message Burn {
string owner = 1;
int32 tick_lower = 2;
int32 tick_upper = 3;
// Unsigned
string amount = 4;
// Unsigned
string amount_0 = 5;
// Unsigned
string amount_1 = 6;
}
message Swap {
string sender = 1;
string recipient = 2;
// Signed
string amount_0 = 3;
// Signed
string amount_1 = 4;
// Unsigned
string sqrt_price = 6;
// Unsigned
string liquidity = 7;
int32 tick = 8;
}
message Flash {
string sender = 1;
string recipient = 2;
// Unsigned
string amount_0 = 3;
// Unsigned
string amount_1 = 4;
// Unsigned
string paid_0 = 5;
// Unsigned
string paid_1 = 6;
}
message SetFeeProtocol {
// Unsigned
uint64 fee_protocol_0_old = 1;
// Unsigned
uint64 fee_protocol_1_old = 2;
// Unsigned
uint64 fee_protocol_0_new = 3;
// Unsigned
uint64 fee_protocol_1_new = 4;
}
message CollectProtocol {
string sender = 1;
string recipient = 2;
// Unsigned
string amount_0 = 3;
// Unsigned
string amount_1 = 4;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
#![allow(clippy::all, clippy::pedantic, clippy::nursery)]
pub mod factory;
pub mod pool;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
#![allow(clippy::not_unsafe_ptr_arg_deref)]
mod abi;
mod modules;
mod pb;
pub use modules::*;

View File

@@ -0,0 +1,114 @@
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;
use tycho_substreams::prelude::*;
#[substreams::handlers::map]
pub fn map_pools_created(
params: String,
block: eth::Block,
) -> Result<BlockEntityChanges, substreams::errors::Error> {
let mut new_pools: Vec<TransactionEntityChanges> = vec![];
let factory_address = params.as_str();
get_new_pools(&block, &mut new_pools, factory_address);
Ok(BlockEntityChanges { block: None, changes: new_pools })
}
// Extract new pools from PoolCreated events
fn get_new_pools(
block: &eth::Block,
new_pools: &mut Vec<TransactionEntityChanges>,
factory_address: &str,
) {
// Extract new pools from PoolCreated events
let mut on_pool_created = |event: PoolCreated, _tx: &eth::TransactionTrace, _log: &eth::Log| {
let tycho_tx: Transaction = _tx.into();
new_pools.push(TransactionEntityChanges {
tx: Some(tycho_tx.clone()),
entity_changes: vec![EntityChanges {
component_id: event.pool.clone().to_hex(),
attributes: vec![
Attribute {
name: "liquidity".to_string(),
value: BigInt::from(0).to_signed_bytes_be(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "tick".to_string(),
value: BigInt::from(0).to_signed_bytes_be(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "sqrt_price_x96".to_string(),
value: BigInt::from(0).to_signed_bytes_be(),
change: ChangeType::Creation.into(),
},
],
}],
component_changes: vec![ProtocolComponent {
id: event.pool.to_hex(),
tokens: vec![event.token0.clone(), event.token1.clone()],
contracts: vec![],
static_att: vec![
Attribute {
name: "fee".to_string(),
value: event.fee.to_signed_bytes_be(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "tick_spacing".to_string(),
value: event.tick_spacing.to_signed_bytes_be(),
change: ChangeType::Creation.into(),
},
Attribute {
name: "pool_address".to_string(),
value: event.pool.clone(),
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![
BalanceChange {
token: event.token0,
balance: BigInt::from(0).to_signed_bytes_be(),
component_id: event
.pool
.clone()
.to_hex()
.as_bytes()
.to_vec(),
},
BalanceChange {
token: event.token1,
balance: BigInt::from(0).to_signed_bytes_be(),
component_id: event.pool.to_hex().as_bytes().to_vec(),
},
],
})
};
let mut eh = EventHandler::new(block);
eh.filter_by_address(vec![Address::from_str(factory_address).unwrap()]);
eh.on::<PoolCreated, _>(&mut on_pool_created);
eh.handle_events();
}

View File

@@ -0,0 +1,24 @@
use std::str;
use substreams::store::{StoreNew, StoreSetIfNotExists, StoreSetIfNotExistsProto};
use tycho_substreams::models::BlockEntityChanges;
use crate::pb::uniswap::v3::Pool;
#[substreams::handlers::store]
pub fn store_pools(pools_created: BlockEntityChanges, store: StoreSetIfNotExistsProto<Pool>) {
// 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);
}
}
}

View File

@@ -0,0 +1,183 @@
use anyhow::Ok;
use substreams::{
store::{StoreGet, StoreGetProto},
Hex,
};
use substreams_ethereum::{
pb::eth::v2::{self as eth, Log, TransactionTrace},
Event,
};
use substreams_helper::hex::Hexable;
use crate::{
abi::pool::events::{
Burn, Collect, CollectProtocol, Flash, Initialize, Mint, SetFeeProtocol, Swap,
},
pb::uniswap::v3::{
events::{
pool_event::{self, Type},
PoolEvent,
},
Events, Pool,
},
};
#[substreams::handlers::map]
pub fn map_events(
block: eth::Block,
pools_store: StoreGetProto<Pool>,
) -> Result<Events, anyhow::Error> {
let mut pool_events = block
.transaction_traces
.into_iter()
.filter(|tx| tx.status == 1)
.flat_map(|tx| {
tx.clone()
.receipt
.into_iter()
.flat_map(|receipt| receipt.logs)
.filter_map(|log| {
let key = format!("{}:{}", "Pool", log.address.to_hex());
// Skip if the log is not from a known uniswapV3 pool.
if let Some(pool) = pools_store.get_last(key) {
log_to_event(&log, pool, tx.clone())
} else {
None
}
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
pool_events.sort_unstable_by_key(|e| e.log_ordinal);
Ok(Events { pool_events })
}
fn log_to_event(event: &Log, pool: Pool, tx: TransactionTrace) -> Option<PoolEvent> {
if let Some(init) = Initialize::match_and_decode(event) {
Some(PoolEvent {
log_ordinal: event.ordinal,
pool_address: Hex(pool.address).to_string(),
token0: Hex(pool.token0).to_string(),
token1: Hex(pool.token1).to_string(),
transaction: Some(tx.into()),
r#type: Some(Type::Initialize(pool_event::Initialize {
sqrt_price: init.sqrt_price_x96.to_string(),
tick: init.tick.into(),
})),
})
} else if let Some(swap) = Swap::match_and_decode(event) {
Some(PoolEvent {
log_ordinal: event.ordinal,
pool_address: Hex(pool.address).to_string(),
token0: Hex(pool.token0).to_string(),
token1: Hex(pool.token1).to_string(),
transaction: Some(tx.into()),
r#type: Some(Type::Swap(pool_event::Swap {
sender: Hex(swap.sender).to_string(),
recipient: Hex(swap.recipient).to_string(),
amount_0: swap.amount0.to_string(),
amount_1: swap.amount1.to_string(),
sqrt_price: swap.sqrt_price_x96.to_string(),
liquidity: swap.liquidity.to_string(),
tick: swap.tick.into(),
})),
})
} else if let Some(flash) = Flash::match_and_decode(event) {
Some(PoolEvent {
log_ordinal: event.ordinal,
pool_address: Hex(pool.address).to_string(),
token0: Hex(pool.token0).to_string(),
token1: Hex(pool.token1).to_string(),
transaction: Some(tx.into()),
r#type: Some(Type::Flash(pool_event::Flash {
sender: Hex(flash.sender).to_string(),
recipient: Hex(flash.recipient).to_string(),
amount_0: flash.amount0.to_string(),
amount_1: flash.amount1.to_string(),
paid_0: flash.paid0.to_string(),
paid_1: flash.paid1.to_string(),
})),
})
} else if let Some(mint) = Mint::match_and_decode(event) {
Some(PoolEvent {
log_ordinal: event.ordinal,
pool_address: Hex(pool.address).to_string(),
token0: Hex(pool.token0).to_string(),
token1: Hex(pool.token1).to_string(),
transaction: Some(tx.into()),
r#type: Some(Type::Mint(pool_event::Mint {
sender: Hex(mint.sender).to_string(),
owner: Hex(mint.owner).to_string(),
tick_lower: mint.tick_lower.into(),
tick_upper: mint.tick_upper.into(),
amount: mint.amount.to_string(),
amount_0: mint.amount0.to_string(),
amount_1: mint.amount1.to_string(),
})),
})
} else if let Some(burn) = Burn::match_and_decode(event) {
Some(PoolEvent {
log_ordinal: event.ordinal,
pool_address: Hex(pool.address).to_string(),
token0: Hex(pool.token0).to_string(),
token1: Hex(pool.token1).to_string(),
transaction: Some(tx.into()),
r#type: Some(Type::Burn(pool_event::Burn {
owner: Hex(burn.owner).to_string(),
tick_lower: burn.tick_lower.into(),
tick_upper: burn.tick_upper.into(),
amount: burn.amount.to_string(),
amount_0: burn.amount0.to_string(),
amount_1: burn.amount1.to_string(),
})),
})
} else if let Some(collect) = Collect::match_and_decode(event) {
Some(PoolEvent {
log_ordinal: event.ordinal,
pool_address: Hex(pool.address).to_string(),
token0: Hex(pool.token0).to_string(),
token1: Hex(pool.token1).to_string(),
transaction: Some(tx.into()),
r#type: Some(Type::Collect(pool_event::Collect {
owner: Hex(collect.owner).to_string(),
recipient: Hex(collect.recipient).to_string(),
tick_lower: collect.tick_lower.into(),
tick_upper: collect.tick_upper.into(),
amount_0: collect.amount0.to_string(),
amount_1: collect.amount1.to_string(),
})),
})
} else if let Some(set_fp) = SetFeeProtocol::match_and_decode(event) {
Some(PoolEvent {
log_ordinal: event.ordinal,
pool_address: Hex(pool.address).to_string(),
token0: Hex(pool.token0).to_string(),
token1: Hex(pool.token1).to_string(),
transaction: Some(tx.into()),
r#type: Some(Type::SetFeeProtocol(pool_event::SetFeeProtocol {
fee_protocol_0_old: set_fp.fee_protocol0_old.to_u64(),
fee_protocol_1_old: set_fp.fee_protocol1_old.to_u64(),
fee_protocol_0_new: set_fp.fee_protocol0_new.to_u64(),
fee_protocol_1_new: set_fp.fee_protocol1_new.to_u64(),
})),
})
} else if let Some(cp) = CollectProtocol::match_and_decode(event) {
Some(PoolEvent {
log_ordinal: event.ordinal,
pool_address: Hex(pool.address).to_string(),
token0: Hex(pool.token0).to_string(),
token1: Hex(pool.token1).to_string(),
transaction: Some(tx.into()),
r#type: Some(Type::CollectProtocol(pool_event::CollectProtocol {
sender: Hex(cp.sender).to_string(),
recipient: Hex(cp.recipient).to_string(),
amount_0: cp.amount0.to_string(),
amount_1: cp.amount1.to_string(),
})),
})
} else {
None
}
}

View File

@@ -0,0 +1,175 @@
use std::str::FromStr;
use anyhow::Ok;
use tycho_substreams::models::{BalanceDelta, BlockBalanceDeltas};
use crate::pb::uniswap::v3::{
events::{pool_event, PoolEvent},
Events,
};
use substreams::{
scalar::BigInt,
store::{StoreAddBigInt, StoreNew},
};
#[substreams::handlers::map]
pub fn map_balance_changes(events: Events) -> Result<BlockBalanceDeltas, anyhow::Error> {
let balance_deltas = events
.pool_events
.into_iter()
.flat_map(event_to_balance_deltas)
.collect();
Ok(BlockBalanceDeltas { balance_deltas })
}
#[substreams::handlers::store]
pub fn store_pools_balances(balances_deltas: BlockBalanceDeltas, store: StoreAddBigInt) {
tycho_substreams::balances::store_balance_changes(balances_deltas, store);
}
fn event_to_balance_deltas(event: PoolEvent) -> Vec<BalanceDelta> {
let address = format!("0x{}", event.pool_address)
.as_bytes()
.to_vec();
match event.r#type.unwrap() {
pool_event::Type::Mint(e) => vec![
BalanceDelta {
token: hex::decode(event.token0.clone()).unwrap(),
delta: BigInt::from_str(&e.amount_0)
.unwrap()
.to_signed_bytes_be(),
component_id: address.clone(),
ord: event.log_ordinal,
tx: event
.transaction
.clone()
.map(Into::into),
},
BalanceDelta {
token: hex::decode(event.token1.clone()).unwrap(),
delta: BigInt::from_str(&e.amount_1)
.unwrap()
.to_signed_bytes_be(),
component_id: address,
ord: event.log_ordinal,
tx: event.transaction.map(Into::into),
},
],
pool_event::Type::Collect(e) => vec![
BalanceDelta {
token: hex::decode(event.token0.clone()).unwrap(),
delta: BigInt::from_str(&e.amount_0)
.unwrap()
.neg()
.to_signed_bytes_be(),
component_id: address.clone(),
ord: event.log_ordinal,
tx: event
.transaction
.clone()
.map(Into::into),
},
BalanceDelta {
token: hex::decode(event.token1.clone()).unwrap(),
delta: BigInt::from_str(&e.amount_1)
.unwrap()
.neg()
.to_signed_bytes_be(),
component_id: address,
ord: event.log_ordinal,
tx: event.transaction.map(Into::into),
},
],
//Burn balance changes are accounted for in the Collect event.
pool_event::Type::Burn(_) => vec![],
pool_event::Type::Swap(e) => {
vec![
BalanceDelta {
token: hex::decode(event.token0.clone()).unwrap(),
delta: BigInt::from_str(&e.amount_0)
.unwrap()
.to_signed_bytes_be(),
component_id: address.clone(),
ord: event.log_ordinal,
tx: event
.transaction
.clone()
.map(Into::into),
},
BalanceDelta {
token: hex::decode(event.token1.clone()).unwrap(),
delta: BigInt::from_str(&e.amount_1)
.unwrap()
.to_signed_bytes_be(),
component_id: address.clone(),
ord: event.log_ordinal,
tx: event.transaction.map(Into::into),
},
]
}
pool_event::Type::Flash(e) => vec![
BalanceDelta {
token: hex::decode(event.token0).unwrap(),
delta: BigInt::from_str(&e.paid_0)
.unwrap()
.clone()
.to_signed_bytes_be(),
component_id: address.clone(),
ord: event.log_ordinal,
tx: event
.transaction
.clone()
.map(Into::into),
},
BalanceDelta {
token: hex::decode(event.token1).unwrap(),
delta: BigInt::from_str(&e.paid_1)
.unwrap()
.clone()
.to_signed_bytes_be(),
component_id: address,
ord: event.log_ordinal,
tx: event.transaction.map(Into::into),
},
],
pool_event::Type::CollectProtocol(e) => {
vec![
BalanceDelta {
token: hex::decode(event.token0).unwrap(),
delta: BigInt::from_str(&e.amount_0)
.unwrap()
.neg()
.clone()
.to_signed_bytes_be(),
component_id: event
.pool_address
.clone()
.as_bytes()
.to_vec(),
ord: event.log_ordinal,
tx: event
.transaction
.clone()
.map(Into::into),
},
BalanceDelta {
token: hex::decode(event.token1).unwrap(),
delta: BigInt::from_str(&e.amount_1)
.unwrap()
.clone()
.neg()
.to_signed_bytes_be(),
component_id: event
.pool_address
.clone()
.as_bytes()
.to_vec(),
ord: event.log_ordinal,
tx: event.transaction.map(Into::into),
},
]
}
_ => vec![],
}
}

View File

@@ -0,0 +1,137 @@
use std::str::FromStr;
use substreams::store::{
StoreGet, StoreGetInt64, StoreSet, StoreSetInt64, StoreSetSum, StoreSetSumBigInt,
};
use crate::pb::uniswap::v3::{
events::{pool_event, PoolEvent},
Events, LiquidityChange, LiquidityChangeType, LiquidityChanges,
};
use substreams::{scalar::BigInt, store::StoreNew};
use anyhow::Ok;
#[substreams::handlers::store]
pub fn store_pool_current_tick(events: Events, store: StoreSetInt64) {
events
.pool_events
.into_iter()
.filter_map(event_to_current_tick)
.for_each(|(pool, ordinal, new_tick_index)| {
store.set(ordinal, format!("pool:{0}", pool), &new_tick_index.into())
});
}
#[substreams::handlers::map]
pub fn map_liquidity_changes(
events: Events,
pools_current_tick_store: StoreGetInt64,
) -> Result<LiquidityChanges, anyhow::Error> {
let mut changes = events
.pool_events
.into_iter()
.filter(PoolEvent::can_introduce_liquidity_changes)
.map(|e| {
(
pools_current_tick_store
.get_at(e.log_ordinal, format!("pool:{0}", &e.pool_address))
.unwrap_or(0),
e,
)
})
.filter_map(|(current_tick, event)| event_to_liquidity_deltas(current_tick, event))
.collect::<Vec<_>>();
changes.sort_unstable_by_key(|l| l.ordinal);
Ok(LiquidityChanges { changes })
}
#[substreams::handlers::store]
pub fn store_liquidity(ticks_deltas: LiquidityChanges, store: StoreSetSumBigInt) {
ticks_deltas
.changes
.iter()
.for_each(|changes| match changes.change_type() {
LiquidityChangeType::Delta => {
store.sum(
changes.ordinal,
format!("pool:{0}", hex::encode(&changes.pool_address)),
BigInt::from_signed_bytes_be(&changes.value),
);
}
LiquidityChangeType::Absolute => {
store.set(
changes.ordinal,
format!("pool:{0}", hex::encode(&changes.pool_address)),
BigInt::from_signed_bytes_be(&changes.value),
);
}
});
}
fn event_to_liquidity_deltas(current_tick: i64, event: PoolEvent) -> Option<LiquidityChange> {
match event.r#type.as_ref().unwrap() {
pool_event::Type::Mint(mint) => {
if current_tick >= mint.tick_lower.into() && current_tick < mint.tick_upper.into() {
Some(LiquidityChange {
pool_address: hex::decode(event.pool_address).unwrap(),
value: BigInt::from_str(&mint.amount)
.unwrap()
.to_signed_bytes_be(),
change_type: LiquidityChangeType::Delta.into(),
ordinal: event.log_ordinal,
transaction: Some(event.transaction.unwrap()),
})
} else {
None
}
}
pool_event::Type::Burn(burn) => {
if current_tick >= burn.tick_lower.into() && current_tick < burn.tick_upper.into() {
Some(LiquidityChange {
pool_address: hex::decode(event.pool_address).unwrap(),
value: BigInt::from_str(&burn.amount)
.unwrap()
.neg()
.to_signed_bytes_be(),
change_type: LiquidityChangeType::Delta.into(),
ordinal: event.log_ordinal,
transaction: Some(event.transaction.unwrap()),
})
} else {
None
}
}
pool_event::Type::Swap(swap) => Some(LiquidityChange {
pool_address: hex::decode(event.pool_address).unwrap(),
value: BigInt::from_str(&swap.liquidity)
.unwrap()
.to_signed_bytes_be(),
change_type: LiquidityChangeType::Absolute.into(),
ordinal: event.log_ordinal,
transaction: Some(event.transaction.unwrap()),
}),
_ => None,
}
}
impl PoolEvent {
fn can_introduce_liquidity_changes(&self) -> bool {
matches!(
self.r#type.as_ref().unwrap(),
pool_event::Type::Mint(_) | pool_event::Type::Burn(_) | pool_event::Type::Swap(_)
)
}
}
fn event_to_current_tick(event: PoolEvent) -> Option<(String, u64, i32)> {
match event.r#type.as_ref().unwrap() {
pool_event::Type::Initialize(initialize) => {
Some((event.pool_address, event.log_ordinal, initialize.tick))
}
pool_event::Type::Swap(swap) => Some((event.pool_address, event.log_ordinal, swap.tick)),
_ => None,
}
}

View File

@@ -0,0 +1,91 @@
use std::str::FromStr;
use substreams::store::StoreAddBigInt;
use crate::pb::uniswap::v3::{
events::{pool_event, PoolEvent},
Events, TickDelta, TickDeltas,
};
use substreams::{
scalar::BigInt,
store::{StoreAdd, StoreNew},
};
use anyhow::Ok;
#[substreams::handlers::map]
pub fn map_ticks_changes(events: Events) -> Result<TickDeltas, anyhow::Error> {
let ticks_deltas = events
.pool_events
.into_iter()
.flat_map(event_to_ticks_deltas)
.collect();
Ok(TickDeltas { deltas: ticks_deltas })
}
#[substreams::handlers::store]
pub fn store_ticks_liquidity(ticks_deltas: TickDeltas, store: StoreAddBigInt) {
let mut deltas = ticks_deltas.deltas.clone();
deltas.sort_unstable_by_key(|delta| delta.ordinal);
deltas.iter().for_each(|delta| {
store.add(
delta.ordinal,
format!("pool:{0}:tick:{1}", hex::encode(&delta.pool_address), delta.tick_index,),
BigInt::from_signed_bytes_be(&delta.liquidity_net_delta),
);
});
}
fn event_to_ticks_deltas(event: PoolEvent) -> Vec<TickDelta> {
match event.r#type.as_ref().unwrap() {
pool_event::Type::Mint(mint) => {
vec![
TickDelta {
pool_address: hex::decode(&event.pool_address).unwrap(),
tick_index: mint.tick_lower,
liquidity_net_delta: BigInt::from_str(&mint.amount)
.unwrap()
.to_signed_bytes_be(),
ordinal: event.log_ordinal,
transaction: event.transaction.clone(),
},
TickDelta {
pool_address: hex::decode(&event.pool_address).unwrap(),
tick_index: mint.tick_upper,
liquidity_net_delta: BigInt::from_str(&mint.amount)
.unwrap()
.neg()
.to_signed_bytes_be(),
ordinal: event.log_ordinal,
transaction: event.transaction,
},
]
}
pool_event::Type::Burn(burn) => vec![
TickDelta {
pool_address: hex::decode(&event.pool_address).unwrap(),
tick_index: burn.tick_lower,
liquidity_net_delta: BigInt::from_str(&burn.amount)
.unwrap()
.neg()
.to_signed_bytes_be(),
ordinal: event.log_ordinal,
transaction: event.transaction.clone(),
},
TickDelta {
pool_address: hex::decode(&event.pool_address).unwrap(),
tick_index: burn.tick_upper,
liquidity_net_delta: BigInt::from_str(&burn.amount)
.unwrap()
.to_signed_bytes_be(),
ordinal: event.log_ordinal,
transaction: event.transaction,
},
],
_ => vec![],
}
}

View File

@@ -0,0 +1,248 @@
use crate::pb::uniswap::v3::{
events::{pool_event, PoolEvent},
Events, LiquidityChanges, TickDeltas,
};
use itertools::Itertools;
use std::{collections::HashMap, str::FromStr, vec};
use substreams::{pb::substreams::StoreDeltas, scalar::BigInt};
use substreams_ethereum::pb::eth::v2::{self as eth};
use substreams_helper::hex::Hexable;
use tycho_substreams::{balances::aggregate_balances_changes, prelude::*};
type PoolAddress = Vec<u8>;
#[substreams::handlers::map]
pub fn map_protocol_changes(
block: eth::Block,
created_pools: BlockEntityChanges,
events: Events,
balances_map_deltas: BlockBalanceDeltas,
balances_store_deltas: StoreDeltas,
ticks_map_deltas: TickDeltas,
ticks_store_deltas: StoreDeltas,
pool_liquidity_changes: LiquidityChanges,
pool_liquidity_store_deltas: StoreDeltas,
) -> Result<BlockChanges, substreams::errors::Error> {
// We merge contract changes by transaction (identified by transaction index) making it easy to
// sort them at the very end.
let mut transaction_changes: HashMap<_, TransactionChangesBuilder> = HashMap::new();
// Add created pools to the tx_changes_map
for change in created_pools.changes.into_iter() {
let tx = change.tx.as_ref().unwrap();
let builder = transaction_changes
.entry(tx.index)
.or_insert_with(|| TransactionChangesBuilder::new(tx));
change
.component_changes
.iter()
.for_each(|c| {
builder.add_protocol_component(c);
});
change
.entity_changes
.iter()
.for_each(|ec| {
builder.add_entity_change(ec);
});
change
.balance_changes
.iter()
.for_each(|bc| {
builder.add_balance_change(bc);
});
}
// Balance changes are gathered by the `StoreDelta` based on `PoolBalanceChanged` creating
// `BlockBalanceDeltas`. We essentially just process the changes that occurred to the `store`
// this block. Then, these balance changes are merged onto the existing map of tx contract
// changes, inserting a new one if it doesn't exist.
aggregate_balances_changes(balances_store_deltas, balances_map_deltas)
.into_iter()
.for_each(|(_, (tx, balances))| {
let builder = transaction_changes
.entry(tx.index)
.or_insert_with(|| TransactionChangesBuilder::new(&tx));
balances
.values()
.for_each(|token_bc_map| {
token_bc_map
.values()
.for_each(|bc| builder.add_balance_change(bc))
});
});
// Insert ticks net-liquidity changes
ticks_store_deltas
.deltas
.into_iter()
.zip(ticks_map_deltas.deltas)
.for_each(|(store_delta, tick_delta)| {
let new_value_bigint =
BigInt::from_str(&String::from_utf8(store_delta.new_value).unwrap()).unwrap();
// If old value is empty or the int value is 0, it's considered as a creation.
let is_creation = store_delta.old_value.is_empty() ||
BigInt::from_str(&String::from_utf8(store_delta.old_value).unwrap())
.unwrap()
.is_zero();
let attribute_name = format!("ticks/{}/net-liquidity", tick_delta.tick_index);
let attribute = Attribute {
name: attribute_name,
value: new_value_bigint.to_signed_bytes_be(),
change: if is_creation {
ChangeType::Creation.into()
} else if new_value_bigint.is_zero() {
ChangeType::Deletion.into()
} else {
ChangeType::Update.into()
},
};
let tx = tick_delta.transaction.unwrap();
let builder = transaction_changes
.entry(tx.index)
.or_insert_with(|| TransactionChangesBuilder::new(&tx.into()));
builder.add_entity_change(&EntityChanges {
component_id: tick_delta.pool_address.to_hex(),
attributes: vec![attribute],
});
});
// Insert liquidity changes
pool_liquidity_store_deltas
.deltas
.into_iter()
.zip(pool_liquidity_changes.changes)
.for_each(|(store_delta, change)| {
let new_value_bigint = BigInt::from_str(
String::from_utf8(store_delta.new_value)
.unwrap()
.split(':')
.nth(1)
.unwrap(),
)
.unwrap();
let tx = change.transaction.unwrap();
let builder = transaction_changes
.entry(tx.index)
.or_insert_with(|| TransactionChangesBuilder::new(&tx.into()));
builder.add_entity_change(&EntityChanges {
component_id: change.pool_address.to_hex(),
attributes: vec![Attribute {
name: "liquidity".to_string(),
value: new_value_bigint.to_signed_bytes_be(),
change: ChangeType::Update.into(),
}],
});
});
// Insert others changes
events
.pool_events
.into_iter()
.flat_map(event_to_attributes_updates)
.for_each(|(tx, pool_address, attr)| {
let builder = transaction_changes
.entry(tx.index)
.or_insert_with(|| TransactionChangesBuilder::new(&tx));
builder.add_entity_change(&EntityChanges {
component_id: pool_address.to_hex(),
attributes: vec![attr],
});
});
Ok(BlockChanges {
block: Some((&block).into()),
changes: transaction_changes
.drain()
.sorted_unstable_by_key(|(index, _)| *index)
.filter_map(|(_, builder)| builder.build())
.collect::<Vec<_>>(),
})
}
fn event_to_attributes_updates(event: PoolEvent) -> Vec<(Transaction, PoolAddress, Attribute)> {
match event.r#type.as_ref().unwrap() {
pool_event::Type::Initialize(initalize) => {
vec![
(
event
.transaction
.clone()
.unwrap()
.into(),
hex::decode(event.pool_address.clone()).unwrap(),
Attribute {
name: "sqrt_price_x96".to_string(),
value: BigInt::from_str(&initalize.sqrt_price)
.unwrap()
.to_signed_bytes_be(),
change: ChangeType::Update.into(),
},
),
(
event.transaction.unwrap().into(),
hex::decode(event.pool_address).unwrap(),
Attribute {
name: "tick".to_string(),
value: BigInt::from(initalize.tick).to_signed_bytes_be(),
change: ChangeType::Update.into(),
},
),
]
}
pool_event::Type::Swap(swap) => vec![
(
event
.transaction
.clone()
.unwrap()
.into(),
hex::decode(event.pool_address.clone()).unwrap(),
Attribute {
name: "sqrt_price_x96".to_string(),
value: BigInt::from_str(&swap.sqrt_price)
.unwrap()
.to_signed_bytes_be(),
change: ChangeType::Update.into(),
},
),
(
event.transaction.unwrap().into(),
hex::decode(event.pool_address).unwrap(),
Attribute {
name: "tick".to_string(),
value: BigInt::from(swap.tick).to_signed_bytes_be(),
change: ChangeType::Update.into(),
},
),
],
pool_event::Type::SetFeeProtocol(sfp) => vec![
(
event
.transaction
.clone()
.unwrap()
.into(),
hex::decode(event.pool_address.clone()).unwrap(),
Attribute {
name: "protocol_fees/token0".to_string(),
value: BigInt::from(sfp.fee_protocol_0_new).to_signed_bytes_be(),
change: ChangeType::Update.into(),
},
),
(
event.transaction.unwrap().into(),
hex::decode(event.pool_address).unwrap(),
Attribute {
name: "protocol_fees/token1".to_string(),
value: BigInt::from(sfp.fee_protocol_1_new).to_signed_bytes_be(),
change: ChangeType::Update.into(),
},
),
],
_ => vec![],
}
}

View File

@@ -0,0 +1,39 @@
pub use map_pool_created::map_pools_created;
pub use map_protocol_changes::map_protocol_changes;
pub use store_pools::store_pools;
use substreams_ethereum::pb::eth::v2::TransactionTrace;
use crate::pb::uniswap::v3::Transaction;
#[path = "1_map_pool_created.rs"]
mod map_pool_created;
#[path = "2_store_pools.rs"]
mod store_pools;
#[path = "3_map_events.rs"]
mod map_events;
#[path = "4_map_and_store_balance_changes.rs"]
mod map_store_balance_changes;
#[path = "4_map_and_store_ticks.rs"]
mod map_store_ticks;
#[path = "4_map_and_store_liquidity.rs"]
mod map_store_liquidity;
#[path = "5_map_protocol_changes.rs"]
mod map_protocol_changes;
impl From<TransactionTrace> for Transaction {
fn from(value: TransactionTrace) -> Self {
Self { hash: value.hash, from: value.from, to: value.to, index: value.index.into() }
}
}
impl From<Transaction> for tycho_substreams::prelude::Transaction {
fn from(value: Transaction) -> Self {
Self { hash: value.hash, from: value.from, to: value.to, index: value.index }
}
}

View File

@@ -0,0 +1,8 @@
// @generated
pub mod uniswap {
// @@protoc_insertion_point(attribute:uniswap.v3)
pub mod v3 {
include!("uniswap.v3.rs");
// @@protoc_insertion_point(uniswap.v3)
}
}

View File

@@ -0,0 +1,298 @@
// @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<u8>,
#[prost(bytes="vec", tag="2")]
pub token0: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes="vec", tag="3")]
pub token1: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes="vec", tag="4")]
pub created_tx_hash: ::prost::alloc::vec::Vec<u8>,
}
/// 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<u8>,
/// The sender of the transaction.
#[prost(bytes="vec", tag="2")]
pub from: ::prost::alloc::vec::Vec<u8>,
/// The receiver of the transaction.
#[prost(bytes="vec", tag="3")]
pub to: ::prost::alloc::vec::Vec<u8>,
/// The transactions index within the block.
#[prost(uint64, tag="4")]
pub index: u64,
}
/// A change to a pool's tick.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TickDelta {
/// The address of the pool.
#[prost(bytes="vec", tag="1")]
pub pool_address: ::prost::alloc::vec::Vec<u8>,
/// The index of the tick.
#[prost(int32, tag="2")]
pub tick_index: i32,
/// The liquidity net delta of this tick. Bigint encoded as signed little endian bytes.
#[prost(bytes="vec", tag="3")]
pub liquidity_net_delta: ::prost::alloc::vec::Vec<u8>,
/// Used to determine the order of the balance changes. Necessary for the balance store.
#[prost(uint64, tag="4")]
pub ordinal: u64,
#[prost(message, optional, tag="5")]
pub transaction: ::core::option::Option<Transaction>,
}
/// A group of TickDelta
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TickDeltas {
#[prost(message, repeated, tag="1")]
pub deltas: ::prost::alloc::vec::Vec<TickDelta>,
}
/// A change to a pool's liquidity.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct LiquidityChange {
/// The address of the pool.
#[prost(bytes="vec", tag="1")]
pub pool_address: ::prost::alloc::vec::Vec<u8>,
/// The liquidity changed amount. Bigint encoded as signed little endian bytes.
#[prost(bytes="vec", tag="2")]
pub value: ::prost::alloc::vec::Vec<u8>,
/// The type of update, can be absolute or delta.
#[prost(enumeration="LiquidityChangeType", tag="3")]
pub change_type: i32,
/// Used to determine the order of the balance changes. Necessary for the balance store.
#[prost(uint64, tag="4")]
pub ordinal: u64,
#[prost(message, optional, tag="5")]
pub transaction: ::core::option::Option<Transaction>,
}
/// A group of LiquidityChange
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct LiquidityChanges {
#[prost(message, repeated, tag="1")]
pub changes: ::prost::alloc::vec::Vec<LiquidityChange>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Events {
#[prost(message, repeated, tag="3")]
pub pool_events: ::prost::alloc::vec::Vec<events::PoolEvent>,
}
/// Nested message and enum types in `Events`.
pub mod events {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct PoolEvent {
#[prost(uint64, tag="100")]
pub log_ordinal: 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(message, optional, tag="105")]
pub transaction: ::core::option::Option<super::Transaction>,
#[prost(oneof="pool_event::Type", tags="1, 2, 3, 4, 5, 6, 7, 8")]
pub r#type: ::core::option::Option<pool_event::Type>,
}
/// 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 Initialize {
/// Unsigned
#[prost(string, tag="1")]
pub sqrt_price: ::prost::alloc::string::String,
#[prost(int32, tag="2")]
pub tick: i32,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Mint {
#[prost(string, tag="1")]
pub sender: ::prost::alloc::string::String,
#[prost(string, tag="2")]
pub owner: ::prost::alloc::string::String,
/// Signed
#[prost(int32, tag="3")]
pub tick_lower: i32,
/// Signed
#[prost(int32, tag="4")]
pub tick_upper: i32,
/// Unsigned
#[prost(string, tag="5")]
pub amount: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="6")]
pub amount_0: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="7")]
pub amount_1: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Collect {
#[prost(string, tag="1")]
pub owner: ::prost::alloc::string::String,
#[prost(string, tag="2")]
pub recipient: ::prost::alloc::string::String,
#[prost(int32, tag="3")]
pub tick_lower: i32,
#[prost(int32, tag="4")]
pub tick_upper: i32,
/// Unsigned
#[prost(string, tag="5")]
pub amount_0: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="6")]
pub amount_1: ::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(int32, tag="2")]
pub tick_lower: i32,
#[prost(int32, tag="3")]
pub tick_upper: i32,
/// Unsigned
#[prost(string, tag="4")]
pub amount: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="5")]
pub amount_0: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="6")]
pub amount_1: ::prost::alloc::string::String,
}
#[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,
/// Signed
#[prost(string, tag="3")]
pub amount_0: ::prost::alloc::string::String,
/// Signed
#[prost(string, tag="4")]
pub amount_1: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="6")]
pub sqrt_price: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="7")]
pub liquidity: ::prost::alloc::string::String,
#[prost(int32, tag="8")]
pub tick: i32,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Flash {
#[prost(string, tag="1")]
pub sender: ::prost::alloc::string::String,
#[prost(string, tag="2")]
pub recipient: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="3")]
pub amount_0: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="4")]
pub amount_1: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="5")]
pub paid_0: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="6")]
pub paid_1: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SetFeeProtocol {
/// Unsigned
#[prost(uint64, tag="1")]
pub fee_protocol_0_old: u64,
/// Unsigned
#[prost(uint64, tag="2")]
pub fee_protocol_1_old: u64,
/// Unsigned
#[prost(uint64, tag="3")]
pub fee_protocol_0_new: u64,
/// Unsigned
#[prost(uint64, tag="4")]
pub fee_protocol_1_new: u64,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct CollectProtocol {
#[prost(string, tag="1")]
pub sender: ::prost::alloc::string::String,
#[prost(string, tag="2")]
pub recipient: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="3")]
pub amount_0: ::prost::alloc::string::String,
/// Unsigned
#[prost(string, tag="4")]
pub amount_1: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Type {
#[prost(message, tag="1")]
Initialize(Initialize),
#[prost(message, tag="2")]
Mint(Mint),
#[prost(message, tag="3")]
Collect(Collect),
#[prost(message, tag="4")]
Burn(Burn),
#[prost(message, tag="5")]
Swap(Swap),
#[prost(message, tag="6")]
Flash(Flash),
#[prost(message, tag="7")]
SetFeeProtocol(SetFeeProtocol),
#[prost(message, tag="8")]
CollectProtocol(CollectProtocol),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum LiquidityChangeType {
Delta = 0,
Absolute = 1,
}
impl LiquidityChangeType {
/// 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 {
LiquidityChangeType::Delta => "DELTA",
LiquidityChangeType::Absolute => "ABSOLUTE",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"DELTA" => Some(Self::Delta),
"ABSOLUTE" => Some(Self::Absolute),
_ => None,
}
}
}
// @@protoc_insertion_point(module)

View File

@@ -15,4 +15,5 @@ ignore = [
"ethereum-curve/src/abi", "ethereum-curve/src/abi",
"ethereum-uniswap-v2/src/abi", "ethereum-uniswap-v2/src/abi",
"ethereum-uniswap-v3/src/abi", "ethereum-uniswap-v3/src/abi",
"ethereum-uniswap-v3-logs-only/src/abi",
] ]