Compare commits

...

70 Commits

Author SHA1 Message Date
679d7f9927
feat: cache contracts created by other cached accounts for instant indexing 2025-07-03 11:52:35 +03:00
ef28ec1298
feat: add quoter updated handler 2025-07-03 10:55:44 +03:00
5cf5f9894c
feat: cache systemSigner account to index custodial contract deployments 2025-07-02 13:12:40 +03:00
20ba62f313
feat-testing: raise duplicates window to 20 minutes 2025-05-30 08:21:03 +03:00
e370d7eae5
fix: wrong config pos 2025-05-29 14:28:30 +03:00
574b42f75e
config: restore batch size 2025-05-29 14:21:43 +03:00
8417a43277
ci/cd: bump go version in Dockerfile 2025-05-26 13:01:33 +03:00
f90cc75323
deps: upgrade, deprecate redis, upgrade pond worker 2025-05-26 13:00:34 +03:00
20434beb17
Merge branch 'feat/tranfer-from-event' 2025-05-26 10:53:09 +03:00
838810dc14
fix: correct keccak signature 2025-05-26 10:50:55 +03:00
0efd01e058
feat: add TransferFrom event 2025-05-13 18:57:28 +03:00
cf23978621
feat: update op-geth 2025-03-13 12:25:59 +03:00
2d30901aa3
fix: load multiple registries via env
Signed-off-by: Mohammed Sohail <sohailsameja@gmail.com>
2025-03-13 12:25:22 +03:00
9c97ce2d2f
fix: load multiple registries via env 2025-03-13 12:20:10 +03:00
414b14a92e
docs: update README 2024-11-21 18:18:02 +03:00
4470095769
feat: add within network check for approve events
Merge commit '5962c26' from cel2
2024-11-20 11:25:55 +03:00
e503ecff5c
feat: restore geth library, bump deps, remove all celo refs 2024-11-20 11:25:26 +03:00
23a7561edc
release: disable cgo, bump to min go version 1.23.3 2024-11-20 11:14:52 +03:00
5962c26659
feat: add within network check for approve events 2024-11-20 10:57:51 +03:00
0b5af2f938
Merge branch 'cel2' of https://github.com/grassrootseconomics/eth-tracker into cel2 2024-11-20 09:59:57 +03:00
cf263c7d15
feat: change transfer event lookup to enforce both token AND address existence before emitting event
* Add ExistsNetwork cache lookup
* Exists now only accepts a single param
2024-11-20 09:56:38 +03:00
faa428c583
fix: load registries 2024-11-20 09:56:38 +03:00
f912f6835a
fix: temp patch to load a single registry
* We seem to have an issue loading a slice from env
2024-11-20 09:56:38 +03:00
fd1cced053
fix: load registries 2024-11-12 10:48:47 +03:00
dca3b4d211
fix: temp patch to load a single registry
* We seem to have an issue loading a slice from env
2024-11-12 10:36:29 +03:00
513912f361
Merge branch 'master' into cel2 2024-11-12 09:10:49 +03:00
90cc1563ec
feat: reduce busyCheckInterval -> 250ms 2024-11-12 09:10:27 +03:00
d4d3763732
Merge branch 'master' into cel2 2024-11-12 09:02:59 +03:00
078ad3614d
fix: remove unecessary dependency
* profiler
2024-11-12 09:02:37 +03:00
c93cc8270f
fix: remove required redis configs 2024-11-04 08:34:05 +03:00
115f6817e6
fix: remove required redis configs 2024-11-04 08:33:14 +03:00
7a64f29dc7
fix: Dockerfile
* remove bootstrap script
2024-11-04 08:18:47 +03:00
e5ea697403
fix: Dockerfile 2024-11-04 08:18:03 +03:00
b650e44392
cel2: merge v1.4.0 changes 2024-10-31 14:59:38 +03:00
eee3757895
docs: update README 2024-10-31 14:58:59 +03:00
dependabot[bot]
2283190147
build(deps): bump github.com/redis/rueidis from 1.0.47 to 1.0.48 (#51)
Bumps [github.com/redis/rueidis](https://github.com/redis/rueidis) from 1.0.47 to 1.0.48.
- [Release notes](https://github.com/redis/rueidis/releases)
- [Commits](https://github.com/redis/rueidis/compare/v1.0.47...v1.0.48)

---
updated-dependencies:
- dependency-name: github.com/redis/rueidis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-31 14:53:39 +03:00
75b402bbf6
feat: self-bootstrapping tracker, jetstream updates
* This removes redis as a hard dependency
* add profiler utils (temp)
2024-10-31 14:52:51 +03:00
6f850bb1d4
release(1.3.0-rc): Merge branch 'master' into cel2 2024-10-26 09:14:28 +03:00
e03dabdf0f
feat: add token approve handler 2024-10-14 09:25:18 +03:00
0e3bf47f83
Merge branch 'master' into cel2 2024-10-08 11:34:59 +03:00
e1158ab14a
feat: decouple bolt db folder 2024-10-08 11:34:43 +03:00
1def49c3ab
Merge branch 'master' into cel2 2024-10-08 09:02:01 +03:00
dependabot[bot]
f15373e34e
build(deps): bump github.com/lmittmann/w3 from 0.17.0 to 0.17.1 (#44)
Bumps [github.com/lmittmann/w3](https://github.com/lmittmann/w3) from 0.17.0 to 0.17.1.
- [Release notes](https://github.com/lmittmann/w3/releases)
- [Commits](https://github.com/lmittmann/w3/compare/v0.17.0...v0.17.1)

---
updated-dependencies:
- dependency-name: github.com/lmittmann/w3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 08:56:13 +03:00
dependabot[bot]
9f9f564f84
build(deps): bump github.com/knadh/koanf/providers/file (#46)
Bumps [github.com/knadh/koanf/providers/file](https://github.com/knadh/koanf) from 1.1.0 to 1.1.2.
- [Release notes](https://github.com/knadh/koanf/releases)
- [Commits](https://github.com/knadh/koanf/compare/v1.1.0...providers/file/v1.1.2)

---
updated-dependencies:
- dependency-name: github.com/knadh/koanf/providers/file
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 08:52:13 +03:00
dependabot[bot]
98e6b3bf68
build(deps): bump github.com/redis/rueidis from 1.0.46 to 1.0.47 (#47)
Bumps [github.com/redis/rueidis](https://github.com/redis/rueidis) from 1.0.46 to 1.0.47.
- [Release notes](https://github.com/redis/rueidis/releases)
- [Commits](https://github.com/redis/rueidis/compare/v1.0.46...v1.0.47)

---
updated-dependencies:
- dependency-name: github.com/redis/rueidis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 08:52:04 +03:00
725c18c0ff
Squashed commit of the following:
commit 05d142664d
Author: Mohamed Sohail 天明 <sohailsameja@gmail.com>
Date:   Mon Oct 7 15:12:58 2024 +0300

    feat: handle contract creation (#43)

    * feat: add contract creation handler

    * fix: process contract creations

    * fix: redis keys name

commit 4b2ad3daf9
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Date:   Mon Oct 7 15:12:15 2024 +0300

    build(deps): bump github.com/knadh/koanf/providers/env (#37)

    Bumps [github.com/knadh/koanf/providers/env](https://github.com/knadh/koanf) from 0.1.0 to 1.0.0.
    - [Release notes](https://github.com/knadh/koanf/releases)
    - [Commits](https://github.com/knadh/koanf/compare/v0.1.0...v1.0.0)

    ---
    updated-dependencies:
    - dependency-name: github.com/knadh/koanf/providers/env
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...

    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

commit f1086fcdc1
Author: Mohamed Sohail 天明 <sohailsameja@gmail.com>
Date:   Mon Oct 7 10:07:11 2024 +0300

    feat: optimize exists to check multiple keys in one call (#40)

    * closes #32

commit fd59d286f5
Author: Mohammed Sohail <sohailsameja@gmail.com>
Date:   Mon Oct 7 09:49:01 2024 +0300

    feat: add custodial registration proxy handler
2024-10-07 15:14:24 +03:00
05d142664d
feat: handle contract creation (#43)
* feat: add contract creation handler

* fix: process contract creations

* fix: redis keys name
2024-10-07 15:12:58 +03:00
dependabot[bot]
4b2ad3daf9
build(deps): bump github.com/knadh/koanf/providers/env (#37)
Bumps [github.com/knadh/koanf/providers/env](https://github.com/knadh/koanf) from 0.1.0 to 1.0.0.
- [Release notes](https://github.com/knadh/koanf/releases)
- [Commits](https://github.com/knadh/koanf/compare/v0.1.0...v1.0.0)

---
updated-dependencies:
- dependency-name: github.com/knadh/koanf/providers/env
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-07 15:12:15 +03:00
f1086fcdc1
feat: optimize exists to check multiple keys in one call (#40)
* closes #32
2024-10-07 10:07:11 +03:00
fd59d286f5
feat: add custodial registration proxy handler 2024-10-07 09:49:01 +03:00
8e3e027044
Merge branch 'master' into cel2 2024-10-04 12:35:52 +03:00
17c7f5825b
docs: add CEL2 notice 2024-10-04 12:35:35 +03:00
22d5582643
feat: cel2 specific build branch 2024-10-04 12:32:42 +03:00
55159196f4
fix: remove wide jetstream dedup window. Now reduced to 1 minute.
* closes #39
2024-10-04 11:54:10 +03:00
1c7c53a12f
fix: rewrite bootstrap script from scratch to verify correctness/fix inconsistency
* same size when using map and redis
* same size when tested against different RPC nodes

* closes #38
2024-10-04 11:52:30 +03:00
6057208b50
fix: Makefile 2024-10-03 10:43:28 +03:00
ffdac26c41
docs: update README 2024-09-27 15:01:14 +03:00
404e769655
chore: remove celo specifics 2024-09-27 14:55:10 +03:00
8c15109238
feat (BREAKING): evm compat tracker with mod directive replace approach 2024-09-27 14:28:26 +03:00
dependabot[bot]
afacc4425d
build(deps): bump github.com/uptrace/bunrouter from 1.0.21 to 1.0.22 (#34)
Bumps [github.com/uptrace/bunrouter](https://github.com/uptrace/bunrouter) from 1.0.21 to 1.0.22.
- [Release notes](https://github.com/uptrace/bunrouter/releases)
- [Changelog](https://github.com/uptrace/bunrouter/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uptrace/bunrouter/compare/v1.0.21...v1.0.22)

---
updated-dependencies:
- dependency-name: github.com/uptrace/bunrouter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-27 13:41:40 +03:00
efc84970cc
refactor: Decouple router and handlers (#31)
* refactor: decouple router to allow adding custom log and input data handlers

* feat: refactor handlers

* devops: update service build args

* chore: cleanup unecessary files/code
2024-09-18 17:35:57 +03:00
6cb5ea4f02
Merge pull request #28 from grassrootseconomics/dependabot/go_modules/github.com/alitto/pond-1.9.2
build(deps): bump github.com/alitto/pond from 1.9.1 to 1.9.2
2024-09-18 17:35:41 +03:00
bb8122a576
Merge pull request #29 from grassrootseconomics/dependabot/go_modules/github.com/redis/rueidis-1.0.46
build(deps): bump github.com/redis/rueidis from 1.0.44 to 1.0.46
2024-09-18 17:35:30 +03:00
dependabot[bot]
cb14939367
build(deps): bump github.com/alitto/pond from 1.9.1 to 1.9.2
Bumps [github.com/alitto/pond](https://github.com/alitto/pond) from 1.9.1 to 1.9.2.
- [Release notes](https://github.com/alitto/pond/releases)
- [Commits](https://github.com/alitto/pond/compare/v1.9.1...v1.9.2)

---
updated-dependencies:
- dependency-name: github.com/alitto/pond
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-18 14:32:02 +00:00
dependabot[bot]
0c7a35c0c7
build(deps): bump github.com/redis/rueidis from 1.0.44 to 1.0.46
Bumps [github.com/redis/rueidis](https://github.com/redis/rueidis) from 1.0.44 to 1.0.46.
- [Release notes](https://github.com/redis/rueidis/releases)
- [Commits](https://github.com/redis/rueidis/compare/v1.0.44...v1.0.46)

---
updated-dependencies:
- dependency-name: github.com/redis/rueidis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-18 14:31:46 +00:00
14e85dc1e5
Merge pull request #26 from grassrootseconomics/dependabot/go_modules/github.com/bits-and-blooms/bitset-1.14.3
build(deps): bump github.com/bits-and-blooms/bitset from 1.13.0 to 1.14.3
2024-09-18 17:30:39 +03:00
8ced6a02b1
Merge pull request #25 from grassrootseconomics/dependabot/go_modules/go.etcd.io/bbolt-1.3.11
build(deps): bump go.etcd.io/bbolt from 1.3.10 to 1.3.11
2024-09-18 17:30:17 +03:00
dependabot[bot]
a80d387838
build(deps): bump github.com/bits-and-blooms/bitset
Bumps [github.com/bits-and-blooms/bitset](https://github.com/bits-and-blooms/bitset) from 1.13.0 to 1.14.3.
- [Release notes](https://github.com/bits-and-blooms/bitset/releases)
- [Commits](https://github.com/bits-and-blooms/bitset/compare/v1.13.0...v1.14.3)

---
updated-dependencies:
- dependency-name: github.com/bits-and-blooms/bitset
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 23:16:10 +00:00
dependabot[bot]
d398ed2198
build(deps): bump go.etcd.io/bbolt from 1.3.10 to 1.3.11
Bumps [go.etcd.io/bbolt](https://github.com/etcd-io/bbolt) from 1.3.10 to 1.3.11.
- [Release notes](https://github.com/etcd-io/bbolt/releases)
- [Commits](https://github.com/etcd-io/bbolt/compare/v1.3.10...v1.3.11)

---
updated-dependencies:
- dependency-name: go.etcd.io/bbolt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 23:16:06 +00:00
22ffc224ca
fix: mint and burn func signatures, adjustable batch size for pool and backfill buffer 2024-09-05 14:24:32 +03:00
63 changed files with 2297 additions and 2144 deletions

View File

@ -52,5 +52,4 @@ jobs:
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
tags: |
ghcr.io/grassrootseconomics/celo-tracker:latest
ghcr.io/grassrootseconomics/celo-tracker:${{ env.RELEASE_TAG }}
ghcr.io/grassrootseconomics/eth-tracker:${{ env.RELEASE_TAG }}

5
.gitignore vendored
View File

@ -2,5 +2,6 @@ tracker_db
.vscode
.idx
**/*.env
celo-tracker
celo-tracker-cache-bootstrap
eth-tracker
eth-tracker-cache-bootstrap
*.pprof

View File

@ -1,6 +1,6 @@
FROM golang:1.23.0-bookworm AS build
FROM golang:1.24-bookworm AS build
ENV CGO_ENABLED=1
ENV CGO_ENABLED=0
ARG BUILDPLATFORM
ARG TARGETPLATFORM
@ -11,8 +11,7 @@ WORKDIR /build
COPY . .
RUN go mod download
RUN go build -o celo-tracker-cache-bootstrap -ldflags="-X main.build=${BUILD} -s -w" cmd/bootstrap/main.go
RUN go build -o celo-tracker -ldflags="-X main.build=${BUILD} -s -w" cmd/service/main.go
RUN go build -o eth-tracker -ldflags="-X main.build=${BUILD} -s -w" cmd/service/*.go
FROM debian:bookworm-slim
@ -25,4 +24,4 @@ COPY --from=build /build/* .
EXPOSE 5001
CMD ["./celo-tracker"]
CMD ["./eth-tracker"]

View File

@ -1,7 +1,7 @@
BIN := celo-tracker
BOOTSTRAP_BIN := celo-tracker-cache-bootstrap
BIN := eth-tracker
BOOTSTRAP_BIN := eth-tracker-cache-bootstrap
DB_FILE := tracker_db
BUILD_CONF := CGO_ENABLED=1 GOOS=linux GOARCH=amd64
BUILD_CONF := CGO_ENABLED=0 GOOS=linux GOARCH=amd64
BUILD_COMMIT := $(shell git rev-parse --short HEAD 2> /dev/null)
DEBUG := DEV=true
@ -14,11 +14,10 @@ clean-db:
rm ${DB_FILE}
build:
${BUILD_CONF} go build -ldflags="-X main.build=${BUILD_COMMIT} -s -w" -o ${BOOTSTRAP_BIN} cmd/bootstrap/main.go
${BUILD_CONF} go build -ldflags="-X main.build=${BUILD_COMMIT} -s -w" -o ${BIN} cmd/service/main.go
${BUILD_CONF} go build -ldflags="-X main.build=${BUILD_COMMIT} -s -w" -o ${BIN} cmd/service/*.go
run-bootstrap:
${BUILD_CONF} ${DEBUG} go run cmd/bootstrap/main.go
run:
${BUILD_CONF} ${DEBUG} go run cmd/service/main.go
${BUILD_CONF} ${DEBUG} go run cmd/service/*.go

View File

@ -1,30 +1,48 @@
# celo-tracker
# eth-tracker
![GitHub Tag](https://img.shields.io/github/v/tag/grassrootseconomics/celo-tracker)
![GitHub Tag](https://img.shields.io/github/v/tag/grassrootseconomics/eth-tracker)
A fast and lightweight tracker designed to monitor the Celo blockchain for live and historical transaction events, including reverted transactions. It filters these events and publishes them to NATS for further processing.
A fast and lightweight tracker designed to monitor EVM blockchains for live and
historical transaction events, including reverted transactions. It filters these
events and publishes them to NATS for further processing.
It applies deduplication at the NATS level, making it safe to run in a distributed fashion.
On a warmed up archive RPC node (HTTP) with the default config, it can process
in excess of 10k blocks/min utilizing not more than 50 MB of RAM.
It applies deduplication at the NATS level, making it safe to run in a
distributed fashion.
Note: To run it against an L2/EVM chain, you will need to manually add a replace
directive in the `go.mod` file pointing to the EVM chain's `*geth` compatible
source code. This will allow the tracker to process transaction types other than
Ethereum's `0x0, 0x1 and 0x2`.
### CEL2
We maintain a CEL2 compatible tracker (source and container image) on the `cel2`
branch.
## Getting Started
A `Makefile` is also provided to build the required binaries to run celo-tracker.
A `Makefile` is also provided to build the required binaries to run eth-tracker.
### Bootstrap Cache
### Cache Bootstrap
An optional binary, `celo-tracker-cache-bootstrap`, is included to build the Redis cache with all relevant Grassroots Economics smart contract and user addresses to allow filtering on very busy smart contracts e.g. cUSD.
During startup `eth-tracker` will always build the cache with all relevant
Grassroots Economics smart contract and user addresses to allow filtering on
very busy smart contracts e.g. cUSD.
The cache will auto-update based on any additions/removals from all indexes.
### Prerequisites
* Git
* Docker
* NATS server
* Redis server
* Access to a Celo RPC node
- Git
- Docker
- NATS server
- Access to an RPC node, archive preffered
See [docker-compose.yaml](dev/docker-compose.yaml) for an example on how to run and deploy a single instance.
See [docker-compose.yaml](dev/docker-compose.yaml) for an example on how to run
and deploy a single instance.
### 1. Build the Docker image
@ -33,18 +51,21 @@ We provide pre-built images for `linux/amd64`. See the packages tab on Github.
If you are on any other platform:
```bash
git clone https://github.com/grassrootseconomics/celo-tracker.git
cd celo-tracker
docker buildx build --build-arg BUILD=$(git rev-parse --short HEAD) --tag celo-tracker:$(git rev-parse --short HEAD) --tag celo-tracker:latest .
git clone https://github.com/grassrootseconomics/eth-tracker.git
cd eth-tracker
docker buildx build --build-arg BUILD=$(git rev-parse --short HEAD) --tag eth-tracker:$(git rev-parse --short HEAD) --tag eth-tracker:latest .
docker images
```
### 2. Run NATS and Redis
### 2. Run NATS
For an example, see `dev/docker-compose.yaml`.
### 3. Update config values
See `.env.example` on how to override default values defined in `config.toml` using env variables. Alternatively, mount your own config.toml either during build time or Docker runtime.
See `.env.example` on how to override default values defined in `config.toml`
using env variables. Alternatively, mount your own config.toml either during
build time or Docker runtime.
```bash
# Override only specific config values
@ -52,8 +73,8 @@ nano .env.example
mv .env.example .env
```
Refer to [`config.toml`](config.toml) to understand different config value settings.
Refer to [`config.toml`](config.toml) to understand different config value
settings.
### 4. Run the tracker
@ -80,7 +101,8 @@ docker compose up
### Monitoring with NATS CLI
Install NATS CLI from [here](https://github.com/nats-io/natscli?tab=readme-ov-file#installation).
Install NATS CLI from
[here](https://github.com/nats-io/natscli?tab=readme-ov-file#installation).
```bash
nats subscribe "TRACKER.*"
@ -88,7 +110,10 @@ nats subscribe "TRACKER.*"
### DB File
A `tracker_db` file is created on the first run. This keeps track of all blocks missed by the processor to attempt a retry later on. This file should not be deleted if you want to maintain resume support for historical tracking across restarts.
A `tracker_db` file is created on the first run. This keeps track of all blocks
missed by the processor to attempt a retry later on. This file should not be
deleted if you want to maintain resume support for historical tracking across
restarts.
## License

View File

@ -1,236 +0,0 @@
package main
import (
"context"
"flag"
"log/slog"
"os"
"time"
"github.com/celo-org/celo-blockchain/common"
"github.com/grassrootseconomics/celo-tracker/internal/cache"
"github.com/grassrootseconomics/celo-tracker/internal/chain"
"github.com/grassrootseconomics/celo-tracker/internal/util"
"github.com/grassrootseconomics/celoutils/v3"
"github.com/grassrootseconomics/w3-celo"
"github.com/grassrootseconomics/w3-celo/module/eth"
"github.com/knadh/koanf/v2"
)
const cacheType = "redis"
var (
build = "dev"
confFlag string
lo *slog.Logger
ko *koanf.Koanf
)
func init() {
flag.StringVar(&confFlag, "config", "config.toml", "Config file location")
flag.Parse()
lo = util.InitLogger()
ko = util.InitConfig(lo, confFlag)
lo.Info("starting GE redis cache bootstrapper", "build", build)
}
func main() {
var (
tokenRegistryGetter = w3.MustNewFunc("tokenRegistry()", "address")
quoterGetter = w3.MustNewFunc("quoter()", "address")
)
chain, err := chain.NewRPCFetcher(chain.CeloRPCOpts{
RPCEndpoint: ko.MustString("chain.rpc_endpoint"),
ChainID: ko.MustInt64("chain.chainid"),
})
if err != nil {
lo.Error("could not initialize chain client", "error", err)
os.Exit(1)
}
cache, err := cache.New(cache.CacheOpts{
Logg: lo,
CacheType: cacheType,
RedisDSN: ko.MustString("redis.dsn"),
})
if err != nil {
lo.Error("could not initialize cache", "error", err)
os.Exit(1)
}
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5)
defer cancel()
for _, registry := range ko.MustStrings("bootstrap.ge_registries") {
registryMap, err := chain.Provider().RegistryMap(ctx, celoutils.HexToAddress(registry))
if err != nil {
lo.Error("could not fetch registry", "error", err)
os.Exit(1)
}
if tokenIndex := registryMap[celoutils.TokenIndex]; tokenIndex != celoutils.ZeroAddress {
tokenIndexIter, err := chain.Provider().NewBatchIterator(ctx, tokenIndex)
if err != nil {
lo.Error("could not create token index iter", "error", err)
os.Exit(1)
}
for {
batch, err := tokenIndexIter.Next(ctx)
if err != nil {
lo.Error("error fetching next token index batch", "error", err)
os.Exit(1)
}
if batch == nil {
break
}
lo.Debug("index batch", "index", tokenIndex.Hex(), "size", len(batch))
for _, address := range batch {
if err := cache.Add(ctx, address.Hex()); err != nil {
lo.Error("redis error setting key", "error", err)
os.Exit(1)
}
}
}
}
if poolIndex := registryMap[celoutils.PoolIndex]; poolIndex != celoutils.ZeroAddress {
poolIndexIter, err := chain.Provider().NewBatchIterator(ctx, poolIndex)
if err != nil {
lo.Error("cache could create pool index iter", "error", err)
os.Exit(1)
}
for {
batch, err := poolIndexIter.Next(ctx)
if err != nil {
lo.Error("error fetching next pool index batch", "error", err)
os.Exit(1)
}
if batch == nil {
break
}
lo.Debug("index batch", "index", poolIndex.Hex(), "size", len(batch))
for _, address := range batch {
if err := cache.Add(ctx, address.Hex()); err != nil {
lo.Error("redis error setting key", "error", err, "address", address.Hex())
os.Exit(1)
}
var poolTokenIndex, priceQuoter common.Address
err := chain.Provider().Client.CallCtx(
ctx,
eth.CallFunc(address, tokenRegistryGetter).Returns(&poolTokenIndex),
eth.CallFunc(address, quoterGetter).Returns(&priceQuoter),
)
if err != nil {
lo.Error("error fetching pool token index and/or quoter", "error", err)
os.Exit(1)
}
if priceQuoter != celoutils.ZeroAddress {
if err := cache.Add(ctx, priceQuoter.Hex()); err != nil {
lo.Error("redis error setting key", "error", err)
os.Exit(1)
}
}
if poolTokenIndex != celoutils.ZeroAddress {
if err := cache.Add(ctx, poolTokenIndex.Hex()); err != nil {
lo.Error("redis error setting key", "error", err)
os.Exit(1)
}
poolTokenIndexIter, err := chain.Provider().NewBatchIterator(ctx, poolTokenIndex)
if err != nil {
lo.Error("error creating pool token index iter", "error", err)
os.Exit(1)
}
for {
batch, err := poolTokenIndexIter.Next(ctx)
if err != nil {
lo.Error("error fetching next pool token index batch", "error", err)
os.Exit(1)
}
if batch == nil {
break
}
lo.Debug("index batch", "index", poolTokenIndex.Hex(), "size", len(batch))
for _, address := range batch {
if address != celoutils.ZeroAddress {
if err := cache.Add(ctx, address.Hex()); err != nil {
lo.Error("redis error setting key", "error", err)
os.Exit(1)
}
}
}
}
}
}
}
}
if accountsIndex := registryMap[celoutils.AccountIndex]; accountsIndex != celoutils.ZeroAddress {
accountsIndexIter, err := chain.Provider().NewBatchIterator(ctx, accountsIndex)
if err != nil {
lo.Error("could not create accounts index iter", "error", err)
os.Exit(1)
}
for {
batch, err := accountsIndexIter.Next(ctx)
if err != nil {
lo.Error("error fetching next accounts index batch", "error", err)
os.Exit(1)
}
if batch == nil {
break
}
lo.Debug("index batch", "index", accountsIndex.Hex(), "size", len(batch))
for _, address := range batch {
if err := cache.Add(ctx, address.Hex()); err != nil {
lo.Error("redis error setting key", "error", err)
os.Exit(1)
}
}
}
}
for _, v := range registryMap {
if err := cache.Add(ctx, v.Hex()); err != nil {
lo.Error("redis error setting key", "error", err)
os.Exit(1)
}
}
}
for _, address := range ko.MustStrings("bootstrap.watchlist") {
if err := cache.Add(ctx, address); err != nil {
lo.Error("redis error setting key", "error", err)
os.Exit(1)
}
}
for _, address := range ko.MustStrings("bootstrap.blacklist") {
if err := cache.Remove(ctx, address); err != nil {
lo.Error("redis error deleting key", "error", err)
os.Exit(1)
}
}
if err := cache.Remove(ctx, celoutils.ZeroAddress.Hex()); err != nil {
lo.Error("redis error deleting key", "error", err)
os.Exit(1)
}
cacheSize, err := cache.Size(ctx)
if err != nil {
lo.Warn("error fetching cache size")
}
lo.Info("redis cache bootstrap complete", "addresses_cached", cacheSize)
}

View File

@ -13,18 +13,17 @@ import (
"syscall"
"time"
"github.com/grassrootseconomics/celo-tracker/db"
"github.com/grassrootseconomics/celo-tracker/internal/api"
"github.com/grassrootseconomics/celo-tracker/internal/backfill"
"github.com/grassrootseconomics/celo-tracker/internal/cache"
"github.com/grassrootseconomics/celo-tracker/internal/chain"
"github.com/grassrootseconomics/celo-tracker/internal/pool"
"github.com/grassrootseconomics/celo-tracker/internal/processor"
"github.com/grassrootseconomics/celo-tracker/internal/pub"
"github.com/grassrootseconomics/celo-tracker/internal/router"
"github.com/grassrootseconomics/celo-tracker/internal/stats"
"github.com/grassrootseconomics/celo-tracker/internal/syncer"
"github.com/grassrootseconomics/celo-tracker/internal/util"
"github.com/grassrootseconomics/eth-tracker/db"
"github.com/grassrootseconomics/eth-tracker/internal/api"
"github.com/grassrootseconomics/eth-tracker/internal/backfill"
"github.com/grassrootseconomics/eth-tracker/internal/cache"
"github.com/grassrootseconomics/eth-tracker/internal/chain"
"github.com/grassrootseconomics/eth-tracker/internal/pool"
"github.com/grassrootseconomics/eth-tracker/internal/processor"
"github.com/grassrootseconomics/eth-tracker/internal/pub"
"github.com/grassrootseconomics/eth-tracker/internal/stats"
"github.com/grassrootseconomics/eth-tracker/internal/syncer"
"github.com/grassrootseconomics/eth-tracker/internal/util"
"github.com/knadh/koanf/v2"
)
@ -45,15 +44,15 @@ func init() {
lo = util.InitLogger()
ko = util.InitConfig(lo, confFlag)
lo.Info("starting celo tracker", "build", build)
}
func main() {
lo.Info("starting celo tracker", "build", build)
var wg sync.WaitGroup
ctx, stop := notifyShutdown()
chain, err := chain.NewRPCFetcher(chain.CeloRPCOpts{
chain, err := chain.NewRPCFetcher(chain.EthRPCOpts{
RPCEndpoint: ko.MustString("chain.rpc_endpoint"),
ChainID: ko.MustInt64("chain.chainid"),
})
@ -61,6 +60,7 @@ func main() {
lo.Error("could not initialize chain client", "error", err)
os.Exit(1)
}
lo.Debug("loaded rpc fetcher")
db, err := db.New(db.DBOpts{
Logg: lo,
@ -70,32 +70,39 @@ func main() {
lo.Error("could not initialize blocks db", "error", err)
os.Exit(1)
}
lo.Debug("loaded blocks db")
cache, err := cache.New(cache.CacheOpts{
Logg: lo,
cacheOpts := cache.CacheOpts{
Chain: chain,
Registries: ko.MustStrings("bootstrap.ge_registry"),
Watchlist: ko.Strings("bootstrap.watchlist"),
Blacklist: ko.Strings("bootstrap.blacklist"),
CacheType: ko.MustString("core.cache_type"),
RedisDSN: ko.MustString("redis.dsn"),
})
Logg: lo,
}
if ko.MustString("core.cache_type") == "redis" {
cacheOpts.RedisDSN = ko.MustString("redis.dsn")
}
cache, err := cache.New(cacheOpts)
if err != nil {
lo.Error("could not initialize cache", "error", err)
os.Exit(1)
}
lo.Debug("loaded and boostrapped cache")
jetStreamPub, err := pub.NewJetStreamPub(pub.JetStreamOpts{
Endpoint: ko.MustString("jetstream.endpoint"),
PersistDuration: time.Duration(ko.MustInt("jetstream.persist_duration_hrs")) * time.Hour,
DedupDuration: time.Duration(ko.MustInt("jetstream.dedup_duration_hrs")) * time.Hour,
Logg: lo,
})
if err != nil {
lo.Error("could not initialize jetstream pub", "error", err)
os.Exit(1)
}
lo.Debug("loaded jetstream publisher")
router := router.New(router.RouterOpts{
Pub: jetStreamPub,
Cache: cache,
})
router := bootstrapEventRouter(cache, jetStreamPub.Send)
lo.Debug("bootstrapped event router")
blockProcessor := processor.NewProcessor(processor.ProcessorOpts{
Cache: cache,
@ -104,6 +111,7 @@ func main() {
Router: router,
Logg: lo,
})
lo.Debug("bootstrapped processor")
poolOpts := pool.PoolOpts{
Logg: lo,
@ -114,12 +122,14 @@ func main() {
poolOpts.WorkerCount = runtime.NumCPU() * 3
}
workerPool := pool.New(poolOpts)
lo.Debug("bootstrapped worker pool")
stats := stats.New(stats.StatsOpts{
Cache: cache,
Logg: lo,
Pool: workerPool,
})
lo.Debug("bootstrapped stats provider")
chainSyncer, err := syncer.New(syncer.SyncerOpts{
DB: db,
@ -134,22 +144,28 @@ func main() {
lo.Error("could not initialize chain syncer", "error", err)
os.Exit(1)
}
lo.Debug("bootstrapped realtime syncer")
backfill := backfill.New(backfill.BackfillOpts{
BatchSize: ko.MustInt("core.batch_size"),
DB: db,
Logg: lo,
Pool: workerPool,
})
lo.Debug("bootstrapped backfiller")
apiServer := &http.Server{
Addr: ko.MustString("api.address"),
Handler: api.New(),
}
lo.Debug("bootstrapped API server")
lo.Debug("starting routines")
wg.Add(1)
go func() {
defer wg.Done()
chainSyncer.Start()
lo.Debug("started chain syncer")
}()
wg.Add(1)
@ -158,7 +174,9 @@ func main() {
if err := backfill.Run(false); err != nil {
lo.Error("backfiller initial run error", "error", err)
}
lo.Debug("completed initial backfill run")
backfill.Start()
lo.Debug("started periodic backfiller")
}()
wg.Add(1)

51
cmd/service/router.go Normal file
View File

@ -0,0 +1,51 @@
package main
import (
"github.com/grassrootseconomics/eth-tracker/internal/cache"
"github.com/grassrootseconomics/eth-tracker/internal/handler"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
func bootstrapEventRouter(cacheProvider cache.Cache, pubCB router.Callback) *router.Router {
handlerContainer := handler.New(cacheProvider)
router := router.New(pubCB)
router.RegisterContractCreationHandler(handler.HandleContractCreation(handlerContainer))
router.RegisterLogRoute(w3.H("0x26162814817e23ec5035d6a2edc6c422da2da2119e27cfca6be65cc2dc55ca4c"), handler.HandleFaucetGiveLog())
router.RegisterLogRoute(w3.H("0xa226db3f664042183ee0281230bba26cbf7b5057e50aee7f25a175ff45ce4d7f"), handler.HandleIndexAddLog(handlerContainer))
router.RegisterLogRoute(w3.H("0x24a12366c02e13fe4a9e03d86a8952e85bb74a456c16e4a18b6d8295700b74bb"), handler.HandleIndexRemoveLog(handlerContainer))
router.RegisterLogRoute(w3.H("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0"), handler.HandleOwnershipLog())
router.RegisterLogRoute(w3.H("0x5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62"), handler.HandlePoolDepositLog())
router.RegisterLogRoute(w3.H("0xd6d34547c69c5ee3d2667625c188acf1006abb93e0ee7cf03925c67cf7760413"), handler.HandlePoolSwapLog())
router.RegisterLogRoute(w3.H("0xdb9ce1a76955721ca61ac50cd1b87f9ab8620325c8619a62192c2dc7871d56b1"), handler.HandleQuoterPriceUpdateLog())
router.RegisterLogRoute(w3.H("0x6b7e2e653f93b645d4ed7292d6429f96637084363e477c8aaea1a43ed13c284e"), handler.HandleSealStateChangeLog())
router.RegisterLogRoute(w3.H("0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5"), handler.HandleTokenBurnLog())
router.RegisterLogRoute(w3.H("0xab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f8"), handler.HandleTokenMintLog())
router.RegisterLogRoute(w3.H("0x894e56e1dac400b4475c83d8af0f0aa44de17c62764bd82f6e768a504e242461"), handler.HandleCustodialRegistrationLog())
router.RegisterLogRoute(w3.H("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), handler.HandleTokenTransferLog(handlerContainer))
router.RegisterLogRoute(w3.H("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"), handler.HandleTokenApproveLog(handlerContainer))
router.RegisterLogRoute(w3.H("0x5f7542858008eeb041631f30e6109ae94b83a58e9a58261dd2c42c508850f939"), handler.HandleTokenTransferFromLog(handlerContainer))
router.RegisterLogRoute(w3.H("0x06526a30af2ff868c2686df12e95844d8ae300416bbec5d5ccc2d2f4afdb17a0"), handler.HandleQuoterUpdatedLog())
router.RegisterInputDataRoute("63e4bff4", handler.HandleFaucetGiveInputData())
router.RegisterInputDataRoute("de82efb4", handler.HandleFaucetGiveInputData())
router.RegisterInputDataRoute("0a3b0a4f", handler.HandleIndexAddInputData())
router.RegisterInputDataRoute("4420e486", handler.HandleIndexAddInputData())
router.RegisterInputDataRoute("29092d0e", handler.HandleIndexRemoveInputData())
router.RegisterInputDataRoute("f2fde38b", handler.HandleOwnershipInputData())
router.RegisterInputDataRoute("47e7ef24", handler.HandlePoolDepositInputData())
router.RegisterInputDataRoute("d9caed12", handler.HandlePoolSwapInputData())
router.RegisterInputDataRoute("ebc59dff", handler.HandleQuoterPriceUpdateInputdata())
router.RegisterInputDataRoute("86fe212d", handler.HandleSealStateChangeInputData())
router.RegisterInputDataRoute("42966c68", handler.HandleTokenBurnInputData())
router.RegisterInputDataRoute("449a52f8", handler.HandleTokenMintInputData())
router.RegisterInputDataRoute("4420e486", handler.HandleCustodialRegistrationInputData())
router.RegisterInputDataRoute("a9059cbb", handler.HandleTokenTransferInputData(handlerContainer))
router.RegisterInputDataRoute("23b872dd", handler.HandleTokenTransferInputData(handlerContainer))
router.RegisterInputDataRoute("095ea7b3", handler.HandleTokenApproveInputData(handlerContainer))
router.RegisterInputDataRoute("f912c64b", handler.HandleQuoterUpdatedInputData())
return router
}

View File

@ -4,34 +4,30 @@ address = ":5001"
[core]
# Use a specific cache implementation
cache_type = "redis"
cache_type = "internal"
# Use a specific db implementation
db_type = "bolt"
# Tune max go routines that can process blocks
# Defaults to (nproc * 3)
pool_size = 0
# If you are using an archive node, set this to true
batch_size = 100
[redis]
dsn = "127.0.0.1:6379"
[chain]
ws_endpoint = "wss://ws.celo.grassecon.net"
rpc_endpoint = "https://celo.grassecon.net"
ws_endpoint = "ws://localhost:8546"
rpc_endpoint = "http://localhost:8545"
# Defaults to Celo mainnet
# At the moment only support Celo based blockchains
chainid = 42220
chainid = 1337
# This will start a backfill if set to any other value
# Ideally this should remain 0
start_block = 0
[bootstrap]
# This will bootstrap the cache on which addresses to track
# Grassroots Economics specific registries that autoload all other smart contracts
ge_registries = [
"0xd1FB944748aca327a1ba036B082993D9dd9Bfa0C",
"0x0cc9f4fff962def35bb34a53691180b13e653030",
]
ge_registry = ["0x0f8E97ef2d6A42CF62549D4924FCBdcE83A1C6A5"]
watchlist = [""]
blacklist = [""]
@ -39,4 +35,3 @@ blacklist = [""]
enable = true
endpoint = "nats://127.0.0.1:4222"
persist_duration_hrs = 48
dedup_duration_hrs = 6

View File

@ -14,7 +14,7 @@ type boltDB struct {
}
const (
dbFolderName = "tracker_db"
dbFolderName = "db/tracker_db"
upperBoundKey = "upper"
lowerBoundKey = "lower"

View File

@ -1 +0,0 @@
package db

View File

@ -1,17 +1,4 @@
services:
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --save 60 1 --loglevel warning
volumes:
- tracker-redis:/data
ports:
- "127.0.0.1:6379:6379"
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 10s
timeout: 5s
retries: 5
nats:
image: nats:2
restart: unless-stopped

109
go.mod
View File

@ -1,83 +1,64 @@
module github.com/grassrootseconomics/celo-tracker
module github.com/grassrootseconomics/eth-tracker
go 1.23.0
go 1.24
require (
github.com/VictoriaMetrics/metrics v1.35.1
github.com/alitto/pond v1.9.1
github.com/bits-and-blooms/bitset v1.13.0
github.com/celo-org/celo-blockchain v1.8.4
github.com/grassrootseconomics/celoutils/v3 v3.3.1
github.com/grassrootseconomics/w3-celo v0.19.0
github.com/VictoriaMetrics/metrics v1.37.0
github.com/alitto/pond/v2 v2.3.4
github.com/bits-and-blooms/bitset v1.22.0
github.com/ethereum/go-ethereum v1.15.11
github.com/grassrootseconomics/ethutils v1.4.0
github.com/kamikazechaser/common v0.2.0
github.com/knadh/koanf/parsers/toml v0.1.0
github.com/knadh/koanf/providers/env v0.1.0
github.com/knadh/koanf/providers/file v1.1.0
github.com/knadh/koanf/v2 v2.1.1
github.com/nats-io/nats.go v1.36.0
github.com/puzpuzpuz/xsync/v3 v3.4.0
github.com/redis/rueidis v1.0.44
github.com/stretchr/testify v1.9.0
github.com/uptrace/bunrouter v1.0.21
go.etcd.io/bbolt v1.3.10
github.com/knadh/koanf/providers/env v1.0.0
github.com/knadh/koanf/providers/file v1.1.2
github.com/knadh/koanf/v2 v2.2.0
github.com/lmittmann/w3 v0.19.5
github.com/nats-io/nats.go v1.42.0
github.com/puzpuzpuz/xsync/v3 v3.5.1
github.com/stretchr/testify v1.10.0
github.com/uptrace/bunrouter v1.0.23
go.etcd.io/bbolt v1.4.0
)
require (
filippo.io/edwards25519 v1.0.0-alpha.2 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/VictoriaMetrics/fastcache v1.12.2 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/celo-org/celo-bls-go v0.3.4 // indirect
github.com/celo-org/celo-bls-go-android v0.3.3 // indirect
github.com/celo-org/celo-bls-go-ios v0.3.3 // indirect
github.com/celo-org/celo-bls-go-linux v0.3.3 // indirect
github.com/celo-org/celo-bls-go-macos v0.3.3 // indirect
github.com/celo-org/celo-bls-go-other v0.3.3 // indirect
github.com/celo-org/celo-bls-go-windows v0.3.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/consensys/bavard v0.1.30 // indirect
github.com/consensys/gnark-crypto v0.17.0 // indirect
github.com/crate-crypto/go-eth-kzg v1.3.0 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deckarep/golang-set/v2 v2.8.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.1 // indirect
github.com/ethereum/go-verkle v0.2.2 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/hdevalence/ed25519consensus v0.0.0-20201207055737-7fde80a9d5ff // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.3.0 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/holiman/uint256 v1.3.2 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/knadh/koanf/maps v0.1.2 // indirect
github.com/lmittmann/tint v1.0.4 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/nats-io/nkeys v0.4.7 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/nats-io/nkeys v0.4.11 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/gomega v1.31.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/tsdb v0.7.1 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/supranational/blst v0.3.14 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/valyala/fastrand v1.1.0 // indirect
github.com/valyala/histogram v1.2.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/time v0.11.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)

399
go.sum
View File

@ -1,151 +1,131 @@
filippo.io/edwards25519 v1.0.0-alpha.2 h1:EWbZLqGEPSIj2W69gx04KtNVkyPIfe3uj0DhDQJonbQ=
filippo.io/edwards25519 v1.0.0-alpha.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
github.com/VictoriaMetrics/metrics v1.35.1 h1:o84wtBKQbzLdDy14XeskkCZih6anG+veZ1SwJHFGwrU=
github.com/VictoriaMetrics/metrics v1.35.1/go.mod h1:r7hveu6xMdUACXvB8TYdAj8WEsKzWB0EkpJN+RDtOf8=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alitto/pond v1.9.1 h1:OfCpIrMyrWJpn34f647DcFmUxjK8+7Nu3eoVN/WTP+o=
github.com/alitto/pond v1.9.1/go.mod h1:xQn3P/sHTYcU/1BR3i86IGIrilcrGC2LiS+E2+CJWsI=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd v0.23.2 h1:/YOgUp25sdCnP5ho6Hl3s0E438zlX+Kak7E6TgBgoT0=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72 h1:fUmDBbSvv1uOzo/t8WaxZMVb7BxJ8JECo5lGoR9c5bA=
github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72/go.mod h1:OEE5igu/CDjGegM1Jn6ZMo7R6LlV/JChAkjfQQIRLpg=
github.com/celo-org/celo-blockchain v1.8.4 h1:QJiRRGcZyO+PutT8WkFO1juORANAOGhN8yGNP7HvBqk=
github.com/celo-org/celo-blockchain v1.8.4/go.mod h1:0udpV9QnZ2rnVu3vf8G9Wjs5di6Bf9urjwGVgAUEGB8=
github.com/celo-org/celo-bls-go v0.3.4 h1:slNePT/gVjgUi7f8M4KTwBz/YYgv3JWU6XqyY0xKN84=
github.com/celo-org/celo-bls-go v0.3.4/go.mod h1:qDZHMC3bBqOw5qle28cRtKlEyJhslZtckcc2Tomqdks=
github.com/celo-org/celo-bls-go-android v0.3.3 h1:iZ2Gragn3JItkptmppeq1SENmNVc1f1W25UE4u231HY=
github.com/celo-org/celo-bls-go-android v0.3.3/go.mod h1:cFgtFRH8+6x5b+EyG5SqniXY3aKd03NBSGDgITscX34=
github.com/celo-org/celo-bls-go-ios v0.3.3 h1:/yHaEYft9WfXyPIGuJz7V2/r+tp2IqSpkvKsMsVgbuY=
github.com/celo-org/celo-bls-go-ios v0.3.3/go.mod h1:eaSoMpx29YV5oF7jXVChzJpNfxeZHnAa8G4PjL5CyW0=
github.com/celo-org/celo-bls-go-linux v0.3.3 h1:ukSQSIRyFCQeC1i7LJJunRKvlLuG1JMwNZ6DQZC51fE=
github.com/celo-org/celo-bls-go-linux v0.3.3/go.mod h1:DVpJadg22OrxBtMb0ub6iNVdqDBL/r6EDdWVAA0bHa0=
github.com/celo-org/celo-bls-go-macos v0.3.3 h1:H4ZGc+kS3e/w9Q6qru6FtlkYtVDS8eIQgw6UURB/Jlo=
github.com/celo-org/celo-bls-go-macos v0.3.3/go.mod h1:mYPuRqGMVxj6yZUeL6Q6ggtP52HPBS1jz+FvBPXQ7QA=
github.com/celo-org/celo-bls-go-other v0.3.3 h1:/Q9SLJK22hibPm/WI/OPxbBmgXTgUhjUgobfzz7qj/w=
github.com/celo-org/celo-bls-go-other v0.3.3/go.mod h1:tNxZNfekzyT7TdYQbyNPhkfpcYtA3KCU/IKX5FNxM/U=
github.com/celo-org/celo-bls-go-windows v0.3.3 h1:0IP+Ad9l+op50TIfkmFr+j7+TIjKksVROe+EoF7Ixa4=
github.com/celo-org/celo-bls-go-windows v0.3.3/go.mod h1:82GC5iJA9Qw5gynhYqR8ht3J+l/MO8eSNzgSTMI8UdA=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/VictoriaMetrics/metrics v1.37.0 h1:u5Yr+HFofQyn7kgmmkufgkX0nEA6G1oEyK2eaKsVaUM=
github.com/VictoriaMetrics/metrics v1.37.0/go.mod h1:r7hveu6xMdUACXvB8TYdAj8WEsKzWB0EkpJN+RDtOf8=
github.com/alitto/pond/v2 v2.3.4 h1:hR0bqAwJiI2chu3cLN4gVyNC7rc5mj/l5wg0710nxsY=
github.com/alitto/pond/v2 v2.3.4/go.mod h1:xkjYEgQ05RSpWdfSd1nM3OVv7TBhLdy7rMp3+2Nq+yE=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA=
github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU=
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/consensys/bavard v0.1.30 h1:wwAj9lSnMLFXjEclKwyhf7Oslg8EoaFz9u1QGgt0bsk=
github.com/consensys/bavard v0.1.30/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
github.com/consensys/gnark-crypto v0.17.0 h1:vKDhZMOrySbpZDCvGMOELrHFv/A9mJ7+9I8HEfRZSkI=
github.com/consensys/gnark-crypto v0.17.0/go.mod h1:A2URlMHUT81ifJ0UlLzSlm7TmnE3t7VxEThApdMukJw=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI=
github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4=
github.com/crate-crypto/go-kzg-4844 v1.1.0/go.mod h1:JolLjpSff1tCCJKaJx4psrlEdlXuJEC996PL3tTAFks=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/ethereum/c-kzg-4844/v2 v2.1.1 h1:KhzBVjmURsfr1+S3k/VE35T02+AW2qU9t9gr4R6YpSo=
github.com/ethereum/c-kzg-4844/v2 v2.1.1/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E=
github.com/ethereum/go-ethereum v1.15.11 h1:JK73WKeu0WC0O1eyX+mdQAVHUV+UR1a9VB/domDngBU=
github.com/ethereum/go-ethereum v1.15.11/go.mod h1:mf8YiHIb0GR4x4TipcvBUPxJLw1mFdmxzoDi11sDRoI=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c=
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grassrootseconomics/celoutils/v3 v3.3.1 h1:ehf0eG2c8BfR2DojOm/nk8SQ44DyTFxXKISw1dHym/w=
github.com/grassrootseconomics/celoutils/v3 v3.3.1/go.mod h1:HTHFNnWmvL6rY096xAraOMV3POYFAquoTABP6ogaF7U=
github.com/grassrootseconomics/w3-celo v0.19.0 h1:3O5GK2os3/f9D/5oKsB0YxGyO5S3aqon/kx7ckyjQ4o=
github.com/grassrootseconomics/w3-celo v0.19.0/go.mod h1:M3KJaj25DspF9siaqNXyamAuzC5DmWXx74tpyPp+R4Y=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grassrootseconomics/ethutils v1.4.0 h1:1QRLhOhY7GFoY27IlW9WoVoQuKDIGRjlFiPDiUWDN8Y=
github.com/grassrootseconomics/ethutils v1.4.0/go.mod h1:7RCNwDj1wfgWt8CV4p7cQuZWPPI5fjwl/piq7NthaWg=
github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hdevalence/ed25519consensus v0.0.0-20201207055737-7fde80a9d5ff h1:LeVKjw8pcDQj7WVVnbFvbD7ovcv+r/l15ka1NH6Lswc=
github.com/hdevalence/ed25519consensus v0.0.0-20201207055737-7fde80a9d5ff/go.mod h1:Feit0l8NcNO4g69XNjwvsR0LGcwMMfzI1TF253rOIlQ=
github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4=
github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/uint256 v1.3.0 h1:4wdcm/tnd0xXdu7iS3ruNvxkWwrb4aeBQv19ayYn8F4=
github.com/holiman/uint256 v1.3.0/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/kamikazechaser/common v0.2.0 h1:bqi5UaMTDm/wtZlJEvQDNhsLVJP4Beg+HKWeQ+dhpss=
github.com/kamikazechaser/common v0.2.0/go.mod h1:I1LEc8+W+g/KHZWARc1gMhuSa2STbQgfL4Hao6I/ZwY=
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo=
github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
github.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI=
github.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18=
github.com/knadh/koanf/providers/env v0.1.0 h1:LqKteXqfOWyx5Ab9VfGHmjY9BvRXi+clwyZozgVRiKg=
github.com/knadh/koanf/providers/env v0.1.0/go.mod h1:RE8K9GbACJkeEnkl8L/Qcj8p4ZyPXZIQ191HJi44ZaQ=
github.com/knadh/koanf/providers/file v1.1.0 h1:MTjA+gRrVl1zqgetEAIaXHqYje0XSosxSiMD4/7kz0o=
github.com/knadh/koanf/providers/file v1.1.0/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM=
github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/knadh/koanf/providers/env v1.0.0 h1:ufePaI9BnWH+ajuxGGiJ8pdTG0uLEUWC7/HDDPGLah0=
github.com/knadh/koanf/providers/env v1.0.0/go.mod h1:mzFyRZueYhb37oPmC1HAv/oGEEuyvJDA98r3XAa8Gak=
github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w=
github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
github.com/knadh/koanf/v2 v2.2.0 h1:FZFwd9bUjpb8DyCWARUBy5ovuhDs1lI87dOEn2K8UVU=
github.com/knadh/koanf/v2 v2.2.0/go.mod h1:PSFru3ufQgTsI7IF+95rf9s8XA1+aHxKuO/W+dPoHEY=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc=
github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/lmittmann/w3 v0.19.5 h1:WwVRyIwhRLfIahmpB1EglsB3o1XWsgydgrxIUp5upFQ=
github.com/lmittmann/w3 v0.19.5/go.mod h1:pN97sGGYGvsbqOYj/ms3Pd+7k/aiK/9OpNcxMmmzSOI=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
@ -154,138 +134,101 @@ github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjU
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU=
github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/nats-io/nats.go v1.42.0 h1:ynIMupIOvf/ZWH/b2qda6WGKGNSjwOUutTpWRvAmhaM=
github.com/nats-io/nats.go v1.42.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM=
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4=
github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/redis/rueidis v1.0.44 h1:QfhfuovwEabcywfEXofRjPZuT29pjtpIWDJlCGHZfg8=
github.com/redis/rueidis v1.0.44/go.mod h1:bnbkk4+CkXZgDPEbUtSos/o55i4RhFYYesJ4DS2zmq0=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE=
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg=
github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y=
github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo=
github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/uptrace/bunrouter v1.0.21 h1:HXarvX+N834sXyHpl+I/TuE11m19kLW/qG5u3YpHUag=
github.com/uptrace/bunrouter v1.0.21/go.mod h1:TwT7Bc0ztF2Z2q/ZzMuSVkcb/Ig/d3MQeP2cxn3e1hI=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/uptrace/bunrouter v1.0.23 h1:Bi7NKw3uCQkcA/GUCtDNPq5LE5UdR9pe+UyWbjHB/wU=
github.com/uptrace/bunrouter v1.0.23/go.mod h1:O3jAcl+5qgnF+ejhgkmbceEk0E/mqaK+ADOocdNpY8M=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8=
github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ=
github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ=
github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=

View File

@ -11,7 +11,6 @@ func New() *bunrouter.Router {
router := bunrouter.New()
router.GET("/metrics", metricsHandler())
return router
}

View File

@ -5,18 +5,20 @@ import (
"log/slog"
"time"
"github.com/grassrootseconomics/celo-tracker/db"
"github.com/grassrootseconomics/celo-tracker/internal/pool"
"github.com/grassrootseconomics/eth-tracker/db"
"github.com/grassrootseconomics/eth-tracker/internal/pool"
)
type (
BackfillOpts struct {
BatchSize int
DB db.DB
Logg *slog.Logger
Pool *pool.Pool
}
Backfill struct {
batchSize int
db db.DB
logg *slog.Logger
pool *pool.Pool
@ -27,13 +29,12 @@ type (
const (
idleCheckInterval = 60 * time.Second
busyCheckInterval = 1 * time.Second
maxPoolSizePush = 100
busyCheckInterval = 250 * time.Millisecond
)
func New(o BackfillOpts) *Backfill {
return &Backfill{
batchSize: o.BatchSize,
db: o.DB,
logg: o.Logg,
pool: o.Pool,
@ -90,13 +91,13 @@ func (b *Backfill) Run(skipLatest bool) error {
if missingBlocksCount > 0 {
b.logg.Info("found missing blocks", "skip_latest", skipLatest, "missing_blocks_count", missingBlocksCount)
buffer := make([]uint, maxPoolSizePush)
buffer := make([]uint, b.batchSize)
j := uint(0)
pushedCount := 0
j, buffer = missingBlocks.NextSetMany(j, buffer)
for ; len(buffer) > 0; j, buffer = missingBlocks.NextSetMany(j, buffer) {
for k := range buffer {
if pushedCount >= maxPoolSizePush {
if pushedCount >= b.batchSize {
break
}
@ -104,11 +105,11 @@ func (b *Backfill) Run(skipLatest bool) error {
b.logg.Debug("pushed block from backfill", "block", buffer[k])
pushedCount++
}
j += 1
j++
}
}
if missingBlocksCount > maxPoolSizePush {
if missingBlocksCount > uint(b.batchSize) {
b.ticker.Reset(busyCheckInterval)
} else {
b.ticker.Reset(idleCheckInterval)

228
internal/cache/bootstrap.go vendored Normal file
View File

@ -0,0 +1,228 @@
package cache
import (
"context"
"log/slog"
"os"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/internal/chain"
"github.com/grassrootseconomics/ethutils"
"github.com/lmittmann/w3"
"github.com/lmittmann/w3/module/eth"
)
func bootstrapCache(
chain chain.Chain,
cache Cache,
registries []string,
watchlist []string,
blacklist []string,
lo *slog.Logger,
) error {
var (
tokenRegistryGetter = w3.MustNewFunc("tokenRegistry()", "address")
quoterGetter = w3.MustNewFunc("quoter()", "address")
systemAcccountGetter = w3.MustNewFunc("systemAccount()", "address")
)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5)
defer cancel()
for _, registry := range registries {
registryMap, err := chain.Provider().RegistryMap(ctx, ethutils.HexToAddress(registry))
if err != nil {
lo.Error("could not fetch registry", "registry", registry, "error", err)
os.Exit(1)
}
for k, v := range registryMap {
if v != ethutils.ZeroAddress {
if err := cache.Add(ctx, v.Hex()); err != nil {
return err
}
lo.Debug("cached registry entry", "type", k, "address", v.Hex())
}
}
if custodialRegistrationProxy := registryMap[ethutils.CustodialProxy]; custodialRegistrationProxy != ethutils.ZeroAddress {
var systemAccount common.Address
err := chain.Provider().Client.CallCtx(
ctx,
eth.CallFunc(custodialRegistrationProxy, systemAcccountGetter).Returns(&systemAccount),
)
if err != nil {
return err
}
if systemAccount != ethutils.ZeroAddress {
if err := cache.Add(ctx, systemAccount.Hex()); err != nil {
return err
}
lo.Debug("cached custodial system account", "address", systemAccount.Hex())
}
}
if accountIndex := registryMap[ethutils.AccountIndex]; accountIndex != ethutils.ZeroAddress {
if err := cache.Add(ctx, accountIndex.Hex()); err != nil {
return err
}
lo.Debug("cached account index", "address", accountIndex.Hex())
accountIndexIter, err := chain.Provider().NewBatchIterator(ctx, accountIndex)
if err != nil {
return err
}
for {
accountIndexBatch, err := accountIndexIter.Next(ctx)
if err != nil {
return err
}
if accountIndexBatch == nil {
break
}
for _, address := range accountIndexBatch {
if address != ethutils.ZeroAddress {
if err := cache.Add(ctx, address.Hex()); err != nil {
return err
}
}
}
lo.Debug("cached account index batch", "batch_size", len(accountIndexBatch))
}
}
if tokenIndex := registryMap[ethutils.TokenIndex]; tokenIndex != ethutils.ZeroAddress {
if err := cache.Add(ctx, tokenIndex.Hex()); err != nil {
return err
}
lo.Debug("cached token index", "address", tokenIndex.Hex())
tokenIndexIter, err := chain.Provider().NewBatchIterator(ctx, tokenIndex)
if err != nil {
return err
}
for {
tokenIndexBatch, err := tokenIndexIter.Next(ctx)
if err != nil {
return err
}
if tokenIndexBatch == nil {
break
}
for _, address := range tokenIndexBatch {
if address != ethutils.ZeroAddress {
if err := cache.Add(ctx, address.Hex()); err != nil {
return err
}
}
}
lo.Debug("cached token index batch", "batch_size", len(tokenIndexBatch))
}
}
if poolIndex := registryMap[ethutils.PoolIndex]; poolIndex != ethutils.ZeroAddress {
if err := cache.Add(ctx, poolIndex.Hex()); err != nil {
return err
}
lo.Debug("cached pool index", "address", poolIndex.Hex())
poolIndexIter, err := chain.Provider().NewBatchIterator(ctx, poolIndex)
if err != nil {
return err
}
for {
poolIndexBatch, err := poolIndexIter.Next(ctx)
if err != nil {
return err
}
if poolIndexBatch == nil {
break
}
for _, address := range poolIndexBatch {
if address != ethutils.ZeroAddress {
if err := cache.Add(ctx, address.Hex()); err != nil {
return err
}
var poolTokenIndex, priceQuoter common.Address
err := chain.Provider().Client.CallCtx(
ctx,
eth.CallFunc(address, tokenRegistryGetter).Returns(&poolTokenIndex),
eth.CallFunc(address, quoterGetter).Returns(&priceQuoter),
)
if err != nil {
return err
}
if priceQuoter != ethutils.ZeroAddress {
if err := cache.Add(ctx, priceQuoter.Hex()); err != nil {
return err
}
lo.Debug("cached pool index quoter", "pool", poolIndex.Hex(), "address", priceQuoter.Hex())
}
if poolTokenIndex != ethutils.ZeroAddress {
if err := cache.Add(ctx, poolTokenIndex.Hex()); err != nil {
return err
}
lo.Debug("cached pool index token index", "pool", poolIndex.Hex(), "address", poolTokenIndex.Hex())
poolTokenIndexIter, err := chain.Provider().NewBatchIterator(ctx, poolTokenIndex)
if err != nil {
return err
}
for {
poolTokenIndexBatch, err := poolTokenIndexIter.Next(ctx)
if err != nil {
return err
}
if poolTokenIndexBatch == nil {
break
}
for _, address := range poolTokenIndexBatch {
if address != ethutils.ZeroAddress {
if err := cache.Add(ctx, address.Hex()); err != nil {
return err
}
}
}
lo.Debug("cached pool token index batch", "batch_size", len(poolTokenIndexBatch))
}
}
}
}
lo.Debug("cached pool index batch", "batch_size", len(poolIndexBatch))
}
}
for _, address := range watchlist {
if err := cache.Add(ctx, ethutils.HexToAddress(address).Hex()); err != nil {
return err
}
}
for _, address := range blacklist {
if err := cache.Remove(ctx, ethutils.HexToAddress(address).Hex()); err != nil {
return err
}
}
if err := cache.Remove(ctx, ethutils.ZeroAddress.Hex()); err != nil {
return err
}
cacheSize, err := cache.Size(ctx)
if err != nil {
return err
}
lo.Info("registry bootstrap complete", "registry", registry, "current_cache_size", cacheSize)
}
return nil
}

View File

@ -3,6 +3,8 @@ package cache
import (
"context"
"log/slog"
"github.com/grassrootseconomics/eth-tracker/internal/chain"
)
type (
@ -10,53 +12,43 @@ type (
Add(context.Context, string) error
Remove(context.Context, string) error
Exists(context.Context, string) (bool, error)
ExistsNetwork(context.Context, string, ...string) (bool, error)
Size(context.Context) (int64, error)
}
CacheOpts struct {
Logg *slog.Logger
RedisDSN string
CacheType string
Registries []string
Watchlist []string
Blacklist []string
Chain chain.Chain
Logg *slog.Logger
}
)
func New(o CacheOpts) (Cache, error) {
o.Logg.Info("initializing cache", "registries", o.Registries, "watchlist", o.Watchlist, "blacklist", o.Blacklist)
var cache Cache
switch o.CacheType {
case "map":
case "internal":
cache = NewMapCache()
case "redis":
redisCache, err := NewRedisCache(redisOpts{
DSN: o.RedisDSN,
})
if err != nil {
return nil, err
}
cache = redisCache
default:
cache = NewMapCache()
o.Logg.Warn("invalid cache type, using default type (map)")
}
// geSmartContracts, err := o.Chain.Provider().GetGESmartContracts(
// context.Background(),
// o.Registries,
// )
// if err != nil {
// return nil, fmt.Errorf("cache could not bootstrap GE smart contracts: err %v", err)
// }
// for k, v := range geSmartContracts {
// cache.Add(k, v)
// }
// for _, address := range o.Watchlist {
// cache.Add(address, false)
// }
// for _, address := range o.Blacklist {
// cache.Remove(address)
// }
// o.Logg.Info("cache bootstrap complete", "cached_addresses", cache.Size())
if err := bootstrapCache(
o.Chain,
cache,
o.Registries,
o.Watchlist,
o.Blacklist,
o.Logg,
); err != nil {
return cache, err
}
return cache, nil
}

View File

@ -1,56 +0,0 @@
package cache
import (
"context"
"github.com/redis/rueidis"
)
type (
redisOpts struct {
DSN string
}
redisCache struct {
client rueidis.Client
}
)
func NewRedisCache(o redisOpts) (Cache, error) {
client, err := rueidis.NewClient(rueidis.ClientOption{
InitAddress: []string{o.DSN},
})
if err != nil {
return nil, err
}
return &redisCache{
client: client,
}, nil
}
func (c *redisCache) Add(ctx context.Context, key string) error {
// Without NX it will overwrite any existing KEY
cmd := c.client.B().Set().Key(key).Value("true").Build()
return c.client.Do(ctx, cmd).Error()
}
func (c *redisCache) Remove(ctx context.Context, key string) error {
cmd := c.client.B().Del().Key(key).Build()
return c.client.Do(ctx, cmd).Error()
}
func (c *redisCache) Exists(ctx context.Context, key string) (bool, error) {
cmd := c.client.B().Exists().Key(key).Build()
res, err := c.client.Do(ctx, cmd).AsBool()
if err != nil {
return false, err
}
return res, nil
}
func (c *redisCache) Size(ctx context.Context) (int64, error) {
cmd := c.client.B().Dbsize().Build()
return c.client.Do(ctx, cmd).AsInt64()
}

View File

@ -7,12 +7,12 @@ import (
)
type mapCache struct {
xmap *xsync.Map
xmap *xsync.MapOf[string, bool]
}
func NewMapCache() Cache {
return &mapCache{
xmap: xsync.NewMap(),
xmap: xsync.NewMapOf[string, bool](),
}
}
@ -28,7 +28,27 @@ func (c *mapCache) Remove(_ context.Context, key string) error {
func (c *mapCache) Exists(_ context.Context, key string) (bool, error) {
_, ok := c.xmap.Load(key)
return ok, nil
if ok {
return true, nil
}
return false, nil
}
func (c *mapCache) ExistsNetwork(_ context.Context, token string, addresses ...string) (bool, error) {
_, ok := c.xmap.Load(token)
if !ok {
return false, nil
}
for _, v := range addresses {
_, ok := c.xmap.Load(v)
if ok {
return true, nil
}
}
return false, nil
}
func (c *mapCache) Size(_ context.Context) (int64, error) {

View File

@ -1,121 +0,0 @@
package chain
import (
"context"
"math/big"
"net/http"
"time"
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/core/types"
"github.com/celo-org/celo-blockchain/rpc"
"github.com/grassrootseconomics/celoutils/v3"
"github.com/grassrootseconomics/w3-celo"
"github.com/grassrootseconomics/w3-celo/module/eth"
"github.com/grassrootseconomics/w3-celo/w3types"
)
type (
CeloRPCOpts struct {
RPCEndpoint string
ChainID int64
}
CeloRPC struct {
provider *celoutils.Provider
}
)
func NewRPCFetcher(o CeloRPCOpts) (Chain, error) {
customRPCClient, err := lowTimeoutRPCClient(o.RPCEndpoint)
if err != nil {
return nil, err
}
chainProvider := celoutils.NewProvider(
o.RPCEndpoint,
o.ChainID,
celoutils.WithClient(customRPCClient),
)
return &CeloRPC{
provider: chainProvider,
}, nil
}
func lowTimeoutRPCClient(rpcEndpoint string) (*w3.Client, error) {
httpClient := &http.Client{
Timeout: 10 * time.Second,
}
rpcClient, err := rpc.DialHTTPWithClient(
rpcEndpoint,
httpClient,
)
if err != nil {
return nil, err
}
return w3.NewClient(rpcClient), nil
}
func (c *CeloRPC) GetBlocks(ctx context.Context, blockNumbers []uint64) ([]types.Block, error) {
blocksCount := len(blockNumbers)
calls := make([]w3types.RPCCaller, blocksCount)
blocks := make([]types.Block, blocksCount)
for i, v := range blockNumbers {
calls[i] = eth.BlockByNumber(new(big.Int).SetUint64(v)).Returns(&blocks[i])
}
if err := c.provider.Client.CallCtx(ctx, calls...); err != nil {
return nil, err
}
return blocks, nil
}
func (c *CeloRPC) GetBlock(ctx context.Context, blockNumber uint64) (*types.Block, error) {
var block types.Block
blockCall := eth.BlockByNumber(new(big.Int).SetUint64(blockNumber)).Returns(&block)
if err := c.provider.Client.CallCtx(ctx, blockCall); err != nil {
return nil, err
}
return &block, nil
}
func (c *CeloRPC) GetLatestBlock(ctx context.Context) (uint64, error) {
var latestBlock big.Int
latestBlockCall := eth.BlockNumber().Returns(&latestBlock)
if err := c.provider.Client.CallCtx(ctx, latestBlockCall); err != nil {
return 0, err
}
return latestBlock.Uint64(), nil
}
func (c *CeloRPC) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, error) {
var transaction types.Transaction
if err := c.provider.Client.CallCtx(ctx, eth.Tx(txHash).Returns(&transaction)); err != nil {
return nil, err
}
return &transaction, nil
}
func (c *CeloRPC) GetReceipts(ctx context.Context, blockNumber *big.Int) (types.Receipts, error) {
var receipts types.Receipts
if err := c.provider.Client.CallCtx(ctx, eth.BlockReceipts(blockNumber).Returns(&receipts)); err != nil {
return nil, err
}
return receipts, nil
}
func (c *CeloRPC) Provider() *celoutils.Provider {
return c.provider
}

View File

@ -4,17 +4,17 @@ import (
"context"
"math/big"
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/core/types"
"github.com/grassrootseconomics/celoutils/v3"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/grassrootseconomics/ethutils"
)
type Chain interface {
GetBlocks(context.Context, []uint64) ([]types.Block, error)
GetBlocks(context.Context, []uint64) ([]*types.Block, error)
GetBlock(context.Context, uint64) (*types.Block, error)
GetLatestBlock(context.Context) (uint64, error)
GetTransaction(context.Context, common.Hash) (*types.Transaction, error)
GetReceipts(context.Context, *big.Int) (types.Receipts, error)
// Expose provider until we eject from celoutils
Provider() *celoutils.Provider
Provider() *ethutils.Provider
}

View File

@ -1,3 +1,118 @@
package chain
// TBA
import (
"context"
"math/big"
"net/http"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/grassrootseconomics/ethutils"
"github.com/lmittmann/w3"
"github.com/lmittmann/w3/module/eth"
"github.com/lmittmann/w3/w3types"
)
type (
EthRPCOpts struct {
RPCEndpoint string
ChainID int64
}
EthRPC struct {
provider *ethutils.Provider
}
)
func NewRPCFetcher(o EthRPCOpts) (Chain, error) {
customRPCClient, err := lowTimeoutRPCClient(o.RPCEndpoint)
if err != nil {
return nil, err
}
chainProvider := ethutils.NewProvider(
o.RPCEndpoint,
o.ChainID,
ethutils.WithClient(customRPCClient),
)
return &EthRPC{
provider: chainProvider,
}, nil
}
func lowTimeoutRPCClient(rpcEndpoint string) (*w3.Client, error) {
httpClient := &http.Client{
Timeout: 10 * time.Second,
}
rpcClient, err := rpc.DialOptions(context.Background(), rpcEndpoint, rpc.WithHTTPClient(httpClient))
if err != nil {
return nil, err
}
return w3.NewClient(rpcClient), nil
}
func (c *EthRPC) GetBlocks(ctx context.Context, blockNumbers []uint64) ([]*types.Block, error) {
blocksCount := len(blockNumbers)
calls := make([]w3types.RPCCaller, blocksCount)
blocks := make([]*types.Block, blocksCount)
for i, v := range blockNumbers {
calls[i] = eth.BlockByNumber(new(big.Int).SetUint64(v)).Returns(&blocks[i])
}
if err := c.provider.Client.CallCtx(ctx, calls...); err != nil {
return nil, err
}
return blocks, nil
}
func (c *EthRPC) GetBlock(ctx context.Context, blockNumber uint64) (*types.Block, error) {
var block *types.Block
blockCall := eth.BlockByNumber(new(big.Int).SetUint64(blockNumber)).Returns(&block)
if err := c.provider.Client.CallCtx(ctx, blockCall); err != nil {
return nil, err
}
return block, nil
}
func (c *EthRPC) GetLatestBlock(ctx context.Context) (uint64, error) {
var latestBlock *big.Int
latestBlockCall := eth.BlockNumber().Returns(&latestBlock)
if err := c.provider.Client.CallCtx(ctx, latestBlockCall); err != nil {
return 0, err
}
return latestBlock.Uint64(), nil
}
func (c *EthRPC) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, error) {
var transaction *types.Transaction
if err := c.provider.Client.CallCtx(ctx, eth.Tx(txHash).Returns(&transaction)); err != nil {
return nil, err
}
return transaction, nil
}
func (c *EthRPC) GetReceipts(ctx context.Context, blockNumber *big.Int) (types.Receipts, error) {
var receipts types.Receipts
if err := c.provider.Client.CallCtx(ctx, eth.BlockReceipts(blockNumber).Returns(&receipts)); err != nil {
return nil, err
}
return receipts, nil
}
func (c *EthRPC) Provider() *ethutils.Provider {
return c.provider
}

View File

@ -9,12 +9,12 @@ import (
)
const (
testRPCEndpoint = "https://celo.archive.grassecon.net"
testChainID = 42220
testRPCEndpoint = "https://alfajores-forno.celo-testnet.org"
testChainID = 44787
)
func setupCeloRPC() (Chain, error) {
opts := CeloRPCOpts{
func setupEthRPC() (Chain, error) {
opts := EthRPCOpts{
RPCEndpoint: testRPCEndpoint,
ChainID: testChainID,
}
@ -22,13 +22,13 @@ func setupCeloRPC() (Chain, error) {
}
func TestRPC_GetBlocks(t *testing.T) {
rpcFetcher, err := setupCeloRPC()
rpcFetcher, err := setupEthRPC()
require.NoError(t, err)
blockNumbers := []uint64{
19_600_000,
23_000_000,
27_000_000,
26_385_000,
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
@ -39,10 +39,10 @@ func TestRPC_GetBlocks(t *testing.T) {
}
func TestRPC_GetBlock(t *testing.T) {
rpcFetcher, err := setupCeloRPC()
rpcFetcher, err := setupEthRPC()
require.NoError(t, err)
var blockNumber uint64 = 19_900_000
var blockNumber uint64 = 26_386_000
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
@ -52,7 +52,7 @@ func TestRPC_GetBlock(t *testing.T) {
}
func TestRPC_GetLatestBlock(t *testing.T) {
rpcFetcher, err := setupCeloRPC()
rpcFetcher, err := setupEthRPC()
require.NoError(t, err)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
@ -64,10 +64,10 @@ func TestRPC_GetLatestBlock(t *testing.T) {
}
func TestRPC_GetTransaction(t *testing.T) {
rpcFetcher, err := setupCeloRPC()
rpcFetcher, err := setupEthRPC()
require.NoError(t, err)
var blockNumber uint64 = 19_900_000
var blockNumber uint64 = 26_387_000
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
@ -81,10 +81,10 @@ func TestRPC_GetTransaction(t *testing.T) {
}
func TestRPC_GetReceipts(t *testing.T) {
rpcFetcher, err := setupCeloRPC()
rpcFetcher, err := setupEthRPC()
require.NoError(t, err)
var blockNumber uint64 = 19_900_000
var blockNumber uint64 = 26_388_000
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

View File

@ -0,0 +1,32 @@
package handler
import (
"context"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
)
const contractCreationEventName = "CONTRACT_CREATION"
func HandleContractCreation(hc *HandlerContainer) router.ContractCreationHandlerFunc {
return func(ctx context.Context, ccp router.ContractCreationPayload, c router.Callback) error {
contractCreationEvent := event.Event{
Block: ccp.Block,
ContractAddress: ccp.ContractAddress,
Success: ccp.Success,
Timestamp: ccp.Timestamp,
TxHash: ccp.TxHash,
TxType: contractCreationEventName,
Payload: map[string]any{
"from": ccp.From,
},
}
if err := hc.cache.Add(ctx, ccp.ContractAddress); err != nil {
return err
}
return c(ctx, contractCreationEvent)
}
}

View File

@ -0,0 +1,66 @@
package handler
import (
"context"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const custodialRegistrationEventName = "CUSTODIAL_REGISTRATION"
var (
custodialRegistrationEvent = w3.MustNewEvent("NewRegistration(address indexed subject)")
custodialRegistrationSig = w3.MustNewFunc("register(address)", "")
)
func HandleCustodialRegistrationLog() router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var account common.Address
if err := custodialRegistrationEvent.DecodeArgs(lp.Log, &account); err != nil {
return err
}
custodialRegistrationEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: custodialRegistrationEventName,
Payload: map[string]any{
"account": account.Hex(),
},
}
return c(ctx, custodialRegistrationEvent)
}
}
func HandleCustodialRegistrationInputData() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
var account common.Address
if err := custodialRegistrationSig.DecodeArgs(w3.B(idp.InputData), &account); err != nil {
return err
}
custodialRegistrationEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: custodialRegistrationEventName,
Payload: map[string]any{
"account": account.Hex(),
},
}
return c(ctx, custodialRegistrationEvent)
}
}

View File

@ -0,0 +1,91 @@
package handler
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/grassrootseconomics/ethutils"
"github.com/lmittmann/w3"
)
const faucetGiveEventName = "FAUCET_GIVE"
var (
faucetGiveEvent = w3.MustNewEvent("Give(address indexed _recipient, address indexed _token, uint256 _amount)")
faucetGiveToSig = w3.MustNewFunc("giveTo(address)", "uint256")
faucetGimmeSig = w3.MustNewFunc("gimme()", "uint256")
)
func HandleFaucetGiveLog() router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var (
recipient common.Address
token common.Address
amount big.Int
)
if err := faucetGiveEvent.DecodeArgs(lp.Log, &recipient, &token, &amount); err != nil {
return err
}
faucetGiveEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: faucetGiveEventName,
Payload: map[string]any{
"recipient": recipient.Hex(),
"token": token.Hex(),
"amount": amount.String(),
},
}
return c(ctx, faucetGiveEvent)
}
}
func HandleFaucetGiveInputData() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
faucetGiveEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: faucetGiveEventName,
}
switch idp.InputData[:8] {
case "63e4bff4":
var to common.Address
if err := faucetGiveToSig.DecodeArgs(w3.B(idp.InputData), &to); err != nil {
return err
}
faucetGiveEvent.Payload = map[string]any{
"recipient": to.Hex(),
"token": ethutils.ZeroAddress,
"amount": "0",
}
return c(ctx, faucetGiveEvent)
case "de82efb4":
faucetGiveEvent.Payload = map[string]any{
"recipient": ethutils.ZeroAddress,
"token": ethutils.ZeroAddress,
"amount": "0",
}
return c(ctx, faucetGiveEvent)
}
return nil
}
}

View File

@ -0,0 +1,13 @@
package handler
import "github.com/grassrootseconomics/eth-tracker/internal/cache"
type HandlerContainer struct {
cache cache.Cache
}
func New(cacheProvider cache.Cache) *HandlerContainer {
return &HandlerContainer{
cache: cacheProvider,
}
}

View File

@ -0,0 +1,89 @@
package handler
import (
"context"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const indexAddEventName = "INDEX_ADD"
var (
indexAddEvent = w3.MustNewEvent("AddressAdded(address _token)")
indexAddSig = w3.MustNewFunc("add(address)", "bool")
indexRegisterSig = w3.MustNewFunc("register(address)", "bool")
)
func HandleIndexAddLog(hc *HandlerContainer) router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var address common.Address
if err := indexAddEvent.DecodeArgs(lp.Log, &address); err != nil {
return err
}
indexAddEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: indexAddEventName,
Payload: map[string]any{
"address": address.Hex(),
},
}
if err := hc.cache.Add(ctx, address.Hex()); err != nil {
return err
}
return c(ctx, indexAddEvent)
}
}
func HandleIndexAddInputData() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
indexAddEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: indexAddEventName,
}
switch idp.InputData[:8] {
case "0a3b0a4f":
var address common.Address
indexAddEvent.Payload = map[string]any{
"address": address.Hex(),
}
if err := indexAddSig.DecodeArgs(w3.B(idp.InputData), &address); err != nil {
return err
}
return c(ctx, indexAddEvent)
case "4420e486":
var address common.Address
indexAddEvent.Payload = map[string]any{
"address": address.Hex(),
}
if err := indexRegisterSig.DecodeArgs(w3.B(idp.InputData), &address); err != nil {
return err
}
return c(ctx, indexAddEvent)
}
return nil
}
}

View File

@ -0,0 +1,70 @@
package handler
import (
"context"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const indexRemoveEventName = "INDEX_REMOVE"
var (
indexRemoveEvent = w3.MustNewEvent("AddressRemoved(address _token)")
indexRemoveSig = w3.MustNewFunc("remove(address)", "bool")
)
func HandleIndexRemoveLog(hc *HandlerContainer) router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var address common.Address
if err := indexRemoveEvent.DecodeArgs(lp.Log, &address); err != nil {
return err
}
indexRemoveEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: indexRemoveEventName,
Payload: map[string]any{
"address": address.Hex(),
},
}
if err := hc.cache.Remove(ctx, address.Hex()); err != nil {
return err
}
return c(ctx, indexRemoveEvent)
}
}
func HandleIndexRemoveInputData() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
var address common.Address
if err := indexRemoveSig.DecodeArgs(w3.B(idp.InputData), &address); err != nil {
return err
}
indexRemoveEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: indexRemoveEventName,
Payload: map[string]any{
"address": address.Hex(),
},
}
return c(ctx, indexRemoveEvent)
}
}

View File

@ -0,0 +1,71 @@
package handler
import (
"context"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const ownershipEventName = "OWNERSHIP_TRANSFERRED"
var (
ownershipEvent = w3.MustNewEvent("OwnershipTransferred(address indexed previousOwner, address indexed newOwner)")
ownershipToSig = w3.MustNewFunc("transferOwnership(address)", "bool")
)
func HandleOwnershipLog() router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var (
previousOwner common.Address
newOwner common.Address
)
if err := ownershipEvent.DecodeArgs(lp.Log, &previousOwner, &newOwner); err != nil {
return err
}
ownershipEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: ownershipEventName,
Payload: map[string]any{
"previousOwner": previousOwner.Hex(),
"newOwner": newOwner.Hex(),
},
}
return c(ctx, ownershipEvent)
}
}
func HandleOwnershipInputData() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
var newOwner common.Address
if err := ownershipToSig.DecodeArgs(w3.B(idp.InputData), &newOwner); err != nil {
return err
}
ownershipEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: ownershipEventName,
Payload: map[string]any{
"previousOwner": idp.From,
"newOwner": newOwner.Hex(),
},
}
return c(ctx, ownershipEvent)
}
}

View File

@ -0,0 +1,83 @@
package handler
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const poolDepositEventName = "POOL_DEPOSIT"
var (
poolDepositEvent = w3.MustNewEvent("Deposit(address indexed initiator, address indexed tokenIn, uint256 amountIn)")
poolDepositSig = w3.MustNewFunc("deposit(address, uint256)", "")
)
func HandlePoolDepositLog() router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var (
initiator common.Address
tokenIn common.Address
amountIn big.Int
)
if err := poolDepositEvent.DecodeArgs(
lp.Log,
&initiator,
&tokenIn,
&amountIn,
); err != nil {
return err
}
poolDepositEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: poolDepositEventName,
Payload: map[string]any{
"initiator": initiator.Hex(),
"tokenIn": tokenIn.Hex(),
"amountIn": amountIn.String(),
},
}
return c(ctx, poolDepositEvent)
}
}
func HandlePoolDepositInputData() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
var (
tokenIn common.Address
amountIn big.Int
)
if err := poolDepositSig.DecodeArgs(w3.B(idp.InputData), &tokenIn, &amountIn); err != nil {
return err
}
poolDepositEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: poolDepositEventName,
Payload: map[string]any{
"initiator": idp.From,
"tokenIn": tokenIn.Hex(),
"amountIn": amountIn.String(),
},
}
return c(ctx, poolDepositEvent)
}
}

View File

@ -0,0 +1,96 @@
package handler
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const poolSwapEventName = "POOL_SWAP"
var (
poolSwapEvent = w3.MustNewEvent("Swap(address indexed initiator, address indexed tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut, uint256 fee)")
poolSwapSig = w3.MustNewFunc("withdraw(address, address, uint256)", "")
)
func HandlePoolSwapLog() router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var (
initiator common.Address
tokenIn common.Address
tokenOut common.Address
amountIn big.Int
amountOut big.Int
fee big.Int
)
if err := poolSwapEvent.DecodeArgs(
lp.Log,
&initiator,
&tokenIn,
&tokenOut,
&amountIn,
&amountOut,
&fee,
); err != nil {
return err
}
poolSwapEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: poolSwapEventName,
Payload: map[string]any{
"initiator": initiator.Hex(),
"tokenIn": tokenIn.Hex(),
"tokenOut": tokenOut.Hex(),
"amountIn": amountIn.String(),
"amountOut": amountOut.String(),
"fee": fee.String(),
},
}
return c(ctx, poolSwapEvent)
}
}
func HandlePoolSwapInputData() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
var (
tokenOut common.Address
tokenIn common.Address
amountIn big.Int
)
if err := poolSwapSig.DecodeArgs(w3.B(idp.InputData), &tokenOut, &tokenIn, &amountIn); err != nil {
return err
}
poolSwapEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: poolSwapEventName,
Payload: map[string]any{
"initiator": idp.From,
"tokenIn": tokenIn.Hex(),
"tokenOut": tokenOut.Hex(),
"amountIn": amountIn.String(),
"amountOut": "0",
"fee": "0",
},
}
return c(ctx, poolSwapEvent)
}
}

View File

@ -0,0 +1,75 @@
package handler
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const quoterPriceEventName = "QUOTER_PRICE_INDEX_UPDATED"
var (
quoterPriceEvent = w3.MustNewEvent("PriceIndexUpdated(address _tokenAddress, uint256 _exchangeRate)")
quoterPriceToSig = w3.MustNewFunc("setPriceIndexValue(address, uint256)", "uint256")
)
func HandleQuoterPriceUpdateLog() router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var (
token common.Address
exchangeRate big.Int
)
if err := quoterPriceEvent.DecodeArgs(lp.Log, &token, &exchangeRate); err != nil {
return err
}
quoterPriceEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: quoterPriceEventName,
Payload: map[string]any{
"token": token.Hex(),
"exchangeRate": exchangeRate.String(),
},
}
return c(ctx, quoterPriceEvent)
}
}
func HandleQuoterPriceUpdateInputdata() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
var (
token common.Address
exchangeRate big.Int
)
if err := quoterPriceToSig.DecodeArgs(w3.B(idp.InputData), &token, &exchangeRate); err != nil {
return err
}
quoterPriceEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: quoterPriceEventName,
Payload: map[string]any{
"token": token.Hex(),
"exchangeRate": exchangeRate.String(),
},
}
return c(ctx, quoterPriceEvent)
}
}

View File

@ -0,0 +1,66 @@
package handler
import (
"context"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const quoterUpdatedEventName = "QUOTER_UPDATED"
var (
quoterUpdatedEvent = w3.MustNewEvent("QuoterUpdated(address indexed newQuoter)")
quoterUpdatedSig = w3.MustNewFunc("setQuoter(address)", "")
)
func HandleQuoterUpdatedLog() router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var newQuoter common.Address
if err := quoterUpdatedEvent.DecodeArgs(lp.Log, &newQuoter); err != nil {
return err
}
quoterUpdatedEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: quoterUpdatedEventName,
Payload: map[string]any{
"newQuoter": newQuoter.Hex(),
},
}
return c(ctx, quoterUpdatedEvent)
}
}
func HandleQuoterUpdatedInputData() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
var newQuoter common.Address
if err := quoterUpdatedSig.DecodeArgs(w3.B(idp.InputData), &newQuoter); err != nil {
return err
}
quoterUpdatedEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: quoterUpdatedEventName,
Payload: map[string]any{
"newQuoter": newQuoter.Hex(),
},
}
return c(ctx, quoterUpdatedEvent)
}
}

70
internal/handler/seal.go Normal file
View File

@ -0,0 +1,70 @@
package handler
import (
"context"
"math/big"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const sealEventName = "SEAL_STATE_CHANGE"
var (
sealEvent = w3.MustNewEvent("SealStateChange(bool indexed _final, uint256 _sealState)")
sealToSig = w3.MustNewFunc("seal(uint256)", "uint256")
)
func HandleSealStateChangeLog() router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var (
final bool
sealState big.Int
)
if err := sealEvent.DecodeArgs(lp.Log, &final, &sealState); err != nil {
return err
}
sealEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: sealEventName,
Payload: map[string]any{
"final": final,
"sealState": sealState.String(),
},
}
return c(ctx, sealEvent)
}
}
func HandleSealStateChangeInputData() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
var sealState big.Int
if err := sealToSig.DecodeArgs(w3.B(idp.InputData), &sealState); err != nil {
return err
}
sealEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: sealEventName,
Payload: map[string]any{
"sealState": sealState.String(),
},
}
return c(ctx, sealEvent)
}
}

View File

@ -0,0 +1,94 @@
package handler
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const approveEventName = "TOKEN_APPROVE"
var (
tokenApproveEvent = w3.MustNewEvent("Approval(address indexed _owner, address indexed _spender, uint256 _value)")
tokenApproveToSig = w3.MustNewFunc("approve(address, uint256)", "bool")
)
func HandleTokenApproveLog(hc *HandlerContainer) router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var (
owner common.Address
spender common.Address
value big.Int
)
if err := tokenApproveEvent.DecodeArgs(lp.Log, &owner, &spender, &value); err != nil {
return err
}
proceed, err := hc.checkWithinNetwork(ctx, lp.Log.Address.Hex(), owner.Hex(), spender.Hex())
if err != nil {
return err
}
if !proceed {
return nil
}
tokenApproveEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: approveEventName,
Payload: map[string]any{
"owner": owner.Hex(),
"spender": spender.Hex(),
"value": value.String(),
},
}
return c(ctx, tokenApproveEvent)
}
}
func HandleTokenApproveInputData(hc *HandlerContainer) router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
var (
spender common.Address
value big.Int
)
if err := tokenApproveToSig.DecodeArgs(w3.B(idp.InputData), &spender, &value); err != nil {
return err
}
proceed, err := hc.checkWithinNetwork(ctx, idp.ContractAddress, idp.From, spender.Hex())
if err != nil {
return err
}
if !proceed {
return nil
}
tokenApproveEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: approveEventName,
Payload: map[string]any{
"owner": idp.From,
"spender": spender.Hex(),
"value": value.String(),
},
}
return c(ctx, tokenApproveEvent)
}
}

View File

@ -0,0 +1,72 @@
package handler
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const burnEventName = "TOKEN_BURN"
var (
tokenBurnEvent = w3.MustNewEvent("Burn(address indexed _tokenBurner, uint256 _value)")
tokenBurnToSig = w3.MustNewFunc("burn(uint256)", "bool")
)
func HandleTokenBurnLog() router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var (
tokenBurner common.Address
value big.Int
)
if err := tokenBurnEvent.DecodeArgs(lp.Log, &tokenBurner, &value); err != nil {
return err
}
tokenBurnEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: burnEventName,
Payload: map[string]any{
"tokenBurner": tokenBurner.Hex(),
"value": value.String(),
},
}
return c(ctx, tokenBurnEvent)
}
}
func HandleTokenBurnInputData() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
var value big.Int
if err := tokenBurnToSig.DecodeArgs(w3.B(idp.InputData), &value); err != nil {
return err
}
tokenBurnEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: burnEventName,
Payload: map[string]any{
"tokenBurner": idp.From,
"value": value.String(),
},
}
return c(ctx, tokenBurnEvent)
}
}

View File

@ -0,0 +1,78 @@
package handler
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const mintEventName = "TOKEN_MINT"
var (
tokenMintEvent = w3.MustNewEvent("Mint(address indexed _tokenMinter, address indexed _beneficiary, uint256 _value)")
tokenMintToSig = w3.MustNewFunc("mintTo(address, uint256)", "bool")
)
func HandleTokenMintLog() router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var (
tokenMinter common.Address
to common.Address
value big.Int
)
if err := tokenMintEvent.DecodeArgs(lp.Log, &tokenMinter, &to, &value); err != nil {
return err
}
tokenMintEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: mintEventName,
Payload: map[string]any{
"tokenMinter": tokenMinter.Hex(),
"to": to.Hex(),
"value": value.String(),
},
}
return c(ctx, tokenMintEvent)
}
}
func HandleTokenMintInputData() router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
var (
to common.Address
value big.Int
)
if err := tokenMintToSig.DecodeArgs(w3.B(idp.InputData), &to, &value); err != nil {
return err
}
tokenMintEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: mintEventName,
Payload: map[string]any{
"tokenMinter": idp.From,
"to": to.Hex(),
"value": value.String(),
},
}
return c(ctx, tokenMintEvent)
}
}

View File

@ -0,0 +1,170 @@
package handler
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
"github.com/lmittmann/w3"
)
const transferEventName = "TOKEN_TRANSFER"
var (
tokenTransferEvent = w3.MustNewEvent("Transfer(address indexed _from, address indexed _to, uint256 _value)")
tokenTransferFromEvent = w3.MustNewEvent("TransferFrom(address indexed _from, address indexed _to, address indexed _spender, uint256 _value)")
tokenTransferSig = w3.MustNewFunc("transfer(address, uint256)", "bool")
tokenTransferFromSig = w3.MustNewFunc("transferFrom(address, address, uint256)", "bool")
)
func HandleTokenTransferLog(hc *HandlerContainer) router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var (
from common.Address
to common.Address
value big.Int
)
if err := tokenTransferEvent.DecodeArgs(lp.Log, &from, &to, &value); err != nil {
return err
}
proceed, err := hc.checkWithinNetwork(ctx, lp.Log.Address.Hex(), from.Hex(), to.Hex())
if err != nil {
return err
}
if !proceed {
return nil
}
tokenTransferEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: transferEventName,
Payload: map[string]any{
"from": from.Hex(),
"to": to.Hex(),
"value": value.String(),
},
}
return c(ctx, tokenTransferEvent)
}
}
func HandleTokenTransferInputData(hc *HandlerContainer) router.InputDataHandlerFunc {
return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error {
tokenTransferEvent := event.Event{
Block: idp.Block,
ContractAddress: idp.ContractAddress,
Success: false,
Timestamp: idp.Timestamp,
TxHash: idp.TxHash,
TxType: transferEventName,
}
switch idp.InputData[:8] {
case "a9059cbb":
var (
to common.Address
value big.Int
)
if err := tokenTransferSig.DecodeArgs(w3.B(idp.InputData), &to, &value); err != nil {
return err
}
proceed, err := hc.checkWithinNetwork(ctx, idp.ContractAddress, idp.From, to.Hex())
if err != nil {
return err
}
if !proceed {
return nil
}
tokenTransferEvent.Payload = map[string]any{
"from": idp.From,
"to": to.Hex(),
"value": value.String(),
}
return c(ctx, tokenTransferEvent)
case "23b872dd":
var (
from common.Address
to common.Address
value big.Int
)
if err := tokenTransferFromSig.DecodeArgs(w3.B(idp.InputData), &from, &to, &value); err != nil {
return err
}
proceed, err := hc.checkWithinNetwork(ctx, idp.ContractAddress, from.Hex(), to.Hex())
if err != nil {
return err
}
if !proceed {
return nil
}
tokenTransferEvent.Payload = map[string]any{
"from": from.Hex(),
"to": to.Hex(),
"value": value.String(),
}
return c(ctx, tokenTransferEvent)
}
return nil
}
}
func HandleTokenTransferFromLog(hc *HandlerContainer) router.LogHandlerFunc {
return func(ctx context.Context, lp router.LogPayload, c router.Callback) error {
var (
from common.Address
to common.Address
spender common.Address
value big.Int
)
if err := tokenTransferFromEvent.DecodeArgs(lp.Log, &from, &to, &spender, &value); err != nil {
return err
}
proceed, err := hc.checkWithinNetwork(ctx, lp.Log.Address.Hex(), from.Hex(), to.Hex())
if err != nil {
return err
}
if !proceed {
return nil
}
tokenTransferFromEvent := event.Event{
Index: lp.Log.Index,
Block: lp.Log.BlockNumber,
ContractAddress: lp.Log.Address.Hex(),
Success: true,
Timestamp: lp.Timestamp,
TxHash: lp.Log.TxHash.Hex(),
TxType: transferEventName,
Payload: map[string]any{
"from": from.Hex(),
"to": to.Hex(),
"spender": spender.Hex(),
"value": value.String(),
},
}
return c(ctx, tokenTransferFromEvent)
}
}

View File

@ -0,0 +1,12 @@
package handler
import "context"
func (hc *HandlerContainer) checkWithinNetwork(ctx context.Context, contractAddress string, from string, to string) (bool, error) {
exists, err := hc.cache.ExistsNetwork(ctx, contractAddress, from, to)
if err != nil {
return false, err
}
return exists, nil
}

View File

@ -3,10 +3,9 @@ package pool
import (
"context"
"log/slog"
"runtime/debug"
"github.com/alitto/pond"
"github.com/grassrootseconomics/celo-tracker/internal/processor"
"github.com/alitto/pond/v2"
"github.com/grassrootseconomics/eth-tracker/internal/processor"
)
type (
@ -18,21 +17,16 @@ type (
Pool struct {
logg *slog.Logger
workerPool *pond.WorkerPool
workerPool pond.Pool
processor *processor.Processor
}
)
const blocksBuffer = 100
func New(o PoolOpts) *Pool {
return &Pool{
logg: o.Logg,
workerPool: pond.New(
workerPool: pond.NewPool(
o.WorkerCount,
blocksBuffer,
pond.Strategy(pond.Balanced()),
pond.PanicHandler(panicHandler(o.Logg)),
),
processor: o.Processor,
}
@ -42,6 +36,7 @@ func (p *Pool) Stop() {
p.workerPool.StopAndWait()
}
// non-blocking
func (p *Pool) Push(block uint64) {
p.workerPool.Submit(func() {
err := p.processor.ProcessBlock(context.Background(), block)
@ -55,12 +50,6 @@ func (p *Pool) Size() uint64 {
return p.workerPool.WaitingTasks()
}
func (p *Pool) ActiveWorkers() int {
func (p *Pool) ActiveWorkers() int64 {
return p.workerPool.RunningWorkers()
}
func panicHandler(logg *slog.Logger) func(interface{}) {
return func(panic interface{}) {
logg.Error("block processor goroutine exited from a panic", "error", panic, "stack_trace", string(debug.Stack()))
}
}

View File

@ -6,12 +6,12 @@ import (
"fmt"
"log/slog"
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/core/types"
"github.com/grassrootseconomics/celo-tracker/db"
"github.com/grassrootseconomics/celo-tracker/internal/cache"
"github.com/grassrootseconomics/celo-tracker/internal/chain"
"github.com/grassrootseconomics/celo-tracker/internal/router"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/grassrootseconomics/eth-tracker/db"
"github.com/grassrootseconomics/eth-tracker/internal/cache"
"github.com/grassrootseconomics/eth-tracker/internal/chain"
"github.com/grassrootseconomics/eth-tracker/pkg/router"
)
type (
@ -54,31 +54,90 @@ func (p *Processor) ProcessBlock(ctx context.Context, blockNumber uint64) error
}
for _, receipt := range receipts {
if receipt.Status > 0 {
if receipt.Status == 1 {
for _, log := range receipt.Logs {
exists, err := p.cache.Exists(ctx, log.Address.Hex())
if err != nil {
return err
}
if exists {
if err := p.router.RouteSuccessTx(
if err := p.router.ProcessLog(
ctx,
router.SuccessTx{
router.LogPayload{
Log: log,
Timestamp: block.Time(),
},
); err != nil && !errors.Is(err, context.Canceled) {
return err
return fmt.Errorf("route success transaction error: tx %s: %v", receipt.TxHash.Hex(), err)
}
}
}
} else {
if receipt.ContractAddress != (common.Address{}) {
tx, err := p.chain.GetTransaction(ctx, receipt.TxHash)
if err != nil && !errors.Is(err, context.Canceled) {
return fmt.Errorf("get transaction error: tx %s: %v", receipt.TxHash.Hex(), err)
}
if tx.To() != nil {
from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx)
if err != nil {
return fmt.Errorf("transaction decode error: tx %s: %v", receipt.TxHash.Hex(), err)
}
exists, err := p.cache.Exists(ctx, from.Hex())
if err != nil {
return err
}
if exists {
if err := p.router.ProcessContractCreation(
ctx,
router.ContractCreationPayload{
From: from.Hex(),
Block: blockNumber,
ContractAddress: receipt.ContractAddress.Hex(),
Timestamp: block.Time(),
TxHash: receipt.TxHash.Hex(),
Success: true,
},
); err != nil && !errors.Is(err, context.Canceled) {
return fmt.Errorf("route success contract creation error: tx %s: %v", receipt.TxHash.Hex(), err)
}
}
}
}
if receipt.Status == 0 {
tx, err := p.chain.GetTransaction(ctx, receipt.TxHash)
if err != nil && !errors.Is(err, context.Canceled) {
return fmt.Errorf("get transaction error: tx %s: %v", receipt.TxHash.Hex(), err)
}
if tx.To() == nil {
from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx)
if err != nil {
return fmt.Errorf("transaction decode error: tx %s: %v", receipt.TxHash.Hex(), err)
}
exists, err := p.cache.Exists(ctx, from.Hex())
if err != nil {
return err
}
if exists {
if err := p.router.ProcessContractCreation(
ctx,
router.ContractCreationPayload{
From: from.Hex(),
Block: blockNumber,
ContractAddress: receipt.ContractAddress.Hex(),
Timestamp: block.Time(),
TxHash: receipt.TxHash.Hex(),
Success: false,
},
); err != nil && !errors.Is(err, context.Canceled) {
return fmt.Errorf("route reverted contract creation error: tx %s: %v", receipt.TxHash.Hex(), err)
}
}
} else {
exists, err := p.cache.Exists(ctx, tx.To().Hex())
if err != nil {
return err
@ -89,9 +148,9 @@ func (p *Processor) ProcessBlock(ctx context.Context, blockNumber uint64) error
return fmt.Errorf("transaction decode error: tx %s: %v", receipt.TxHash.Hex(), err)
}
if err := p.router.RouteRevertTx(
if err := p.router.ProcessInputData(
ctx,
router.RevertTx{
router.InputDataPayload{
From: from.Hex(),
InputData: common.Bytes2Hex(tx.Data()),
Block: blockNumber,
@ -100,7 +159,7 @@ func (p *Processor) ProcessBlock(ctx context.Context, blockNumber uint64) error
TxHash: receipt.TxHash.Hex(),
},
); err != nil && !errors.Is(err, context.Canceled) {
return err
return fmt.Errorf("route revert transaction error: tx %s: %v", receipt.TxHash.Hex(), err)
}
}
}

View File

@ -2,26 +2,25 @@ package pub
import (
"context"
"errors"
"fmt"
"log/slog"
"time"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
"github.com/nats-io/nats.go"
"github.com/nats-io/nats.go/jetstream"
)
type (
JetStreamOpts struct {
Logg *slog.Logger
Endpoint string
DedupDuration time.Duration
PersistDuration time.Duration
Logg *slog.Logger
}
jetStreamPub struct {
js jetstream.JetStream
natsConn *nats.Conn
jsCtx nats.JetStreamContext
}
)
@ -37,33 +36,25 @@ func NewJetStreamPub(o JetStreamOpts) (Pub, error) {
return nil, err
}
js, err := natsConn.JetStream()
js, err := jetstream.New(natsConn)
if err != nil {
return nil, err
}
o.Logg.Info("successfully connected to NATS server")
stream, err := js.StreamInfo(streamName)
if err != nil && !errors.Is(err, nats.ErrStreamNotFound) {
return nil, err
}
if stream == nil {
_, err := js.AddStream(&nats.StreamConfig{
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
js.CreateStream(ctx, jetstream.StreamConfig{
Name: streamName,
MaxAge: o.PersistDuration,
Storage: nats.FileStorage,
Subjects: streamSubjects,
Duplicates: o.DedupDuration,
MaxAge: o.PersistDuration,
Storage: jetstream.FileStorage,
Duplicates: time.Minute * 20,
})
if err != nil {
return nil, err
}
o.Logg.Info("successfully created NATS JetStream stream", "stream_name", streamName)
}
return &jetStreamPub{
natsConn: natsConn,
jsCtx: js,
js: js,
}, nil
}
@ -73,16 +64,17 @@ func (p *jetStreamPub) Close() {
}
}
func (p *jetStreamPub) Send(_ context.Context, payload event.Event) error {
func (p *jetStreamPub) Send(ctx context.Context, payload event.Event) error {
data, err := payload.Serialize()
if err != nil {
return err
}
_, err = p.jsCtx.Publish(
_, err = p.js.Publish(
ctx,
fmt.Sprintf("%s.%s", streamName, payload.TxType),
data,
nats.MsgId(fmt.Sprintf("%s:%d", payload.TxHash, payload.Index)),
jetstream.WithMsgID(fmt.Sprintf("%s:%d", payload.TxHash, payload.Index)),
)
if err != nil {
return err

View File

@ -3,7 +3,7 @@ package pub
import (
"context"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
)
type Pub interface {

View File

@ -1,73 +0,0 @@
package queue
//
// import (
// "context"
// "log/slog"
// "github.com/alitto/pond"
// "github.com/grassrootseconomics/celo-tracker/internal/processor"
// )
// type (
// QueueOpts struct {
// QueueSize int
// Logg *slog.Logger
// Processor *processor.Processor
// Pool *pond.WorkerPool
// }
// Queue struct {
// logg *slog.Logger
// processChan chan uint64
// stopSignal chan interface{}
// processor *processor.Processor
// pool *pond.WorkerPool
// }
// )
// func New(o QueueOpts) *Queue {
// return &Queue{
// logg: o.Logg,
// processChan: make(chan uint64, o.QueueSize),
// stopSignal: make(chan interface{}),
// processor: o.Processor,
// pool: o.Pool,
// }
// }
// func (q *Queue) Stop() {
// q.stopSignal <- struct{}{}
// }
// func (q *Queue) Process() {
// for {
// select {
// case <-q.stopSignal:
// q.logg.Info("shutdown signal received stopping queue processing")
// return
// case block, ok := <-q.processChan:
// if !ok {
// return
// }
// q.pool.Submit(func() {
// err := q.processor.ProcessBlock(context.Background(), block)
// if err != nil {
// q.logg.Error("block processor error", "block_number", block, "error", err)
// }
// })
// }
// }
// }
// func (q *Queue) Push(block uint64) {
// q.processChan <- block
// }
// func (q *Queue) Size() int {
// return len(q.processChan)
// }
// func (q *Queue) WaitingSize() uint64 {
// return q.pool.WaitingTasks()
// }

View File

@ -1,94 +0,0 @@
package router
import (
"context"
"math/big"
"github.com/celo-org/celo-blockchain/common"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/celoutils/v3"
"github.com/grassrootseconomics/w3-celo"
)
type faucetGiveHandler struct{}
const faucetGiveEventName = "FAUCET_GIVE"
var (
_ Handler = (*faucetGiveHandler)(nil)
faucetGiveEvent = w3.MustNewEvent("Give(address indexed _recipient, address indexed _token, uint256 _amount)")
faucetGiveToSig = w3.MustNewFunc("giveTo(address)", "uint256")
faucetGimmeSig = w3.MustNewFunc("gimme()", "uint256")
)
func (h *faucetGiveHandler) Name() string {
return faucetGiveEventName
}
func (h *faucetGiveHandler) SuccessTx(ctx context.Context, tx SuccessTx, pubCB PubCallback) error {
var (
recipient common.Address
token common.Address
amount big.Int
)
if err := faucetGiveEvent.DecodeArgs(tx.Log, &recipient, &token, &amount); err != nil {
return err
}
faucetGiveEvent := event.Event{
Index: tx.Log.Index,
Block: tx.Log.BlockNumber,
ContractAddress: tx.Log.Address.Hex(),
Success: true,
Timestamp: tx.Timestamp,
TxHash: tx.Log.TxHash.Hex(),
TxType: faucetGiveEventName,
Payload: map[string]any{
"recipient": recipient.Hex(),
"token": token.Hex(),
"amount": amount.String(),
},
}
return pubCB(ctx, faucetGiveEvent)
}
func (h *faucetGiveHandler) RevertTx(ctx context.Context, tx RevertTx, pubCB PubCallback) error {
faucetGiveEvent := event.Event{
Block: tx.Block,
ContractAddress: tx.ContractAddress,
Success: false,
Timestamp: tx.Timestamp,
TxHash: tx.TxHash,
TxType: faucetGiveEventName,
}
switch tx.InputData[:8] {
case "63e4bff4":
var to common.Address
if err := faucetGiveToSig.DecodeArgs(w3.B(tx.InputData), &to); err != nil {
return err
}
faucetGiveEvent.Payload = map[string]any{
"recipient": to.Hex(),
"token": celoutils.ZeroAddress,
"amount": "0",
}
return pubCB(ctx, faucetGiveEvent)
case "de82efb4":
faucetGiveEvent.Payload = map[string]any{
"recipient": celoutils.ZeroAddress,
"token": celoutils.ZeroAddress,
"amount": "0",
}
return pubCB(ctx, faucetGiveEvent)
}
return nil
}

View File

@ -1,95 +0,0 @@
package router
import (
"context"
"github.com/celo-org/celo-blockchain/common"
"github.com/grassrootseconomics/celo-tracker/internal/cache"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/w3-celo"
)
type indexAddHandler struct {
cache cache.Cache
}
const indexAddEventName = "INDEX_ADD"
var (
_ Handler = (*indexAddHandler)(nil)
indexAddEvent = w3.MustNewEvent("AddressAdded(address _token)")
indexAddSig = w3.MustNewFunc("add(address)", "bool")
indexRegisterSig = w3.MustNewFunc("register(address)", "bool")
)
func (h *indexAddHandler) Name() string {
return indexAddEventName
}
func (h *indexAddHandler) SuccessTx(ctx context.Context, tx SuccessTx, pubCB PubCallback) error {
var address common.Address
if err := indexAddEvent.DecodeArgs(tx.Log, &address); err != nil {
return err
}
indexAddEvent := event.Event{
Index: tx.Log.Index,
Block: tx.Log.BlockNumber,
ContractAddress: tx.Log.Address.Hex(),
Success: true,
Timestamp: tx.Timestamp,
TxHash: tx.Log.TxHash.Hex(),
TxType: indexAddEventName,
Payload: map[string]any{
"address": address.Hex(),
},
}
if err := h.cache.Add(ctx, address.Hex()); err != nil {
return err
}
return pubCB(ctx, indexAddEvent)
}
func (h *indexAddHandler) RevertTx(ctx context.Context, tx RevertTx, pubCB PubCallback) error {
indexAddEvent := event.Event{
Block: tx.Block,
ContractAddress: tx.ContractAddress,
Success: false,
Timestamp: tx.Timestamp,
TxHash: tx.TxHash,
TxType: indexAddEventName,
}
switch tx.InputData[:8] {
case "0a3b0a4f":
var address common.Address
indexAddEvent.Payload = map[string]any{
"address": address.Hex(),
}
if err := indexAddSig.DecodeArgs(w3.B(tx.InputData), &address); err != nil {
return err
}
return pubCB(ctx, indexAddEvent)
case "4420e486":
var address common.Address
indexAddEvent.Payload = map[string]any{
"address": address.Hex(),
}
if err := indexRegisterSig.DecodeArgs(w3.B(tx.InputData), &address); err != nil {
return err
}
return pubCB(ctx, indexAddEvent)
}
return nil
}

View File

@ -1,77 +0,0 @@
package router
import (
"context"
"github.com/celo-org/celo-blockchain/common"
"github.com/grassrootseconomics/celo-tracker/internal/cache"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/w3-celo"
)
type indexRemoveHandler struct {
cache cache.Cache
}
const indexRemoveEventName = "INDEX_REMOVE"
var (
_ Handler = (*indexRemoveHandler)(nil)
indexRemoveEvent = w3.MustNewEvent("AddressRemoved(address _token)")
indexRemoveSig = w3.MustNewFunc("remove(address)", "bool")
)
func (h *indexRemoveHandler) Name() string {
return indexRemoveEventName
}
func (h *indexRemoveHandler) SuccessTx(ctx context.Context, tx SuccessTx, pubCB PubCallback) error {
var address common.Address
if err := indexRemoveEvent.DecodeArgs(tx.Log, &address); err != nil {
return err
}
indexRemoveEvent := event.Event{
Index: tx.Log.Index,
Block: tx.Log.BlockNumber,
ContractAddress: tx.Log.Address.Hex(),
Success: true,
Timestamp: tx.Timestamp,
TxHash: tx.Log.TxHash.Hex(),
TxType: indexRemoveEventName,
Payload: map[string]any{
"address": address.Hex(),
},
}
if err := h.cache.Remove(ctx, address.Hex()); err != nil {
return err
}
return pubCB(ctx, indexRemoveEvent)
}
func (h *indexRemoveHandler) RevertTx(ctx context.Context, tx RevertTx, pubCB PubCallback) error {
var address common.Address
if err := indexRemoveSig.DecodeArgs(w3.B(tx.InputData), &address); err != nil {
return err
}
indexRemoveEvent := event.Event{
Block: tx.Block,
ContractAddress: tx.ContractAddress,
Success: false,
Timestamp: tx.Timestamp,
TxHash: tx.TxHash,
TxType: indexRemoveEventName,
Payload: map[string]any{
"address": address.Hex(),
},
}
return pubCB(ctx, indexRemoveEvent)
}

View File

@ -1,77 +0,0 @@
package router
import (
"context"
"github.com/celo-org/celo-blockchain/common"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/w3-celo"
)
type ownershipHandler struct{}
const (
ownershipEventName = "OWNERSHIP_TRANSFERRED"
)
var (
_ Handler = (*ownershipHandler)(nil)
ownershipEvent = w3.MustNewEvent("OwnershipTransferred(address indexed previousOwner, address indexed newOwner)")
ownershipToSig = w3.MustNewFunc("transferOwnership(address)", "bool")
)
func (h *ownershipHandler) Name() string {
return ownershipEventName
}
func (h *ownershipHandler) SuccessTx(ctx context.Context, tx SuccessTx, pubCB PubCallback) error {
var (
previousOwner common.Address
newOwner common.Address
)
if err := ownershipEvent.DecodeArgs(tx.Log, &previousOwner, &newOwner); err != nil {
return err
}
ownershipEvent := event.Event{
Index: tx.Log.Index,
Block: tx.Log.BlockNumber,
ContractAddress: tx.Log.Address.Hex(),
Success: true,
Timestamp: tx.Timestamp,
TxHash: tx.Log.TxHash.Hex(),
TxType: ownershipEventName,
Payload: map[string]any{
"previousOwner": previousOwner.Hex(),
"newOwner": newOwner.Hex(),
},
}
return pubCB(ctx, ownershipEvent)
}
func (h *ownershipHandler) RevertTx(ctx context.Context, tx RevertTx, pubCB PubCallback) error {
var newOwner common.Address
if err := ownershipToSig.DecodeArgs(w3.B(tx.InputData), &newOwner); err != nil {
return err
}
ownershipEvent := event.Event{
Block: tx.Block,
ContractAddress: tx.ContractAddress,
Success: false,
Timestamp: tx.Timestamp,
TxHash: tx.TxHash,
TxType: ownershipEventName,
Payload: map[string]any{
"previousOwner": tx.From,
"newOwner": newOwner.Hex(),
},
}
return pubCB(ctx, ownershipEvent)
}

View File

@ -1,86 +0,0 @@
package router
import (
"context"
"math/big"
"github.com/celo-org/celo-blockchain/common"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/w3-celo"
)
type poolDepositHandler struct{}
const poolDepositEventName = "POOL_DEPOSIT"
var (
_ Handler = (*poolDepositHandler)(nil)
poolDepositEvent = w3.MustNewEvent("Deposit(address indexed initiator, address indexed tokenIn, uint256 amountIn)")
poolDepositSig = w3.MustNewFunc("deposit(address, uint256)", "")
)
func (h *poolDepositHandler) Name() string {
return poolDepositEventName
}
func (h *poolDepositHandler) SuccessTx(ctx context.Context, tx SuccessTx, pubCB PubCallback) error {
var (
initiator common.Address
tokenIn common.Address
amountIn big.Int
)
if err := poolDepositEvent.DecodeArgs(
tx.Log,
&initiator,
&tokenIn,
&amountIn,
); err != nil {
return err
}
poolDepositEvent := event.Event{
Index: tx.Log.Index,
Block: tx.Log.BlockNumber,
ContractAddress: tx.Log.Address.Hex(),
Success: true,
Timestamp: tx.Timestamp,
TxHash: tx.Log.TxHash.Hex(),
TxType: poolDepositEventName,
Payload: map[string]any{
"initiator": initiator.Hex(),
"tokenIn": tokenIn.Hex(),
"amountIn": amountIn.String(),
},
}
return pubCB(ctx, poolDepositEvent)
}
func (h *poolDepositHandler) RevertTx(ctx context.Context, tx RevertTx, pubCB PubCallback) error {
var (
tokenIn common.Address
amountIn big.Int
)
if err := poolDepositSig.DecodeArgs(w3.B(tx.InputData), &tokenIn, &amountIn); err != nil {
return err
}
poolDepositEvent := event.Event{
Block: tx.Block,
ContractAddress: tx.ContractAddress,
Success: false,
Timestamp: tx.Timestamp,
TxHash: tx.TxHash,
TxType: poolDepositEventName,
Payload: map[string]any{
"initiator": tx.From,
"tokenIn": tokenIn.Hex(),
"amountIn": amountIn.String(),
},
}
return pubCB(ctx, poolDepositEvent)
}

View File

@ -1,99 +0,0 @@
package router
import (
"context"
"math/big"
"github.com/celo-org/celo-blockchain/common"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/w3-celo"
)
type poolSwapHandler struct{}
const poolSwapEventName = "POOL_SWAP"
var (
_ Handler = (*poolSwapHandler)(nil)
poolSwapEvent = w3.MustNewEvent("Swap(address indexed initiator, address indexed tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut, uint256 fee)")
poolSwapSig = w3.MustNewFunc("withdraw(address, address, uint256)", "")
)
func (h *poolSwapHandler) Name() string {
return poolSwapEventName
}
func (h *poolSwapHandler) SuccessTx(ctx context.Context, tx SuccessTx, pubCB PubCallback) error {
var (
initiator common.Address
tokenIn common.Address
tokenOut common.Address
amountIn big.Int
amountOut big.Int
fee big.Int
)
if err := poolSwapEvent.DecodeArgs(
tx.Log,
&initiator,
&tokenIn,
&tokenOut,
&amountIn,
&amountOut,
&fee,
); err != nil {
return err
}
poolSwapEvent := event.Event{
Index: tx.Log.Index,
Block: tx.Log.BlockNumber,
ContractAddress: tx.Log.Address.Hex(),
Success: true,
Timestamp: tx.Timestamp,
TxHash: tx.Log.TxHash.Hex(),
TxType: poolSwapEventName,
Payload: map[string]any{
"initiator": initiator.Hex(),
"tokenIn": tokenIn.Hex(),
"tokenOut": tokenOut.Hex(),
"amountIn": amountIn.String(),
"amountOut": amountOut.String(),
"fee": fee.String(),
},
}
return pubCB(ctx, poolSwapEvent)
}
func (h *poolSwapHandler) RevertTx(ctx context.Context, tx RevertTx, pubCB PubCallback) error {
var (
tokenOut common.Address
tokenIn common.Address
amountIn big.Int
)
if err := poolSwapSig.DecodeArgs(w3.B(tx.InputData), &tokenOut, &tokenIn, &amountIn); err != nil {
return err
}
poolSwapEvent := event.Event{
Block: tx.Block,
ContractAddress: tx.ContractAddress,
Success: false,
Timestamp: tx.Timestamp,
TxHash: tx.TxHash,
TxType: poolSwapEventName,
Payload: map[string]any{
"initiator": tx.From,
"tokenIn": tokenIn.Hex(),
"tokenOut": tokenOut.Hex(),
"amountIn": amountIn.String(),
"amountOut": "0",
"fee": "0",
},
}
return pubCB(ctx, poolSwapEvent)
}

View File

@ -1,78 +0,0 @@
package router
import (
"context"
"math/big"
"github.com/celo-org/celo-blockchain/common"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/w3-celo"
)
type quoterPriceHandler struct{}
const quoterPriceEventName = "QUOTER_PRICE_INDEX_UPDATED"
var (
_ Handler = (*quoterPriceHandler)(nil)
quoterPriceEvent = w3.MustNewEvent("PriceIndexUpdated(address _tokenAddress, uint256 _exchangeRate)")
quoterPriceToSig = w3.MustNewFunc("setPriceIndexValue(address, uint256)", "uint256")
)
func (h *quoterPriceHandler) Name() string {
return quoterPriceEventName
}
func (h *quoterPriceHandler) SuccessTx(ctx context.Context, tx SuccessTx, pubCB PubCallback) error {
var (
token common.Address
exchangeRate big.Int
)
if err := quoterPriceEvent.DecodeArgs(tx.Log, &token, &exchangeRate); err != nil {
return err
}
quoterPriceEvent := event.Event{
Index: tx.Log.Index,
Block: tx.Log.BlockNumber,
ContractAddress: tx.Log.Address.Hex(),
Success: true,
Timestamp: tx.Timestamp,
TxHash: tx.Log.TxHash.Hex(),
TxType: quoterPriceEventName,
Payload: map[string]any{
"token": token.Hex(),
"exchangeRate": exchangeRate.String(),
},
}
return pubCB(ctx, quoterPriceEvent)
}
func (h *quoterPriceHandler) RevertTx(ctx context.Context, tx RevertTx, pubCB PubCallback) error {
var (
token common.Address
exchangeRate big.Int
)
if err := quoterPriceToSig.DecodeArgs(w3.B(tx.InputData), &token, &exchangeRate); err != nil {
return err
}
quoterPriceEvent := event.Event{
Block: tx.Block,
ContractAddress: tx.ContractAddress,
Success: false,
Timestamp: tx.Timestamp,
TxHash: tx.TxHash,
TxType: quoterPriceEventName,
Payload: map[string]any{
"token": token.Hex(),
"exchangeRate": exchangeRate.String(),
},
}
return pubCB(ctx, quoterPriceEvent)
}

View File

@ -1,123 +0,0 @@
package router
import (
"context"
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/core/types"
"github.com/grassrootseconomics/celo-tracker/internal/cache"
"github.com/grassrootseconomics/celo-tracker/internal/pub"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/w3-celo"
)
type (
PubCallback func(context.Context, event.Event) error
SuccessTx struct {
Log *types.Log
Timestamp uint64
}
RevertTx struct {
From string
InputData string
Block uint64
ContractAddress string
Timestamp uint64
TxHash string
}
Handler interface {
Name() string
SuccessTx(context.Context, SuccessTx, PubCallback) error
RevertTx(context.Context, RevertTx, PubCallback) error
}
RouterOpts struct {
Pub pub.Pub
Cache cache.Cache
}
Router struct {
pub pub.Pub
cache cache.Cache
logHandlers map[common.Hash]Handler
inputDataHandlers map[string]Handler
}
)
func New(o RouterOpts) *Router {
var (
indexAddHandler *indexAddHandler = &indexAddHandler{cache: o.Cache}
indexRemoveHandler *indexRemoveHandler = &indexRemoveHandler{cache: o.Cache}
tokenTransferHandler *tokenTransferHandler = &tokenTransferHandler{cache: o.Cache}
faucetGiveHandler *faucetGiveHandler = &faucetGiveHandler{}
ownershipHandler *ownershipHandler = &ownershipHandler{}
poolDepositHandler *poolDepositHandler = &poolDepositHandler{}
poolSwapHandler *poolSwapHandler = &poolSwapHandler{}
quoterPriceHandler *quoterPriceHandler = &quoterPriceHandler{}
sealHandler *sealHandler = &sealHandler{}
tokenBurnHandler *tokenBurnHandler = &tokenBurnHandler{}
tokenMintHandler *tokenMintHandler = &tokenMintHandler{}
)
logHandlers := map[common.Hash]Handler{
w3.H("0x26162814817e23ec5035d6a2edc6c422da2da2119e27cfca6be65cc2dc55ca4c"): faucetGiveHandler,
w3.H("0xa226db3f664042183ee0281230bba26cbf7b5057e50aee7f25a175ff45ce4d7f"): indexAddHandler,
w3.H("0x24a12366c02e13fe4a9e03d86a8952e85bb74a456c16e4a18b6d8295700b74bb"): indexRemoveHandler,
w3.H("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0"): ownershipHandler,
w3.H("0x5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f62"): poolDepositHandler,
w3.H("0xd6d34547c69c5ee3d2667625c188acf1006abb93e0ee7cf03925c67cf7760413"): poolSwapHandler,
w3.H("0xdb9ce1a76955721ca61ac50cd1b87f9ab8620325c8619a62192c2dc7871d56b1"): quoterPriceHandler,
w3.H("0x6b7e2e653f93b645d4ed7292d6429f96637084363e477c8aaea1a43ed13c284e"): sealHandler,
w3.H("0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5"): tokenBurnHandler,
w3.H("0xab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f8"): tokenMintHandler,
w3.H("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"): tokenTransferHandler,
}
inputDataHandlers := map[string]Handler{
"63e4bff4": faucetGiveHandler,
"de82efb4": faucetGiveHandler,
"0a3b0a4f": indexAddHandler,
"4420e486": indexAddHandler,
"29092d0e": indexRemoveHandler,
"f2fde38b": ownershipHandler,
"47e7ef24": poolDepositHandler,
"d9caed12": poolSwapHandler,
"ebc59dff": quoterPriceHandler,
"86fe212d": sealHandler,
"42966c68": tokenBurnHandler,
"449a52f8": tokenMintHandler,
"a9059cbb": tokenTransferHandler,
"23b872dd": tokenTransferHandler,
}
return &Router{
pub: o.Pub,
logHandlers: logHandlers,
inputDataHandlers: inputDataHandlers,
}
}
func (r *Router) RouteSuccessTx(ctx context.Context, msg SuccessTx) error {
handler, ok := r.logHandlers[msg.Log.Topics[0]]
if ok {
return handler.SuccessTx(ctx, msg, r.pub.Send)
}
return nil
}
func (r *Router) RouteRevertTx(ctx context.Context, msg RevertTx) error {
if len(msg.InputData) < 8 {
return nil
}
handler, ok := r.inputDataHandlers[msg.InputData[:8]]
if ok {
return handler.RevertTx(ctx, msg, r.pub.Send)
}
return nil
}

View File

@ -1,73 +0,0 @@
package router
import (
"context"
"math/big"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/w3-celo"
)
type sealHandler struct{}
const sealEventName = "SEAL_STATE_CHANGE"
var (
_ Handler = (*sealHandler)(nil)
sealEvent = w3.MustNewEvent("SealStateChange(bool indexed _final, uint256 _sealState)")
sealToSig = w3.MustNewFunc("seal(uint256)", "uint256")
)
func (h *sealHandler) Name() string {
return sealEventName
}
func (h *sealHandler) SuccessTx(ctx context.Context, tx SuccessTx, pubCB PubCallback) error {
var (
final bool
sealState big.Int
)
if err := sealEvent.DecodeArgs(tx.Log, &final, &sealState); err != nil {
return err
}
sealEvent := event.Event{
Index: tx.Log.Index,
Block: tx.Log.BlockNumber,
ContractAddress: tx.Log.Address.Hex(),
Success: true,
Timestamp: tx.Timestamp,
TxHash: tx.Log.TxHash.Hex(),
TxType: sealEventName,
Payload: map[string]any{
"final": final,
"sealState": sealState.String(),
},
}
return pubCB(ctx, sealEvent)
}
func (h *sealHandler) RevertTx(ctx context.Context, tx RevertTx, pubCB PubCallback) error {
var sealState big.Int
if err := sealToSig.DecodeArgs(w3.B(tx.InputData), &sealState); err != nil {
return err
}
sealEvent := event.Event{
Block: tx.Block,
ContractAddress: tx.ContractAddress,
Success: false,
Timestamp: tx.Timestamp,
TxHash: tx.TxHash,
TxType: sealEventName,
Payload: map[string]any{
"sealState": sealState.String(),
},
}
return pubCB(ctx, sealEvent)
}

View File

@ -1,75 +0,0 @@
package router
import (
"context"
"math/big"
"github.com/celo-org/celo-blockchain/common"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/w3-celo"
)
type tokenBurnHandler struct{}
const burnEventName = "TOKEN_BURN"
var (
_ Handler = (*tokenBurnHandler)(nil)
tokenBurnEvent = w3.MustNewEvent("Burn(address indexed _tokenBurner, uint256 _value)")
tokenBurnToSig = w3.MustNewFunc("Burn(uint256)", "bool")
)
func (h *tokenBurnHandler) Name() string {
return burnEventName
}
func (h *tokenBurnHandler) SuccessTx(ctx context.Context, tx SuccessTx, pubCB PubCallback) error {
var (
tokenBurner common.Address
value big.Int
)
if err := tokenBurnEvent.DecodeArgs(tx.Log, &tokenBurner, &value); err != nil {
return err
}
tokenBurnEvent := event.Event{
Index: tx.Log.Index,
Block: tx.Log.BlockNumber,
ContractAddress: tx.Log.Address.Hex(),
Success: true,
Timestamp: tx.Timestamp,
TxHash: tx.Log.TxHash.Hex(),
TxType: burnEventName,
Payload: map[string]any{
"tokenBurner": tokenBurner.Hex(),
"value": value.String(),
},
}
return pubCB(ctx, tokenBurnEvent)
}
func (h *tokenBurnHandler) RevertTx(ctx context.Context, tx RevertTx, pubCB PubCallback) error {
var value big.Int
if err := tokenBurnToSig.DecodeArgs(w3.B(tx.InputData), &value); err != nil {
return err
}
tokenBurnEvent := event.Event{
Block: tx.Block,
ContractAddress: tx.ContractAddress,
Success: false,
Timestamp: tx.Timestamp,
TxHash: tx.TxHash,
TxType: burnEventName,
Payload: map[string]any{
"tokenBurner": tx.From,
"value": value.String(),
},
}
return pubCB(ctx, tokenBurnEvent)
}

View File

@ -1,82 +0,0 @@
package router
import (
"context"
"math/big"
"github.com/celo-org/celo-blockchain/common"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/w3-celo"
)
type tokenMintHandler struct{}
const mintEventName = "TOKEN_MINT"
var (
_ Handler = (*tokenMintHandler)(nil)
tokenMintEvent = w3.MustNewEvent("Mint(address indexed _tokenMinter, address indexed _beneficiary, uint256 _value)")
tokenMintToSig = w3.MustNewFunc("MintTo(address, uint256)", "bool")
)
func (h *tokenMintHandler) Name() string {
return mintEventName
}
func (h *tokenMintHandler) SuccessTx(ctx context.Context, tx SuccessTx, pubCB PubCallback) error {
var (
tokenMinter common.Address
to common.Address
value big.Int
)
if err := tokenMintEvent.DecodeArgs(tx.Log, &tokenMinter, &to, &value); err != nil {
return err
}
tokenMintEvent := event.Event{
Index: tx.Log.Index,
Block: tx.Log.BlockNumber,
ContractAddress: tx.Log.Address.Hex(),
Success: true,
Timestamp: tx.Timestamp,
TxHash: tx.Log.TxHash.Hex(),
TxType: mintEventName,
Payload: map[string]any{
"tokenMinter": tokenMinter.Hex(),
"to": to.Hex(),
"value": value.String(),
},
}
return pubCB(ctx, tokenMintEvent)
}
func (h *tokenMintHandler) RevertTx(ctx context.Context, tx RevertTx, pubCB PubCallback) error {
var (
to common.Address
value big.Int
)
if err := tokenMintToSig.DecodeArgs(w3.B(tx.InputData), &to, &value); err != nil {
return err
}
tokenMintEvent := event.Event{
Block: tx.Block,
ContractAddress: tx.ContractAddress,
Success: false,
Timestamp: tx.Timestamp,
TxHash: tx.TxHash,
TxType: mintEventName,
Payload: map[string]any{
"tokenMinter": tx.From,
"to": to.Hex(),
"value": value.String(),
},
}
return pubCB(ctx, tokenMintEvent)
}

View File

@ -1,161 +0,0 @@
package router
import (
"context"
"math/big"
"github.com/celo-org/celo-blockchain/common"
"github.com/grassrootseconomics/celo-tracker/internal/cache"
"github.com/grassrootseconomics/celo-tracker/pkg/event"
"github.com/grassrootseconomics/celoutils/v3"
"github.com/grassrootseconomics/w3-celo"
)
type tokenTransferHandler struct {
cache cache.Cache
}
const transferEventName = "TOKEN_TRANSFER"
var (
_ Handler = (*tokenTransferHandler)(nil)
tokenTransferEvent = w3.MustNewEvent("Transfer(address indexed _from, address indexed _to, uint256 _value)")
tokenTransferSig = w3.MustNewFunc("transfer(address, uint256)", "bool")
tokenTransferFromSig = w3.MustNewFunc("transferFrom(address, address, uint256)", "bool")
stables = map[string]bool{
celoutils.CUSDContractMainnet: true,
celoutils.CKESContractMainnet: true,
celoutils.USDTContractMainnet: true,
celoutils.USDCContractMainnet: true,
}
)
func (h *tokenTransferHandler) Name() string {
return transferEventName
}
func (h *tokenTransferHandler) SuccessTx(ctx context.Context, tx SuccessTx, pubCB PubCallback) error {
var (
from common.Address
to common.Address
value big.Int
)
if err := tokenTransferEvent.DecodeArgs(tx.Log, &from, &to, &value); err != nil {
return err
}
proceed, err := h.checkStables(ctx, from.Hex(), to.Hex(), tx.Log.Address.Hex())
if err != nil {
return err
}
if !proceed {
return nil
}
tokenTransferEvent := event.Event{
Index: tx.Log.Index,
Block: tx.Log.BlockNumber,
ContractAddress: tx.Log.Address.Hex(),
Success: true,
Timestamp: tx.Timestamp,
TxHash: tx.Log.TxHash.Hex(),
TxType: transferEventName,
Payload: map[string]any{
"from": from.Hex(),
"to": to.Hex(),
"value": value.String(),
},
}
return pubCB(ctx, tokenTransferEvent)
}
func (h *tokenTransferHandler) RevertTx(ctx context.Context, tx RevertTx, pubCB PubCallback) error {
tokenTransferEvent := event.Event{
Block: tx.Block,
ContractAddress: tx.ContractAddress,
Success: false,
Timestamp: tx.Timestamp,
TxHash: tx.TxHash,
TxType: transferEventName,
}
switch tx.InputData[:8] {
case "a9059cbb":
var (
to common.Address
value big.Int
)
if err := tokenTransferSig.DecodeArgs(w3.B(tx.InputData), &to, &value); err != nil {
return err
}
proceed, err := h.checkStables(ctx, tx.From, to.Hex(), tx.ContractAddress)
if err != nil {
return err
}
if !proceed {
return nil
}
tokenTransferEvent.Payload = map[string]any{
"from": tx.From,
"to": to.Hex(),
"value": value.String(),
}
return pubCB(ctx, tokenTransferEvent)
case "23b872dd":
var (
from common.Address
to common.Address
value big.Int
)
if err := tokenTransferFromSig.DecodeArgs(w3.B(tx.InputData), &from, &to, &value); err != nil {
return err
}
proceed, err := h.checkStables(ctx, from.Hex(), to.Hex(), tx.ContractAddress)
if err != nil {
return err
}
if !proceed {
return nil
}
tokenTransferEvent.Payload = map[string]any{
"from": from.Hex(),
"to": to.Hex(),
"value": value.String(),
}
return pubCB(ctx, tokenTransferEvent)
}
return nil
}
func (h *tokenTransferHandler) checkStables(ctx context.Context, from string, to string, contractAddress string) (bool, error) {
_, ok := stables[contractAddress]
if !ok {
return true, nil
}
// TODO: Pipeline this check on Redis with a new method
fromExists, err := h.cache.Exists(ctx, from)
if err != nil {
return false, err
}
toExists, err := h.cache.Exists(ctx, to)
if err != nil {
return false, err
}
return fromExists || toExists, nil
}

View File

@ -6,8 +6,8 @@ import (
"sync/atomic"
"time"
"github.com/grassrootseconomics/celo-tracker/internal/cache"
"github.com/grassrootseconomics/celo-tracker/internal/pool"
"github.com/grassrootseconomics/eth-tracker/internal/cache"
"github.com/grassrootseconomics/eth-tracker/internal/pool"
)
type (

View File

@ -4,9 +4,9 @@ import (
"context"
"time"
"github.com/celo-org/celo-blockchain"
"github.com/celo-org/celo-blockchain/core/types"
"github.com/celo-org/celo-blockchain/event"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
)
type BlockQueueFn func(uint64) error
@ -23,7 +23,7 @@ func (s *Syncer) Start() {
s.realtimeSub = event.ResubscribeErr(resubscribeInterval, s.resubscribeFn())
}
func (s *Syncer) receiveRealtimeBlocks(ctx context.Context, fn BlockQueueFn) (celo.Subscription, error) {
func (s *Syncer) receiveRealtimeBlocks(ctx context.Context, fn BlockQueueFn) (ethereum.Subscription, error) {
newHeadersReceiver := make(chan *types.Header, 1)
sub, err := s.ethClient.SubscribeNewHead(ctx, newHeadersReceiver)
s.logg.Info("realtime syncer connected to ws endpoint")

View File

@ -4,12 +4,12 @@ import (
"context"
"log/slog"
"github.com/celo-org/celo-blockchain"
"github.com/celo-org/celo-blockchain/ethclient"
"github.com/grassrootseconomics/celo-tracker/db"
"github.com/grassrootseconomics/celo-tracker/internal/chain"
"github.com/grassrootseconomics/celo-tracker/internal/pool"
"github.com/grassrootseconomics/celo-tracker/internal/stats"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/grassrootseconomics/eth-tracker/db"
"github.com/grassrootseconomics/eth-tracker/internal/chain"
"github.com/grassrootseconomics/eth-tracker/internal/pool"
"github.com/grassrootseconomics/eth-tracker/internal/stats"
)
type (
@ -27,7 +27,7 @@ type (
db db.DB
ethClient *ethclient.Client
logg *slog.Logger
realtimeSub celo.Subscription
realtimeSub ethereum.Subscription
pool *pool.Pool
stats *stats.Stats
stopCh chan struct{}

View File

@ -41,13 +41,22 @@ func InitConfig(lo *slog.Logger, confFilePath string) *koanf.Koanf {
os.Exit(1)
}
if err := ko.Load(env.Provider("TRACKER_", ".", func(s string) string {
return strings.ReplaceAll(strings.ToLower(
strings.TrimPrefix(s, "TRACKER_")), "__", ".")
}), nil); err != nil {
err := ko.Load(env.ProviderWithValue("TRACKER_", ".", func(s string, v string) (string, interface{}) {
key := strings.ReplaceAll(strings.ToLower(strings.TrimPrefix(s, "TRACKER_")), "__", ".")
if strings.Contains(v, " ") {
return key, strings.Split(v, " ")
}
return key, v
}), nil)
if err != nil {
lo.Error("could not override config from env vars", "error", err)
os.Exit(1)
}
if os.Getenv("DEBUG") != "" {
ko.Print()
}
return ko
}

110
pkg/router/router.go Normal file
View File

@ -0,0 +1,110 @@
package router
import (
"context"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/grassrootseconomics/eth-tracker/pkg/event"
)
type (
Callback func(context.Context, event.Event) error
LogPayload struct {
Log *types.Log
Timestamp uint64
}
InputDataPayload struct {
From string
InputData string
Block uint64
ContractAddress string
Timestamp uint64
TxHash string
}
ContractCreationPayload struct {
From string
ContractAddress string
Block uint64
Timestamp uint64
TxHash string
Success bool
}
LogHandlerFunc func(context.Context, LogPayload, Callback) error
InputDataHandlerFunc func(context.Context, InputDataPayload, Callback) error
ContractCreationHandlerFunc func(context.Context, ContractCreationPayload, Callback) error
LogRouteEntry struct {
Signature common.Hash
HandlerFunc LogHandlerFunc
}
InputDataEntry struct {
Signature string
HandlerFunc InputDataHandlerFunc
}
Router struct {
callbackFn Callback
logHandlers map[common.Hash]LogRouteEntry
inputDataHandlers map[string]InputDataEntry
contractCreationHandler ContractCreationHandlerFunc
}
)
func New(callbackFn Callback) *Router {
return &Router{
callbackFn: callbackFn,
logHandlers: make(map[common.Hash]LogRouteEntry),
inputDataHandlers: make(map[string]InputDataEntry),
contractCreationHandler: nil,
}
}
func (r *Router) RegisterLogRoute(signature common.Hash, handlerFunc LogHandlerFunc) {
r.logHandlers[signature] = LogRouteEntry{
Signature: signature,
HandlerFunc: handlerFunc,
}
}
func (r *Router) RegisterInputDataRoute(signature string, handlerFunc InputDataHandlerFunc) {
r.inputDataHandlers[signature] = InputDataEntry{
Signature: signature,
HandlerFunc: handlerFunc,
}
}
func (r *Router) RegisterContractCreationHandler(handlerFunc ContractCreationHandlerFunc) {
r.contractCreationHandler = handlerFunc
}
func (r *Router) ProcessLog(ctx context.Context, payload LogPayload) error {
handler, ok := r.logHandlers[payload.Log.Topics[0]]
if ok {
return handler.HandlerFunc(ctx, payload, r.callbackFn)
}
return nil
}
func (r *Router) ProcessInputData(ctx context.Context, payload InputDataPayload) error {
if len(payload.InputData) < 8 {
return nil
}
handler, ok := r.inputDataHandlers[payload.InputData[:8]]
if ok {
return handler.HandlerFunc(ctx, payload, r.callbackFn)
}
return nil
}
func (r *Router) ProcessContractCreation(ctx context.Context, payload ContractCreationPayload) error {
return r.contractCreationHandler(ctx, payload, r.callbackFn)
}