Skip to content

Add erc20 balances substream #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions erc20-balances/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
build
node_modules
*.spkg
*.log

# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
29 changes: 29 additions & 0 deletions erc20-balances/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "erc20"
version = "0.3.0"
description = "ERC-20"
edition = "2021"
authors = [
"Denis <denis@pinax.network>",
"Yaro <yaro@pinax.network>",
"Matthieu Vachon <https://github.com/maoueh>"
]

[lib]
crate-type = ["cdylib"]

[dependencies]
ethabi = "18.0"
prost = "0.11"
prost-types = "0.11"
num-bigint = "0.4"
substreams = "0.5"
substreams-ethereum = "0.9"
substreams-entity-change = "1.3"
substreams-sink-prometheus = "0.1"
substreams-sink-kv = "0.1"

[build-dependencies]
prost-build = "0.11"
anyhow = "1"
substreams-ethereum = "0.9"
21 changes: 21 additions & 0 deletions erc20-balances/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Pinax

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
44 changes: 44 additions & 0 deletions erc20-balances/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
ENDPOINT ?= mainnet.eth.streamingfast.io:443

.PHONY: all
all:
make build
make pack
make graph
make info

.PHONY: build
build:
cargo build --target wasm32-unknown-unknown --release

.PHONY: protogen
protogen:
substreams protogen --exclude-paths sf/substreams,google

.PHONY: tt
tt:
substreams run -e $(ENDPOINT) substreams.yaml graph_out -s 18384526 -t +10

.PHONY: pack
pack:
substreams pack

.PHONY: graph
graph:
substreams graph

.PHONY: info
info:
substreams info

.PHONY: run
run:
substreams run map_block -e eth.substreams.pinax.network:9000 -s -1000 -o jsonl

.PHONY: gui
gui:
substreams gui map_block -e eth.substreams.pinax.network:9000 -s 447766

.PHONY: deploy
deploy:
graph deploy --studio erc-20
159 changes: 159 additions & 0 deletions erc20-balances/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Substreams ERC20 Balance Changes
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

The goal of this Substreams project is to extract all ERC20 transfers from Ethereum events for the full chain.

The `map_balance_changes` module will output messages of type `erc20.types.v1.BalanceChange` defined by:

```proto
message BalanceChange {
string contract = 1;
string owner = 2;
string new_balance = 4;
BalanceChangeType change_type = 9;
}
```

## Known issues:

Tracking balance changes requires tracking state changes on chain. However, different contracts have different ways of storing balances.

We have implemented the following strategies for tracking balance changes:

### Type 1: Storage change is in the same call as the transfer

example:
https://etherscan.io/tx/0xf490320cff087d82747fcb0e6ed797f899ff887bcd15162933ea051c94c596ea#eventlog

Here is the relevant section from the Firehose block for this transaction:

```json
{
"index": 1,
"callType": "CALL",
"caller": "45225d3536ac02928f16071ab05066bce95c2cd5",
"address": "dac17f958d2ee523a2206206994597c13d831ec7",
"gasLimit": "104810",
"gasConsumed": "41601",
"input": "a9059cbb000000000000000000000000caf7ce56598e8588c9bf471e08b53e8a8d9541b300000000000000000000000000000000000000000000000000000000c84cfb23",
"executedCode": true,
"keccakPreimages": {
"3cacfdf5e3a27369ea8efd976a1d467ed2ce08586e22e7366aa4d82943439fa7": "00000000000000000000000045225d3536ac02928f16071ab05066bce95c2cd50000000000000000000000000000000000000000000000000000000000000006",
"d116b96c704431079cf20227b36d5f02fea21af673489300fe1ae3229e0c0d74": "000000000000000000000000caf7ce56598e8588c9bf471e08b53e8a8d9541b30000000000000000000000000000000000000000000000000000000000000002",
"ec2750738b8e716c607ab9d95b2d48bc4d6b8eacc278d1510c490ab2c788884d": "00000000000000000000000045225d3536ac02928f16071ab05066bce95c2cd50000000000000000000000000000000000000000000000000000000000000002"
},
"storageChanges": [
{
"address": "dac17f958d2ee523a2206206994597c13d831ec7",
"key": "ec2750738b8e716c607ab9d95b2d48bc4d6b8eacc278d1510c490ab2c788884d",
"oldValue": "000000000000000000000000000000000000000000000000000000355ed4c80e",
"newValue": "000000000000000000000000000000000000000000000000000000349687cceb",
"ordinal": "1154"
},
{
"address": "dac17f958d2ee523a2206206994597c13d831ec7",
"key": "d116b96c704431079cf20227b36d5f02fea21af673489300fe1ae3229e0c0d74",
"oldValue": "0000000000000000000000000000000000000000000000000000000000000000",
"newValue": "00000000000000000000000000000000000000000000000000000000c84cfb23",
"ordinal": "1155"
}
],
"logs": [
{
"address": "dac17f958d2ee523a2206206994597c13d831ec7",
"topics": [
"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"00000000000000000000000045225d3536ac02928f16071ab05066bce95c2cd5",
"000000000000000000000000caf7ce56598e8588c9bf471e08b53e8a8d9541b3"
],
"data": "00000000000000000000000000000000000000000000000000000000c84cfb23",
"blockIndex": 49,
"ordinal": "1157"
}
]
}
```

The correctness of the `old_balance` and `new_balance` values in this case is easily determined.

These types of transfers will result in a BalanceChange message with `change_type` set to `TYPE_1`.

### Type 2: Storage change is in a different call than the transfer

In this case, the Transfer but this results in storage changes in different child calls, where often the amount sent will be split to multiple accounts.

example:
https://etherscan.io/tx/0x5a31fb5d3f5bbb95023438f017ad6cd501ce70e445f31c2660c784e5a7eb5d83#eventlog

```json
{
"index": 4,
"logs": [
{
"address": "225bc3affc1da39bd3cb2100c74a41c62310d1e1",
"topics": [
"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"000000000000000000000000541f52216afdfeef6851eea9772b17d3cafd9438",
"000000000000000000000000b30acc73814d34941d71a1dfa5c2a5e618a062fe"
],
"data": "0000000000000000000000000000000000000000000000000000000000451f50",
"index": 2,
"blockIndex": 2,
"ordinal": "68"
}
]
},
{
"index": 10,
"keccakPreimages": {
"c0309ad5a3dcaf0d46cab6102b742e914f7ff8447190f509bf80a0f0b60c452c": "000000000000000000000000b30acc73814d34941d71a1dfa5c2a5e618a062fe0000000000000000000000000000000000000000000000000000000000000002"
},
"storageChanges": [
{
"address": "276c5c6ca8507ed7bac085fc9b9521f4f54b58d3",
"key": "c0309ad5a3dcaf0d46cab6102b742e914f7ff8447190f509bf80a0f0b60c452c",
"oldValue": "000000000000000000000000000000000000000000000000000000012d03e73e",
"newValue": "000000000000000000000000000000000000000000000000000000012d48915e",
"ordinal": "61"
}
],
}
```

In this example, the Transfer call is made in call index 4. Then in the subsequent child calls, the transfer of 4,530,000 tokens is split into transfers by the contract: One transfer of 4,500,000 to the original receiver and a transfer of 30,000 to another address. Some work is required to track the balance changes in this case.

These types of transfers will result in a BalanceChange message with `change_type` set to `TYPE_2`.

### Others

There are other types of transfers where the balance of the accounts before and after is not clear.

example:
https://etherscan.io/tx/0x5a31fb5d3f5bbb95023438f017ad6cd501ce70e445f31c2660c784e5a7eb5d83#eventlog

These transfers will result in a BalanceChange message with `change_type` set to `null`.

These should currently be discarded by the consumer of the substream as they are guaranteed to be incorrect.


## Running

### Generate protos

```bash
make protogen
```

### Build substreams

```bash
make build
```

### Build spkg

```bash
make pack
```


Loading