master

Master Thesis code
Log | Files | Refs | README

commit db85f91cb1bc12ec0e3633b3c6343b466a0ce1ed
parent fd270e7817f6fb50ae5d86bd30fd871cd786a30d
Author: miksa234 <milutin@popovic.xyz>
Date:   Fri, 25 Apr 2025 20:07:19 +0100

inital

Diffstat:
A.gitignore | 362+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MREADME.md | 3+++
Ablock_extractor/Cargo.lock | 4598+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ablock_extractor/Cargo.toml | 23+++++++++++++++++++++++
Ablock_extractor/README.md | 11+++++++++++
Ablock_extractor/src/interfaces.rs | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ablock_extractor/src/lib.rs | 9+++++++++
Ablock_extractor/src/main.rs | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ablock_extractor/src/pools.rs | 355+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ablock_extractor/src/prices.rs | 262+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ablock_extractor/src/tokens.rs | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arl_arb/README.md | 1+
Arl_arb/main.py | 9+++++++++
Arl_arb/rl_arb/__init__.py | 1+
Arl_arb/rl_arb/brute_force.py | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arl_arb/rl_arb/cli.py | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arl_arb/rl_arb/config.py | 48++++++++++++++++++++++++++++++++++++++++++++++++
Arl_arb/rl_arb/initializer.py | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arl_arb/rl_arb/logger.py | 10++++++++++
Arl_arb/rl_arb/mcts.py | 502+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arl_arb/rl_arb/mdp.py | 298+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arl_arb/rl_arb/net.py | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arl_arb/rl_arb/reinforce.py | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arl_arb/rl_arb/rlearn.py | 558+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arl_arb/rl_arb/utils.py | 290+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
25 files changed, 8499 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,362 @@ +# C extensions +*.so + + +# Model & Data +data +model + +# Packages +*.egg +*.egg-info +bootstrap_scss +migrations +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +lib +lib64 +__pycache__ +.ipynb_checkpoints + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +htmlcov +.tox +nosetests.xml +test.db + +# Translations +*.pot + +# Mr Developer +venv +src/app.db +src/.env +logs +*log +images +.env +.vimspector.json + + +# Added by cargo +/target +target + +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bbl-SAVE-ERROR +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync +*.rubbercache +rubber.cache + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hypdoc +*.hd + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib diff --git a/README.md b/README.md @@ -0,0 +1,3 @@ +N-Cyclic Arbitrage on Constant Function Market Makers + +Master Thesis. diff --git a/block_extractor/Cargo.lock b/block_extractor/Cargo.lock @@ -0,0 +1,4598 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "const-random", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "alloy" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbcc41e8a11a4975b18ec6afba2cc48d591fa63336a4c526dacb50479a8d6b35" +dependencies = [ + "alloy-consensus", + "alloy-contract", + "alloy-core", + "alloy-eips", + "alloy-genesis", + "alloy-network", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", +] + +[[package]] +name = "alloy-chains" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ab9d1367c6ffb90c93fb4a9a4989530aa85112438c6f73a734067255d348469" +dependencies = [ + "alloy-primitives", + "num_enum", + "strum", +] + +[[package]] +name = "alloy-consensus" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4138dc275554afa6f18c4217262ac9388790b2fc393c2dfe03c51d357abf013" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-trie", + "auto_impl", + "c-kzg", + "derive_more", + "k256", + "serde", +] + +[[package]] +name = "alloy-consensus-any" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa04e1882c31288ce1028fdf31b6ea94cfa9eafa2e497f903ded631c8c6a42c" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-contract" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f21886c1fea0626f755a49b2ac653b396fb345233f6170db2da3d0ada31560c" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-types-eth", + "alloy-sol-types", + "alloy-transport", + "futures", + "futures-util", + "thiserror 2.0.11", +] + +[[package]] +name = "alloy-core" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "648275bb59110f88cc5fa9a176845e52a554ebfebac2d21220bcda8c9220f797" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-rlp", + "alloy-sol-types", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9138f4f0912793642d453523c3116bd5d9e11de73b70177aa7cb3e94b98ad2" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow", +] + +[[package]] +name = "alloy-eip2930" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabf647eb4650c91a9d38cb6f972bb320009e7e9d61765fb688a86f1563b33e8" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more", + "k256", + "serde", +] + +[[package]] +name = "alloy-eips" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dd5869ed09e399003e0e0ec6903d981b2a92e74c5d37e6b40890bad2517526" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "derive_more", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-genesis" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d2a7fe5c1a9bd6793829ea21a636f30fc2b3f5d2e7418ba86d96e41dd1f460" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "alloy-trie", + "serde", +] + +[[package]] +name = "alloy-json-abi" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24acd2f5ba97c7a320e67217274bc81fe3c3174b8e6144ec875d9d54e760e278" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2008bedb8159a255b46b7c8614516eda06679ea82f620913679afbd8031fea72" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "serde", + "serde_json", + "thiserror 2.0.11", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4556f01fe41d0677495df10a648ddcf7ce118b0e8aa9642a0e2b6dd1fb7259de" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-json-rpc", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-any", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror 2.0.11", +] + +[[package]] +name = "alloy-network-primitives" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31c3c6b71340a1d076831823f09cb6e02de01de5c6630a9631bdb36f947ff80" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec878088ec6283ce1e90d280316aadd3d6ce3de06ff63d68953c855e7e447e92" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "foldhash", + "hashbrown 0.15.2", + "indexmap", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand", + "ruint", + "rustc-hash", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-provider" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22c4441b3ebe2d77fa9cf629ba68c3f713eb91779cff84275393db97eddd82" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types-eth", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "parking_lot", + "pin-project", + "reqwest", + "schnellru", + "serde", + "serde_json", + "thiserror 2.0.11", + "tokio", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-pubsub" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2269fd635f7b505f27c63a3cb293148cd02301efce4c8bdd9ff54fbfc4a20e23" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "bimap", + "futures", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6c1d995bff8d011f7cd6c81820d51825e6e06d6db73914c1630ecf544d83d6" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a40e1ef334153322fd878d07e86af7a529bcb86b2439525920a88eba87bcf943" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06a292b37e182e514903ede6e623b9de96420e8109ce300da288a96d88b7e4b" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-pubsub", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "futures", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-rpc-types" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9383845dd924939e7ab0298bbfe231505e20928907d7905aa3bf112287305e06" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-any" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca445cef0eb6c2cf51cfb4e214fbf1ebd00893ae2e6f3b944c8101b07990f988" +dependencies = [ + "alloy-consensus-any", + "alloy-rpc-types-eth", + "alloy-serde", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f821f30344862a0b6eb9a1c2eb91dfb2ff44c7489f37152a526cdcab79264" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "derive_more", + "serde", + "strum", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0938bc615c02421bd86c1733ca7205cc3d99a122d9f9bff05726bd604b76a5c2" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "itertools 0.13.0", + "serde", + "serde_json", + "thiserror 2.0.11", +] + +[[package]] +name = "alloy-serde" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0465c71d4dced7525f408d84873aeebb71faf807d22d74c4a426430ccd9b55" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bfa395ad5cc952c82358d31e4c68b27bf4a89a5456d9b27e226e77dac50e4ff" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror 2.0.11", +] + +[[package]] +name = "alloy-signer-local" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdc63ce9eda1283fcbaca66ba4a414b841c0e3edbeef9c86a71242fc9e84ccc" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "k256", + "rand", + "thiserror 2.0.11", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d039d267aa5cbb7732fa6ce1fd9b5e9e29368f580f80ba9d7a8450c794de4b2" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "620ae5eee30ee7216a38027dec34e0585c55099f827f92f50d11e3d2d3a4a954" +dependencies = [ + "alloy-json-abi", + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.96", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9f7d057e00f8c5994e4ff4492b76532c51ead39353aa2ed63f8c50c0f4d52e" +dependencies = [ + "alloy-json-abi", + "const-hex", + "dunce", + "heck", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.96", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e60b084fe1aef8acecda2743ff2d93c18ff3eb67a2d3b12f62582a1e66ef5e" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1382302752cd751efd275f4d6ef65877ddf61e0e6f5ac84ef4302b79a33a31a" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "alloy-transport" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d17722a198f33bbd25337660787aea8b8f57814febb7c746bc30407bdfc39448" +dependencies = [ + "alloy-json-rpc", + "base64", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror 2.0.11", + "tokio", + "tower", + "tracing", + "url", + "wasmtimer", +] + +[[package]] +name = "alloy-transport-http" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1509599021330a31c4a6816b655e34bf67acb1cc03c564e09fd8754ff6c5de" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "reqwest", + "serde_json", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-ipc" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4da44bc9a5155ab599666d26decafcf12204b72a80eeaba7c5e234ee8ac205" +dependencies = [ + "alloy-json-rpc", + "alloy-pubsub", + "alloy-transport", + "bytes", + "futures", + "interprocess", + "pin-project", + "serde_json", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "alloy-transport-ws" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58011745b2f17b334db40df9077d75b181f78360a5bc5c35519e15d4bfce15e2" +dependencies = [ + "alloy-pubsub", + "alloy-transport", + "futures", + "http", + "rustls", + "serde_json", + "tokio", + "tokio-tungstenite", + "tracing", + "ws_stream_wasm", +] + +[[package]] +name = "alloy-trie" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6917c79e837aa7b77b7a6dae9f89cbe15313ac161c4d3cfaf8909ef21f3d22d8" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arrayvec", + "derive_more", + "nybbles", + "serde", + "smallvec", + "tracing", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] + +[[package]] +name = "arrow" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ccdcc8fb14508ca20aaec7076032e5c0b0751b906036d4496786e2f227a37a" +dependencies = [ + "arrow-arith", + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-csv", + "arrow-data", + "arrow-ipc", + "arrow-json", + "arrow-ord", + "arrow-row", + "arrow-schema", + "arrow-select", + "arrow-string", +] + +[[package]] +name = "arrow-arith" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1aad8e27f32e411a0fc0bf5a625a35f0bf9b9f871cf4542abe31f7cef4beea2" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "chrono", + "num", +] + +[[package]] +name = "arrow-array" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6ed90c28c6f73a706c55799b8cc3a094e89257238e5b1d65ca7c70bd3ae23f" +dependencies = [ + "ahash", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "chrono", + "half", + "hashbrown 0.15.2", + "num", +] + +[[package]] +name = "arrow-buffer" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4a40bdc1552ea10fbdeae4e5a945d8572c32f66bce457b96c13d9c46b80447" +dependencies = [ + "bytes", + "half", + "num", +] + +[[package]] +name = "arrow-cast" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "430c0a21aa7f81bcf0f97c57216d7127795ea755f494d27bae2bd233be43c2cc" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", + "atoi", + "base64", + "chrono", + "half", + "lexical-core", + "num", + "ryu", +] + +[[package]] +name = "arrow-csv" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4444c8f8c57ac00e6a679ede67d1ae8872c170797dc45b46f75702437a77888" +dependencies = [ + "arrow-array", + "arrow-cast", + "arrow-schema", + "chrono", + "csv", + "csv-core", + "lazy_static", + "regex", +] + +[[package]] +name = "arrow-data" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09af476cfbe9879937e50b1334c73189de6039186e025b1b1ac84b283b87b20e" +dependencies = [ + "arrow-buffer", + "arrow-schema", + "half", + "num", +] + +[[package]] +name = "arrow-ipc" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136296e8824333a8a4c4a6e508e4aa65d5678b801246d0408825ae7b2523c628" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "flatbuffers", +] + +[[package]] +name = "arrow-json" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e222ad0e419ab8276818c5605a5bb1e35ed86fa8c5e550726433cc63b09c3c78" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-data", + "arrow-schema", + "chrono", + "half", + "indexmap", + "lexical-core", + "num", + "serde", + "serde_json", +] + +[[package]] +name = "arrow-ord" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddf14c5f03b679ec8ceac4dfac43f63cdc4ed54dab3cc120a4ef46af38481eb" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", +] + +[[package]] +name = "arrow-row" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9acdc58da19f383f4ba381fa0e3583534ae2ceb31269aaf4a03f08ff13e8443" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "half", +] + +[[package]] +name = "arrow-schema" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a1822a1a952955637e85e8f9d6b0e04dd75d65492b87ec548dd593d3a1f772b" + +[[package]] +name = "arrow-select" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4172e9a12dfe15303d3926269f9ead471ea93bdd067d113abc65cb6c48e246" +dependencies = [ + "ahash", + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "num", +] + +[[package]] +name = "arrow-string" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73683040445f4932342781926189901c9521bb1a787c35dbe628a3ce51372d3c" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", + "memchr", + "num", + "regex", + "regex-syntax", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "async-trait" +version = "0.1.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version 0.4.1", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "auto_impl" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block_extractor" +version = "0.1.0" +dependencies = [ + "alloy", + "anyhow", + "arrow", + "csv", + "dotenv", + "env_logger", + "futures", + "indicatif", + "log", + "parquet", + "tokio", +] + +[[package]] +name = "blst" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +dependencies = [ + "serde", +] + +[[package]] +name = "c-kzg" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + +[[package]] +name = "cc" +version = "1.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "console" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + +[[package]] +name = "const-hex" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csv" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "flatbuffers" +version = "24.12.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1baf0dbf96932ec9a3038d57900329c015b0bfb7b63d904f3bc27e2b02a096" +dependencies = [ + "bitflags 1.3.2", + "rustc_version 0.4.1", +] + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", + "serde", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "indicatif" +version = "0.17.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width", + "web-time", +] + +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + +[[package]] +name = "interprocess" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "894148491d817cb36b6f778017b8ac46b17408d522dd90f539d677ea938362eb" +dependencies = [ + "doctest-file", + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.52.0", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lexical-core" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b765c31809609075565a70b4b71402281283aeda7ecaf4818ac14a7b2ade8958" +dependencies = [ + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de6f9cb01fb0b08060209a057c048fcbab8717b4c1ecd2eac66ebfe39a65b0f2" +dependencies = [ + "lexical-parse-integer", + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-parse-integer" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72207aae22fc0a121ba7b6d479e42cbfea549af1479c3f3a4f12c70dd66df12e" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-util" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a82e24bf537fd24c177ffbbdc6ebcc8d54732c35b50a3f28cc3f4e4c949a0b3" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "lexical-write-float" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5afc668a27f460fb45a81a757b6bf2f43c2d7e30cb5a2dcd3abf294c78d62bd" +dependencies = [ + "lexical-util", + "lexical-write-integer", + "static_assertions", +] + +[[package]] +name = "lexical-write-integer" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629ddff1a914a836fb245616a7888b62903aae58fa771e1d83943035efa0f978" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.2", +] + +[[package]] +name = "lz4_flex" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "nybbles" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983bb634df7248924ee0c4c3a749609b5abcb082c28fffe3254b3eb3602b307" +dependencies = [ + "alloy-rlp", + "const-hex", + "proptest", + "serde", + "smallvec", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl" +version = "0.10.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" +dependencies = [ + "bitflags 2.8.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "parquet" +version = "54.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3334c50239d9f4951653d84fa6f636da86f53742e5e5849a30fbe852f3ff4383" +dependencies = [ + "ahash", + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-data", + "arrow-ipc", + "arrow-schema", + "arrow-select", + "base64", + "brotli", + "bytes", + "chrono", + "flate2", + "half", + "hashbrown 0.15.2", + "lz4_flex", + "num", + "num-bigint", + "paste", + "seq-macro", + "snap", + "thrift", + "twox-hash", + "zstd", + "zstd-sys", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" +dependencies = [ + "memchr", + "thiserror 2.0.11", + "ucd-trie", +] + +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version 0.4.1", +] + +[[package]] +name = "pin-project" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.8.0", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "serde", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags 2.8.0", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ruint" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.25", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.8.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "schnellru" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649" +dependencies = [ + "ahash", + "cfg-if", + "hashbrown 0.13.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.8.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" + +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + +[[package]] +name = "seq-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "serde_json" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] + +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.96", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84e4d83a0a6704561302b917a932484e1cae2d8c6354c64be8b7bac1c1fe057" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "thrift" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" +dependencies = [ + "byteorder", + "integer-encoding", + "ordered-float", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 1.0.69", + "utf-8", +] + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.96", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmtimer" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +dependencies = [ + "memchr", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "ws_stream_wasm" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version 0.4.1", + "send_wrapper", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/block_extractor/Cargo.toml b/block_extractor/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "block_extractor" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.95" +alloy = { version = "0.9.2", features = ["full"] } +tokio = { version = "1.43.0", features = ["full"] } +csv = "1.3.1" +indicatif = "0.17.9" +futures = "0.3.31" +log = "0.4.25" +env_logger = "0.11.6" +dotenv = "0.15.0" +parquet = "54.0.0" +arrow = "54.0.0" + +[lib] +name = "block_extractor_rs" +path = "src/lib.rs" diff --git a/block_extractor/README.md b/block_extractor/README.md @@ -0,0 +1,11 @@ +This program extracts all UniswapV2 and UniswapV3 pools and their tokens. +Additionally it also extracts the last x blocks of price data of these pools. + +To run the code you will need an eth node, best your own because its not +capped by rpc requests. Create an .env file and fill in the HTTP_URL of your +node e.g.: + +~/.env +HTTP_URL="https://eth-mainnet.public.blastapi.io" + +If you want logging add RUST_LOG=INFO to the .env file. diff --git a/block_extractor/src/interfaces.rs b/block_extractor/src/interfaces.rs @@ -0,0 +1,78 @@ +use alloy::sol; + +sol!( + #[sol(rpc)] + interface IUniswapV3Pool { + function slot0() external view returns ( + uint160 sqrtPriceX96, + int24 tick, + uint16 observationIndex, + uint16 observationCardinality, + uint16 observationCardinalityNext, + uint8 feeProtocol, + bool unlocked + ); + + function token0() external view returns ( + address adr + ); + + function token1() external view returns ( + address adr + ); + + function fee() external view returns ( + uint24 fee + ); + } +); + +sol!( + #[sol(rpc)] + interface IUniswapV2Pool { + function getReserves() external view returns ( + uint112 reserve0, + uint112 reserve1, + uint32 blockTimestampLast + ); + + function token0() external view returns ( + address adr + ); + + function token1() external view returns ( + address adr + ); + + function fee() external view returns ( + uint24 fee + ); + } +); + +sol!( + #[sol(rpc)] + interface IERC20 { + function balanceOf(address account) external view returns ( + uint256 balance + ); + + function decimals() external view returns ( + uint8 decimals + ); + + function name() external view returns ( + string name + ); + + function symbol() external view returns ( + string symbol + ); + } +); + +sol!( + #[sol(rpc)] + event PairCreated(address indexed token0, address indexed token1, address pair, uint); + event PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool); +); diff --git a/block_extractor/src/lib.rs b/block_extractor/src/lib.rs @@ -0,0 +1,9 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_imports)] +#![allow(unused_mut)] + +pub mod interfaces; +pub mod prices; +pub mod pools; +pub mod tokens; diff --git a/block_extractor/src/main.rs b/block_extractor/src/main.rs @@ -0,0 +1,90 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_imports)] + +use alloy::{ + eips::BlockId, + providers::{Provider, ProviderBuilder, WsConnect}, transports::http::reqwest::Url, +}; +use arrow::compute::filter; +use dotenv::dotenv; +use log::info; +use std::path::Path; +use anyhow::{anyhow, Result}; +use std::str::FromStr; +use std::sync::Arc; + +use block_extractor_rs::{ + interfaces::*, + tokens::*, + pools::*, + prices::*, +}; + +use std::fs::File; +use parquet::file::reader::{FileReader, SerializedFileReader}; + + +#[tokio::main] +async fn main() -> Result<()> { + + dotenv().ok(); + env_logger::init(); + +// let rpc_url = std::env::var("WSS_URL")?; +// let ws = WsConnect::new(rpc_url); +// let provider = ProviderBuilder::new().on_ws(ws).await?; + + let https_url = std::env::var("HTTPS_URL").unwrap(); + let provider = ProviderBuilder::new().on_builtin( + https_url.as_str() + ).await?; + +// let block_number = BlockId::from(provider.get_block_number().await.unwrap()); +// let from_block_number = 10000835; +// let chunks = 50000; +// let (pools, pool_id) = load_pools( +// provider.clone(), +// Path::new("../data/pools.csv"), +// from_block_number, +// chunks, +// ).await.unwrap(); +// +// let parallel_tokens = 1; +// let tokens = load_tokens( +// provider.clone(), +// Path::new("../data/tokens.csv"), +// &pools, +// parallel_tokens, +// pool_id, +// ).await.unwrap(); +// + let filtered_pools = load_pools_from_file( + Path::new("../data/pools/pools_deg_5_liq_100_block_18_grad.csv"), + ).unwrap(); + + let tokens = load_tokens_from_file( + Path::new("../data/tokens/tokens.csv"), + ).unwrap(); + + info!("#fltered_pools {:?}", filtered_pools.len()); + info!("#tokens {:?}", tokens.len()); + + let p_to_block = provider.get_block_number().await?; + let p_from_block = 18000000; + let block_gap = 3600; // approx 12 hours + + let prices = load_prices( + provider.clone(), + &filtered_pools, + p_from_block, + p_to_block, + block_gap, + Path::new("../data/prices/prices_deg_5_liq_100_block_18_grad.parquet") + ).await.unwrap(); + + info!("Done len prices: {:?}", prices.len()); + + Ok(()) + +} diff --git a/block_extractor/src/pools.rs b/block_extractor/src/pools.rs @@ -0,0 +1,355 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_imports)] +#![allow(unused_mut)] + +use alloy::{ + primitives::{address, Address, FixedBytes}, + providers::{Provider, RootProvider}, + rpc::types::{BlockId, BlockTransactionsKind, Filter}, + sol_types::SolEvent, + transports::{http::{Client, Http}, BoxTransport} +}; +use std::{ + collections::{BTreeMap, HashMap}, + fs::OpenOptions, + path::Path, + str::FromStr, + sync::Arc +}; +use indicatif::{ProgressBar, ProgressStyle}; +use anyhow::{Result, anyhow}; +use csv::StringRecord; +use log::info; + +use crate::interfaces::*; + + +#[derive(Debug, Clone)] +pub enum Version { + V2, + V3 +} + +#[derive(Debug, Clone)] +pub struct Pool { + pub id: i64, + pub address: Address, + pub version: Version, + pub token0: Address, + pub token1: Address, + pub fee: u32, + pub block_number: u64, + pub timestamp: u64, + pub tickspacing: i32, +} + +impl From<StringRecord> for Pool { + fn from(record: StringRecord) -> Self { + let version = match record.get(2).unwrap().parse().unwrap() { + 2 => Version::V2, + _ => Version::V3 + }; + Self { + id: record.get(0).unwrap().parse().unwrap(), + address: Address::from_str(record.get(1).unwrap()).unwrap(), + version, + token0: Address::from_str(record.get(3).unwrap()).unwrap(), + token1: Address::from_str(record.get(4).unwrap()).unwrap(), + fee: record.get(5).unwrap().parse().unwrap(), + block_number: record.get(6).unwrap().parse().unwrap(), + timestamp: record.get(7).unwrap().parse().unwrap(), + tickspacing: record.get(8).unwrap().parse().unwrap(), + } + } +} + + +impl Pool { + pub fn cache_row(&self) -> (i64, String, i32, String, String, u32, u64, u64, i32) { + ( + self.id, + format!("{:?}", self.address), + match self.version { + Version::V2 => 2, + _ => 3, + }, + format!("{:?}", self.token0), + format!("{:?}", self.token1), + self.fee, + self.block_number, + self.timestamp, + self.tickspacing, + ) + } + + pub fn has_token(&self, token: Address) -> bool { + self.token0 == token || self.token1 == token + } +} + +pub async fn load_pools( + provider: RootProvider<BoxTransport>, + path: &Path, + from_block: u64, + chunk: u64, +) -> Result<(BTreeMap<Address, Pool>, i64)> { + + info!("Loading Pools..."); + + let mut pools = BTreeMap::new(); + let mut blocks = vec![]; + + let file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(path) + .unwrap(); + + let mut writer = csv::Writer::from_writer(file); + + if path.exists() { + let mut reader = csv::Reader::from_path(path)?; + for row in reader.records() { + let row = row.unwrap(); + let pool = Pool::from(row); + blocks.push(pool.block_number); + pools.insert(pool.address, pool); + } + } else { + writer.write_record(&[ + "id", + "address", + "version", + "token0", + "token1", + "fee", + "block_number", + "timestamp", + "tickspacing", + ])?; + } + + let last_id = match pools.len() > 0{ + true => pools.last_key_value().unwrap().1.id, + false => -1 + }; + + let from_block = match last_id != -1 { + true => { + match blocks.iter().max() { + Some(b) => *b, + None => { return Err(anyhow!("load_pools could not find last processed block")); } + } + } + false => from_block + }; + + + let to_block = provider.get_block_number().await.unwrap(); +// let from_block = to_block; + let mut processed_blocks = 0u64; + let mut block_range: Vec<(u64, u64)> = vec![]; + + info!("From block {:?} -> To block {:?}", from_block, to_block); + + loop { + let start_idx = from_block + processed_blocks; + let mut end_idx = start_idx + chunk - 1; + if end_idx > to_block { + end_idx = to_block; + block_range.push((start_idx, end_idx)); + break; + } + block_range.push((start_idx, end_idx)); + processed_blocks += chunk; + } + + let sigs = vec![ + PoolCreated::SIGNATURE_HASH, // v3 + PairCreated::SIGNATURE_HASH, // v3 + ]; + + let factories = vec![ + address!("0x1F98431c8aD98523631AE4a59f267346ea31F984"), + address!("0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"), + ]; + + + let pb = ProgressBar::new(to_block-from_block); + pb.set_style( + ProgressStyle::with_template( + "[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} current pools: {msg}", + ) + .unwrap() + .progress_chars("##-"), + ); + pb.inc(0); + + for range in block_range { + match get_pool_data( + provider.clone(), + range.0, + range.1, + sigs.clone(), + factories.clone(), + ).await { + Ok(r) => { + for p in r { pools.insert(p.address, p); } + } + Err(e) => { + info!("get_pool_data call error {:?}", e); + continue; + } + }; + pb.inc(chunk); + pb.set_message(format!("{:?} block range {:?}-{:?}", pools.len(), range.0, range.1)); + } + + let mut id = 0; + let mut added = 0; + + for (_, pool) in pools.iter_mut() { + if pool.id == -1 { + id += 1; + pool.id = id; + } + if (pool.id as i64) > last_id { + writer.serialize(pool.cache_row())?; + added += 1; + } + } + writer.flush()?; + + Ok((pools, last_id)) +} + + +async fn get_pool_data( + provider: RootProvider<BoxTransport>, + from_block: u64, + to_block: u64, + sig_hash: Vec<FixedBytes<32>>, + address: Vec<Address>, +) -> Result<Vec<Pool>> { + let mut pools = Vec::new(); + let mut timestamp_map: HashMap<u64, u64> = HashMap::new(); + + let filter = Filter::new() + .from_block(from_block) + .to_block(to_block) + .event_signature(sig_hash) + .address(address); + + let logs = match provider.get_logs(&filter).await { + Ok(r) => r, + Err(e) => { + info!("Error getting logs {:?}", e); + return Ok(pools); + }, + }; + + for log in logs { + let (version, address, token0, token1, fee, tickspacing) = match log.topic0().unwrap() { + &PairCreated::SIGNATURE_HASH => { + let event = match PairCreated::decode_log_data( + log.data(), true + ) { + Ok(r) => r, + Err(e) => { + info!("UniswapV2Factory decoding error {:?}", e); + continue; + } + }; + let tickspacing: i32 = 0; + let fee: u32 = 3000; + (Version::V2, event.pair, event.token0, event.token1, fee, tickspacing) + }, + &PoolCreated::SIGNATURE_HASH => { + let event = match PoolCreated::decode_log_data( + log.data(), true + ) { + Ok(r) => r, + Err(e) => { + info!("UniswapV3Factory decoding error {:?}", e); + continue; + } + }; + (Version::V3, event.pool, event.token0, event.token1, event.fee.to::<u32>(), event.tickSpacing.as_i32()) + }, + t => { + info!("Counld not match topic {:?}", t); + continue; + } + }; + + let block_number = match log.block_number { + Some(r) => r, + None => { + info!("log does not contain block_number"); + 0u64 + } + }; + + let timestamp = if !timestamp_map.contains_key(&block_number) { + let block = match provider.get_block( + BlockId::from(block_number), + BlockTransactionsKind::default() + ).await { + Ok(r) => { + match r { + Some(v) => v, + None => { + info!("No block returned"); + continue; + } + } + }, + Err(e) => { + info!("Could not get block {:?}", e); + continue; + } + }; + let timestamp = block.header.timestamp; + timestamp + } else { + let timestamp = *timestamp_map.get(&block_number).unwrap(); + timestamp + }; + + let pool_data = Pool { + id: -1, + address, + version, + token0, + token1, + fee, + block_number, + timestamp, + tickspacing + }; + + pools.push(pool_data) + } + Ok(pools) +} + +pub fn load_pools_from_file( + path: &Path, +) -> Result<BTreeMap<Address, Pool>> { + let mut pools = BTreeMap::new(); + + if path.exists() { + let mut reader = csv::Reader::from_path(path)?; + for row in reader.records() { + let row = row.unwrap(); + let pool = Pool::from(row); + pools.insert(pool.address, pool); + } + } else { + return Err(anyhow!("File path does not exist")); + } + + Ok(pools) +} diff --git a/block_extractor/src/prices.rs b/block_extractor/src/prices.rs @@ -0,0 +1,262 @@ +use alloy::{ + eips::BlockId, + primitives::{Address, U256}, + providers::RootProvider, + pubsub::PubSubFrontend, transports::BoxTransport, +}; +use arrow::{ + array::ArrayRef, datatypes::{DataType, Field, Schema}, record_batch::RecordBatch +}; +use parquet::{ + arrow::{arrow_reader::ParquetRecordBatchReaderBuilder, ArrowWriter}, basic::Compression, file::properties::{EnabledStatistics, WriterProperties} +}; +use indicatif::{ProgressBar, ProgressStyle}; +use std::{ + collections::BTreeMap, + fs::{OpenOptions, File}, + path::Path, + sync::Arc +}; +use log::info; +use anyhow::{anyhow, Result}; + +use crate::{interfaces::*, pools::{Pool, Version}}; + +pub struct Price { + pool: Address, + block: u64, + r_t0: Option<U256>, + r_t1: Option<U256>, + spx96: Option<U256>, +} + + +pub async fn load_prices( + provider: RootProvider<BoxTransport>, + pools: &BTreeMap<Address, Pool>, + from_block: u64, + to_block: u64, + block_gap: u64, + path : &Path, +) -> Result<Vec<Price>> { + + let mut prices = Vec::new(); + + + let mut blocks = Vec::new(); + blocks.push(from_block); + let mut cur = from_block; + loop { + cur += block_gap; + if cur > to_block { + blocks.push(to_block); + break + } + blocks.push(cur); + } + + let pb = ProgressBar::new(blocks.len() as u64); + pb.set_style( + ProgressStyle::with_template( + "[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}", + ) + .unwrap() + .progress_chars("##-"), + ); + pb.set_message(format!("From block {:?} - To Block {:?}", from_block, to_block)); + pb.inc(0); + + for block in blocks { + 'pool_loop: for (_, pool) in pools.into_iter() { + match pool.version { + Version::V2 => { + match get_v2_price( + provider.clone(), + block, + pool, + ).await { + Ok(price) => { + prices.push( + Price { + pool: pool.address, + block, + r_t0: Some(price.0), + r_t1: Some(price.1), + spx96: None, + } + ); + } + Err(e) => { + info!("Error getting price {:?}", e); + continue 'pool_loop; + } + }; + } + Version::V3 => { + match get_v3_price( + provider.clone(), + block, + pool + ).await { + Ok(price) => { + prices.push( + Price { + pool: pool.address, + block, + r_t0: Some(price.0), + r_t1: Some(price.1), + spx96: Some(price.2), + } + ); + } + Err(e) => { + info!("Error getting price {:?}", e); + continue 'pool_loop; + } + }; + } + } + } + pb.inc(1) + } + + let file = OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(path) + .unwrap(); + + let props = WriterProperties::builder() + .set_compression(Compression::SNAPPY) + .set_statistics_enabled(EnabledStatistics::None) + .build(); + + let batch = create_record_batch(&prices).unwrap(); + println!("{:?}", batch.schema()); + + let mut writer = ArrowWriter::try_new(file, batch.schema(), Some(props)).unwrap(); + writer.write(&batch).unwrap(); + writer.close().unwrap(); + + Ok(prices) +} + +fn create_record_batch( + prices: &Vec<Price>, +) -> Result<RecordBatch> { + + let pools = prices.iter() + .map(|p| format!("{:?}", p.pool)) + .collect::<Vec<String>>(); + + let blocks = prices.iter() + .map(|p| p.block as i64) + .collect::<Vec<i64>>(); + + let r_t0s = prices.iter() + .map(|p| match p.r_t0{ + Some(r) => Some(format!("{:?}", r)), + None => None, + }).collect::<Vec<Option<String>>>(); + + let r_t1s = prices.iter() + .map(|p| match p.r_t1{ + Some(r) => Some(format!("{:?}", r)), + None => None, + }) + .collect::<Vec<Option<String>>>(); + + let spx96s = prices.iter() + .map(|p| match p.spx96 { + Some(r) => Some(format!("{:?}", r)), + None => None, + }) + .collect::<Vec<Option<String>>>(); + + let batch = RecordBatch::try_from_iter( + vec![ + ("pool_address", Arc::new(arrow::array::StringArray::from(pools)) as ArrayRef), + ("block_number", Arc::new(arrow::array::Int64Array::from(blocks)) as ArrayRef), + ("reserve_t0", Arc::new(arrow::array::StringArray::from(r_t0s)) as ArrayRef), + ("reserve_t1", Arc::new(arrow::array::StringArray::from(r_t1s)) as ArrayRef), + ("sqrt_price_x96", Arc::new(arrow::array::StringArray::from(spx96s)) as ArrayRef), + ] + ).unwrap(); + + Ok(batch) +} + +async fn get_v2_price( + provider: RootProvider<BoxTransport>, + block_number: u64, + pool: &Pool, +) -> Result<(U256, U256)> { + + let block = BlockId::from(block_number); + + let token0_ierc20 = IERC20::new(pool.token0, &provider); // token1 + let token1_ierc20 = IERC20::new(pool.token1, &provider); // token1 + + let balance_token0 = match token0_ierc20 + .balanceOf(pool.address) + .block(block) + .call() + .await { + Ok(r) => r.balance, + Err(e) => { return Err(anyhow!("Error getting balance_token0 {:?}", e)); } + }; + + let balance_token1 = match token1_ierc20 + .balanceOf(pool.address) + .block(block) + .call() + .await { + Ok(r) => r.balance, + Err(e) => { return Err(anyhow!("Error getting balance_token1 {:?}", e)); } + }; + + return Ok((balance_token0, balance_token1)); +} + +async fn get_v3_price( + provider: RootProvider<BoxTransport>, + block_number: u64, + pool: &Pool, +) -> Result<(U256, U256, U256)> { + + let block = BlockId::from(block_number); + + let token0_ierc20 = IERC20::new(pool.token0, &provider); // token1 + let token1_ierc20 = IERC20::new(pool.token1, &provider); // token1 + let pool_int = IUniswapV3Pool::new(pool.address, &provider); // token1 + + + let balance_token0 = match token0_ierc20 + .balanceOf(pool.address) + .block(block) + .call() + .await { + Ok(r) => r.balance, + Err(e) => { return Err(anyhow!("Error getting balance_token0 {:?}", e)); } + }; + let balance_token1 = match token1_ierc20 + .balanceOf(pool.address) + .block(block) + .call() + .await { + Ok(r) => r.balance, + Err(e) => { return Err(anyhow!("Error getting balance_token1 {:?}", e)); } + }; + + let sqrt_price_x96 = match pool_int + .slot0() + .block(block) + .call() + .await { + Ok(r) => U256::from(r.sqrtPriceX96), + Err(e) => { return Err(anyhow!("Error returning sqrt_price_x96 {:?}", e)); } + }; + + return Ok((balance_token0, balance_token1, sqrt_price_x96)); +} diff --git a/block_extractor/src/tokens.rs b/block_extractor/src/tokens.rs @@ -0,0 +1,226 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_imports)] +#![allow(unused_mut)] + +use std::{ + collections::BTreeMap, + fs::OpenOptions, + path::Path, + str::FromStr +}; +use alloy::{ + primitives::Address, + providers::RootProvider, + transports::{http::{Client, Http}, BoxTransport}, +}; +use indicatif::{ProgressBar, ProgressStyle}; +use anyhow::{anyhow, Result}; +use csv::StringRecord; +use log::info; + +use crate::{interfaces::IERC20, pools::Pool}; + + +#[derive(Debug, Clone)] +pub struct Token { + pub id: i64, + pub address: Address, + pub name: String, + pub symbol: String, + pub decimals: u8 +} + +impl From<StringRecord> for Token { + fn from(record: StringRecord) -> Self { + Self { + id: record.get(0).unwrap().parse().unwrap(), + address: Address::from_str(record.get(1).unwrap()).unwrap(), + name: String::from(record.get(2).unwrap()), + symbol: String::from(record.get(3).unwrap()), + decimals: record.get(4).unwrap().parse().unwrap(), + } + } +} + +impl Token { + pub fn cache_row(&self) -> (i64, String, String, String, u8) { + ( + self.id, + format!("{:?}", self.address), + self.name.clone(), + self.symbol.clone(), + self.decimals, + ) + } +} + +pub async fn load_tokens( + provider: RootProvider<BoxTransport>, + path: &Path, + pools: &BTreeMap<Address, Pool>, + parallel: u64, + last_pool_id: i64, +) -> Result<BTreeMap<Address, Token>> { + + info!("Loading tokens..."); + + let mut tokens = BTreeMap::new(); + + let file = OpenOptions::new() + .write(true) + .append(true) + .create(true) + .open(path) + .unwrap(); + + let mut writer = csv::Writer::from_writer(file); + + let mut token_id = 0; + if path.exists() { + let mut reader = csv::Reader::from_path(path)?; + for row in reader.records() { + let row = row.unwrap(); + let token = Token::from(row); + tokens.insert(token.address, token); + token_id += 1; + } + } else { + writer.write_record(&[ + "id", + "address", + "name", + "symbol", + "decimals", + ])?; + } + + let pb = ProgressBar::new(pools.len() as u64); + pb.set_style( + ProgressStyle::with_template( + "[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}", + ) + .unwrap() + .progress_chars("##-"), + ); + + let new_token_id = token_id; + + let mut count = 0; + let mut requests = Vec::new(); + + for (_, pool) in pools.into_iter() { + let pool_id = pool.id; + if pool_id <= last_pool_id { + continue; + } + let token0 = pool.token0; + let token1 = pool.token1; + for token in [token0, token1] { + if !tokens.contains_key(&token) { + requests.push( + tokio::task::spawn( + get_token_data( + provider.clone(), + token, + ) + ) + ); + count += 1 ; + } + if count == parallel { + let results = futures::future::join_all(requests).await; + for result in results { + match result { + Ok(r) => match r { + Ok(t) => { + tokens.insert( + t.address, + Token { + id: token_id, + address: t.address, + name: t.name, + symbol: t.symbol, + decimals: t.decimals + } + ); + token_id += 1 + } + Err(e) => { info!("Something wrong 0 {:?}", e) } + } + Err(e) => { info!("Something wrong 1 {:?}", e) } + } + } + requests = Vec::new(); + count = 0; + pb.inc(parallel); + } + } + } + + let mut added = 0; + for token in tokens.values().collect::<Vec<&Token>>().iter() { + if token.id >= new_token_id { + writer.serialize(token.cache_row())?; + added += 1 + } + } + writer.flush()?; + + Ok(tokens) +} + +async fn get_token_data( + provider: RootProvider<BoxTransport>, + token: Address, +) -> Result<Token> { + + let interface = IERC20::new(token, provider); + + let decimals = match interface.decimals().call().await { + Ok(r) => r.decimals, + Err(e) => { return Err(anyhow!("Decimals of token failed {:?}", e )) } + }; + + let name = match interface.name().call().await { + Ok(r) => r.name, + Err(e) => { + info!("Name of token {:?} failed {:?}", token, e); + String::from("PlaceHolderName") + } + }; + let symbol = match interface.symbol().call().await{ + Ok(r) => r.symbol, + Err(e) => { + info!("Symbol of token failed {:?}", e ); + String::from("PlaceHolderSymbol") + } + }; + + Ok(Token { + id: -1, + address: token, + name, + symbol, + decimals, + }) +} + +pub fn load_tokens_from_file( + path: &Path, +) -> Result<BTreeMap<Address, Token>> { + let mut tokens = BTreeMap::new(); + + if path.exists() { + let mut reader = csv::Reader::from_path(path)?; + for row in reader.records() { + let row = row.unwrap(); + let token = Token::from(row); + tokens.insert(token.address, token); + } + } else { + return Err(anyhow!("File path does not exist")); + } + + Ok(tokens) +} diff --git a/rl_arb/README.md b/rl_arb/README.md @@ -0,0 +1 @@ +This code solves the n-cyclic arbitrage routing problem with RL. diff --git a/rl_arb/main.py b/rl_arb/main.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +from rl_arb.cli import run + +def main(): + run() + +if __name__ == '__main__': + main() diff --git a/rl_arb/rl_arb/__init__.py b/rl_arb/rl_arb/__init__.py @@ -0,0 +1 @@ +#!/usr/bin/env python3 diff --git a/rl_arb/rl_arb/brute_force.py b/rl_arb/rl_arb/brute_force.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 + +import numpy as np +import torch +import time +import os +import sys +import pickle +from torch_geometric.data import Data, Batch +from torch_geometric.nn import summary + +from rl_arb.mcts import MCTSParallel, PMemory +from rl_arb.utils import send_telegram_message +sys.setrecursionlimit(1000000) + +from rl_arb.initializer import Initializer +from rl_arb.config import ARGS_TRAINING, DEVICE +from rl_arb.logger import logging +logger = logging.getLogger('rl_circuit') + +def brute_force_search_trail(mdp, source, cap): + """ + Brute force solution of the MDP problem, by doing recursive depth first + search and capping the length of the state by 'cap' + + Parameters + --------- + mdp: MDP + Class of of the MDP. + source: int + Source node. + cap: int + Boundary on solution length. + + Returns + ------- + tuple + - list of solutions + - list of values of solutions performance (profit) + """ + def dfs(state): +# if len(state) > cap: +# return + + if mdp.check_win(state): + trails.append(state) + return + + valid_actions = mdp.get_valid_actions(state) + for action in valid_actions: + dfs(state + [action]) + return + + trails = [] + state = [(source, source, 0)] + dfs(state) + profits = [0] + for trail in trails: + profits.append(mdp.calculate_profit(trail, mdp.current_block)) + + return trails, np.max(profits) + + +@torch.no_grad() +def test_model(): + """ + Testing function + """ + problem = Initializer() + + problem.mdp.data.to(DEVICE) + problem.mdp.device = DEVICE + problem.model.to(DEVICE) + problem.model.share_memory() + problem.model.eval() + + mcts_parallel = MCTSParallel(problem.mdp, ARGS_TRAINING) + + if not os.path.exists('./test'): + os.mkdir('./test') + + loss = {} + values = {} + brute_values = {} + np.random.seed(0) + selected_blocks = np.random.choice(problem.mdp.num_blocks, 100, replace=False) + + start = problem.mdp.start_node + times = [] + for b in selected_blocks: + problem.mdp.current_block = b + problem.mcts.mdp.current_block = b + st = time.time() + _, brute_profit = brute_force_search_trail(problem.mdp, start, 10) + et = time.time() + print(et-st) + times.append(et-st) + brute_values[b] = np.log(brute_profit) + print(np.mean(times)) + exit() + + for i in range(100): +# problem.model.load_state_dict( +# torch.load( +# f"./model/model_{i}.pt", +# weights_only = True, +# map_location=DEVICE +# ), +# strict = False +# ) + + logger.info(f"Iterations {i}/100") +# send_telegram_message(f"Iterations {i}/100") + loss[i] = {} + values[i] = {} + + step = 25 + for block in range(step, len(selected_blocks)+step, step): + + p_memory = [ + PMemory(mcts_parallel.mdp, b) for b in selected_blocks[block-step: block] + ] + + st = time.time() + while len(p_memory) > 0: + states = [mem.state for mem in p_memory] + + mcts_parallel.search(problem.model, states, p_memory) + for m in range(len(p_memory))[::-1]: + mem = p_memory[m] + probs = np.zeros(len(mcts_parallel.mdp.edge_list)) + + for child in mem.root.children: + probs[ + mcts_parallel.mdp.edge_list.index(child.action_taken) + ] = child.value_best + + if np.sum(probs) == 0: + for child in mem.root.children: + probs[ + mcts_parallel.mdp.edge_list.index(child.action_taken) + ] = child.visit_count + + probs /= np.sum(probs) + + action = mcts_parallel.mdp.edge_list[np.argmax(probs)] + mem.state = mcts_parallel.mdp.get_next_state(mem.state, action) + + value, is_terminal = mcts_parallel.mdp.get_value_and_terminated( + mem.state, + mem.current_block + ) + + if is_terminal: + cb = mem.current_block + values[i][cb] = value + loss[i][cb] = 1-value/brute_values[cb] + + del p_memory[m] + et = time.time() + print((et-st)/25) + + + logger.info(f"AVERAGE loss {np.mean([loss[i][k] for k in loss[i].keys()])}") +# send_telegram_message(f"AVERAGE loss {np.mean([loss[i][k] for k in loss[i].keys()])}") + + with open(f'./test/test.pickle', "wb") as f: + pickle.dump([loss, values, brute_values], f) + + diff --git a/rl_arb/rl_arb/cli.py b/rl_arb/rl_arb/cli.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +import sys +import os +import subprocess + +from rl_arb.initializer import Initializer +from rl_arb.utils import print_summary, send_telegram_message +from rl_arb.brute_force import test_model +from rl_arb.logger import logging +logger = logging.getLogger('rl_circuit') + +def run(): + """ + Small cli for interaction. + """ + logger.info("Started...") + help = """ + flags: + learn: initializes the training process + + test: Does some testing with the model. + + vsc5: intitializes the training on the vsc5 node + + mycomp: intitializes the training on the private comp + """ + + if sys.argv[1] == "mycomp": + hostname = os.uname()[1] + if hostname == "frame": + cwd = os.getcwd() + logger.info("Copying code...") + subprocess.getoutput(f"rsync -Pr {cwd} mycomp:~/") + logger.info("Executing code remotely...") + if sys.argv[2] == "test": + cmd = "test" + else: + cmd = "learn" + with subprocess.Popen( + f"ssh -4 mycomp 'source py_env/bin/activate && cd ./rl_arb && ./main.py {cmd}'", + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=True + ) as process: + for line in process.stdout: + logger.info(line.decode('utf8')) + else: + logger.info("Not on frame, closing...") + + elif sys.argv[1] == "vsc5": + cwd = os.getcwd() + data_path = "/gpfs/data/fs70700/miksa234" + logger.info("Copying code...") + subprocess.getoutput(f"rsync -Pr {cwd} vsc5:{data_path}") + logger.info(subprocess.getoutput(f"ssh -4 vsc5 'cd {data_path} && sbatch rl.job'")) + + elif sys.argv[1] == "learn": + problem = Initializer() + problem.rlearn.learn(problem.optimizer) + + elif sys.argv[1] == "reinforce": + problem = Initializer() + problem.reinforce.learn() + + elif sys.argv[1] == "info": + problem = Initializer() + print_summary(problem) + + elif sys.argv[1] == "test": + test_model() + + elif sys.argv[1] == "help": + logger.info(help) + + else: + logger.info("No valid input detected") + logger.info(help) + + logger.info("END") diff --git a/rl_arb/rl_arb/config.py b/rl_arb/rl_arb/config.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +import torch +from dotenv import dotenv_values + +env = dotenv_values(".env") + +TELEGRAM_TOKEN = env["TELEGRAM_TOKEN"] +TELEGRAM_CHAT_ID = env["TELEGRAM_CHAT_ID"] +TELEGRAM_SEND_URL = f'https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage' + +DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + +WETH = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + +ARGS_GAME = { + 'tau': 1, + 'M': 1, + 'cutoff': 25 +} + +ARGS_MODEL = { + 'in_channels': 4, + 'emb_channels': 128, + 'num_heads': 4, + 'num_layers': 4, + 'ff_dim': 512, + 'policy_mheads': 2, +} + +ARGS_TRAINING = { + 'C': 1.4, + 'num_reinforce': 100000, + 'num_iterations': 100, + 'num_searches': 100, + 'num_rollouts': 100, + 'num_self_play_iterations': 200, + 'num_parallel': 10, + 'num_epochs': 1, + 'batch_size': 25, + 'gamma': 0.98, + 'temperature': 1.25, + 'eps': 0.25, + 'dirichlet_alpha': 0.03, + 'num_processes': 20, + 'multicore': True, + 'telegram': True, +} diff --git a/rl_arb/rl_arb/initializer.py b/rl_arb/rl_arb/initializer.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 + +import os +import networkx as nx +import pandas as pd +import numpy as np +import torch +from torch_geometric.data import Data +import logging + +logger = logging.getLogger('rl_circuit') +import torch.multiprocessing as mp +try: + mp.set_start_method('spawn', force=True) +except RuntimeError: + pass + +from rl_arb.mdp import MDP +from rl_arb.mcts import MCTS +from rl_arb.net import PolicyNet +from rl_arb.rlearn import AgentRLearn +from rl_arb.reinforce import Reinforce +from rl_arb.config import ( + DEVICE, + ARGS_GAME, + ARGS_MODEL, + ARGS_TRAINING, + WETH +) +from rl_arb.utils import ( + load_pools_and_tokens, + make_token_graph, + linear_node_relabel, + filter_pools_with_no_gradient, + send_telegram_message +) + + +class Initializer(): + """ + Initializer object for the problem to load all the importaint variables + through a class + + Attributes: + ---------- + pools: pd.DataFrame + Filtered pools data used for the problem. + tokens: pd.DataFrame + All tokens found from all pools. + prices: pd.DataFrame + Prices of the used pools in a time interval. + graph: nx.MultiDiGraph + Problem graph, where edges are pools, nodes tokens. + token_mapping: dict + Linear node relabeling of eth address to index. + line_graph: nx.Graph + The line graph of 'graph' object. + line_mapping: dict + Linear node relabeling. + mdp: MDP + Network Markov Decision Process to be explored for arbitrage. + model: torch.nn.Module + Torch geometric deep graph neural network. + optimizer: torch.optim.Adam + Torch optimizer used to update the weights. + rlearn: AgentRLearn + Mcts based reinforcement learning algorithm. + mcts: MCTS + Monte Carlo Tree search algorithm. + + """ + def __init__(self): + pools, tokens = load_pools_and_tokens( + '../data/pools/pools_deg_5_liq_100_block_18_grad.csv', + '../data/tokens/tokens.csv', + ) + prices = pd.read_parquet( + '../data/prices/prices_deg_5_liq_100_block_18_grad.parquet' + ) + pools, prices = filter_pools_with_no_gradient(pools, prices) + + G = make_token_graph(pools, prices) + G, token_mapping = linear_node_relabel(G) + + L = nx.line_graph(G, create_using=nx.Graph) + for e in G.edges(data=True): + nx.set_node_attributes( + L, + {(e[0], e[1], e[2]['k']): e[2]['weight']}, + name='mexr' + ) + nx.set_node_attributes( + L, + {(e[0], e[1], e[2]['k']): e[2]['address']}, + name='address' + ) + L, line_mapping = linear_node_relabel(L) + + edges = list(G.edges(data=True)) + for (t0, t1, d) in edges: + inverse_weights = [1/el for el in d['weight']] + G.add_edge(t1, t0, k=d['k'], weight=inverse_weights, address=d['address']) + + + edge_list = [list(e) for e in L.edges()] + rates = np.array([np.log(n[1]['mexr']) for n in L.nodes(data=True)]).T + used = [0 for _ in range(len(L.nodes))] + t0_using = [0 for _ in range(len(L.nodes))] + t1_using = [0 for _ in range(len(L.nodes))] + + node_attr = torch.tensor(np.dstack([*rates, used, t0_using, t1_using])).float().squeeze(0) + edge_index = torch.tensor(edge_list).t().contiguous() + data = Data(x=node_attr, edge_index=edge_index) + + mdp = MDP(G, data, line_mapping, ARGS_GAME, start_node=token_mapping[WETH]) + + model = PolicyNet( + ARGS_MODEL + ).share_memory().to(DEVICE) + + optimizer = torch.optim.Adam(model.parameters(), lr=1e-4, weight_decay=1e-4) + optimizer.zero_grad() + + mcts = MCTS(mdp, ARGS_TRAINING, model) + + rlearn = AgentRLearn(model, mdp, ARGS_TRAINING) + + reinforce = Reinforce(model, optimizer, mdp, mcts, ARGS_TRAINING) + + if not os.path.exists('./model'): + os.mkdir('./model') + + if not os.path.exists('./baseline'): + os.mkdir('./baseline') + + self.pools = pools + self.tokens = tokens + self.prices = prices + + self.graph = G + self.token_mapping = token_mapping + self.line_graph = L + self.line_mapping = line_mapping + self.graph_data = data + + self.mdp = mdp + self.model = model + self.optimizer = optimizer + self.rlearn = rlearn + self.reinforce = reinforce + self.mcts = mcts # not parallel version diff --git a/rl_arb/rl_arb/logger.py b/rl_arb/rl_arb/logger.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 + +import logging +import sys + +format = "[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s" +logging.basicConfig(format=format) + +logger = logging.getLogger('rl_circuit') +logger.setLevel(logging.DEBUG) diff --git a/rl_arb/rl_arb/mcts.py b/rl_arb/rl_arb/mcts.py @@ -0,0 +1,502 @@ +#!/usr/bin/env python3 + +import numpy as np +import torch +from torch_geometric.data import Batch, Data +import time + +import logging + +logger = logging.getLogger('rl_circuit') + +class Node: + """ + A class representing a node in the Monte Carlo Tree Search (MCTS). + + Attributes + ---------- + args : dict + Arguments for the MCTS. + state : object + The state of the mdp at this node. + parent : Node, optional + The parent node of this node. + action_taken : object, optional + The action taken to reach this node. + prior : float, optional + The prior probability of selecting this node. + visit_count : int, optional + The number of times this node has been visited. + children : list + The list of child nodes. + expandable_actions : list + The list of actions that can be taken from this node. + visit_count: int + Number of times visited in the MCTS process. + value_best: float + The best of values backpropagated through this node. + q_value: float + Normalized value_best based on parent value_best. + + + Methods + ------- + is_fully_expanded(): + Checks if the node is fully expanded. + select(): + Selects the child node with the highest UCB value. + select(): + Selects the child node randomly. + get_ucb(child): + Calculates the UCB value for a child node. + expand(policy, mdp): + Expands all possible actions, creating net policy output. + simulate(mdp): + Simulates a rollout from the current state randomly. + backpropagate(value): + Backpropagates the value through the tree. + """ + def __init__( + self, + mdp, + args, + state, + parent=None, + action_taken=None, + prior=0, + visit_count=0, + value_best=0, + ): + self.args = args + self.state = state + self.parent = parent + self.action_taken = action_taken + self.prior = prior + + self.children = [] + self.expandable_actions = mdp.get_valid_actions(state) + + self.visit_count = visit_count + self.value_best = value_best + self.q_value = 0 + + + def is_fully_expanded(self): + """ + Checks if the node is fully expanded. + Node is fully expanded if there are no valid moves, + or node is fully expanded if there are children. + + Returns + ------- + bool + True if the node is fully expanded, False otherwise. + """ + return len(self.children) > 0 + + def select(self): + """ + Calculate ucb for all children. + Selects the child node with the highest UCB value. + + Parameters + ---------- + ucb_method : str, optional + The method to calculate UCB value. Default is 'alphazero'. + + Returns + ------- + Node + The child node with the highest UCB value. + """ + best_child = None + best_ucb = -np.inf + + for child in self.children: + ucb = self.get_ucb(child) + if ucb > best_ucb: + best_child = child + best_ucb = ucb + + return best_child + + def select_random(self): + return np.random.choice(self.children) + + def get_ucb(self, child): + """ + Calculates the UCB value for a child node using the AlphaZero method. + Exploration vs Exploitation constant is self.args['C'] + + Parameters + ---------- + child : Node + The child node. + + Returns + ------- + float + The UCB value for the child node using the AlphaZero method. + """ + q_value = child.q_value + u_value = np.sqrt(self.visit_count)/(child.visit_count + 1) + + return q_value + self.args['C'] * u_value * child.prior + + + def expand(self, policy, mdp): + """ + Expands the node using the policy. + + Parameters + ---------- + policy : array-like + The policy probabilities for each action. + mdp : object + The mdp object. + """ + for action, prob in enumerate(policy): + if prob > 0: + child_state = self.state.copy() + child_state = mdp.get_next_state(child_state, mdp.edge_list[action]) + + child = Node(mdp, self.args, child_state, self, mdp.edge_list[action], prob) + self.children.append(child) + + def simulate(self, mdp, current_block): + """ + Runs a random simulation on the mdp at current state untill terminal, + returns the value of the terminal state. + + Parameters + ---------- + mdp: MDP + MDP class object. + current_block: int + Current block index. + + Returns + ------- + value: float + State value. + """ + value, terminal = mdp.get_value_and_terminated( + self.state, + current_block + ) + + if terminal: + return value + + rollout_state = self.state.copy() + while True: + valid_actions = mdp.get_valid_actions(rollout_state) + action = valid_actions[np.random.choice(len(valid_actions))] + rollout_state = mdp.get_next_state(rollout_state, action) + value, terminal = mdp.get_value_and_terminated( + rollout_state, + current_block + ) + if terminal: + return value + + def backpropagate(self, value): + """ + Backpropagates the value through the tree. + + Parameters + ---------- + value : float + The value to backpropagate. + """ + self.visit_count += 1 + + if self.parent is not None: + self.parent.backpropagate(value) + if self.value_best < value: + self.q_value = value/self.parent.value_best + self.value_best = value + + + +class MCTS: + """ + A class representing the Monte Carlo Tree Search (MCTS) algorithm. + + Attributes + ---------- + mdp : object + The mdp object. + args : dict + Arguments for the MCTS. + model : object + The model used for policy and value predictions. + + Methods + ------- + search(state): + Performs the MCTS search from the given state. + """ + def __init__(self, mdp, args, model): + self.mdp = mdp + self.args = args + self.model = model + + @torch.no_grad() + def search(self, state): + """ + Performs the MCTS search from the given state. + + Parameters + ---------- + state : object + The state of the mdp. + + Returns + ------- + array-like + The visit count distribution over actions. + """ + + root = Node(self.mdp, self.args, state, visit_count=1, value_best=1) + + policy = self.model( + self.mdp.encode_state(root.state, self.mdp.current_block), + self.mdp.data.edge_index, + self.mdp.encode_state(root.state[:1], self.mdp.current_block) + ) + + policy = policy.squeeze(0).detach().cpu() + + policy = self.mdp.mask_policy(policy, root.state) + + root.expand(policy, self.mdp) + + for _ in range(self.args['num_rollouts']): + n = root.select_random() + v = n.simulate(self.mdp, self.mdp.current_block) + n.backpropagate(v) + + for _ in range(self.args['num_searches']): + node = root + while node.is_fully_expanded(): + node = node.select() + + + value, is_terminal = self.mdp.get_value_and_terminated( + node.state, + self.mdp.current_block + ) + + if not is_terminal: + policy = self.model( + self.mdp.encode_state(node.state, self.mdp.current_block), + self.mdp.data.edge_index, + y=self.mdp.encode_state(node.state[:1], self.mdp.current_block), + ) + policy = policy.squeeze(0) + policy = self.mdp.mask_policy(policy, node.state) + + node.expand(policy, self.mdp) + + for _ in range(self.args['num_rollouts']): + n = node.select_random() + v = n.simulate(self.mdp, self.mdp.current_block) + n.backpropagate(v) + + # return visit_count distribution + action_probs = np.zeros(len(self.mdp.edge_list)) + for child in root.children: + action_probs[ + self.mdp.edge_list.index(child.action_taken) + ] = child.value_best + + if sum(action_probs) == 0: + for child in root.children: + action_probs[ + self.mdp.edge_list.index(child.action_taken) + ] = child.visit_count + + + action_probs /= np.sum(action_probs) + + return action_probs + + +class MCTSParallel: + """ + A class representing the parallel version of + the Monte Carlo Tree Search (MCTS) algorithm. + + Attributes + ---------- + mdp : MDP + The mdp object. + args : dict + Arguments for the MCTS. + + Methods + ------- + set_C(C): + Sets the exploration parameter for the MCTS. + search(model, states, p_memory): + Performs the parallel MCTS search from the given states. + """ + def __init__(self, mdp, args): + self.mdp = mdp + self.args = args + + def set_C(self, C): + """ + Set the exploration parameter for the MCTS algorithm. + + Parameters + ---------- + self : object + The instance of the class. + C : float + The exploration parameter to be set. + """ + self.args['C'] = C + + @torch.no_grad() + def search(self, model, states, p_memory): + """ + Performs the parallel MCTS search from the given states. + + Parameters + ---------- + model: Net + Deep GNN computes the policy. + states : list[tuple] + The list of states to search from. + p_memory : list[Pmemory] + The list of PMemory objects for parallel search. + """ + + # batch data + data_list = [ + Data( + x=self.mdp.encode_state(mem.state, mem.current_block), + edge_index=self.mdp.data.edge_index, + y=self.mdp.encode_state(mem.state[:1], mem.current_block), + )\ + for mem in p_memory + ] + + batch = Batch.from_data_list(data_list).to(self.mdp.device) + policy = model.forward(batch.x, batch.edge_index, batch.y, batch.batch) + + del batch + del data_list + + policy = policy.detach().cpu().numpy() + # dirichlet random noise + policy = (1-self.args['eps']) * policy \ + + self.args['eps'] \ + * np.random.dirichlet( + [self.args['dirichlet_alpha']]*len(self.mdp.edges), + size=policy.shape[0] + ) + + for i, mem in enumerate(p_memory): + p_policy = torch.tensor(policy[i]) + p_policy = self.mdp.mask_policy(p_policy, states[i]) + + mem.root = Node(self.mdp, self.args, states[i], visit_count=1, value_best=1) + + mem.root.expand(p_policy, self.mdp) + + for _ in range(self.args['num_rollouts']): + rollout = mem.root.select_random() + value = rollout.simulate(self.mdp, mem.current_block) + rollout.backpropagate(value) + + for _ in range(self.args['num_searches']): + for mem in p_memory: + mem.node = None + node = mem.root + while node.is_fully_expanded(): + node = node.select() + + value, is_terminal = self.mdp.get_value_and_terminated( + node.state, + mem.current_block + ) + + if is_terminal: + node.backpropagate(value) + else: + mem.node = node + + expandable = [i for i in range(len(p_memory)) if p_memory[i].node != None] + + if len(expandable) > 0: + chunks = [expandable[i:i+self.args['num_parallel']] for i in range(0, len(expandable), self.args['num_parallel'])] + policy = [] + for chunk in chunks: + data_list = [ + Data( + x=self.mdp.encode_state( + p_memory[i].node.state, + p_memory[i].current_block + ), + edge_index=self.mdp.data.edge_index, + y=self.mdp.encode_state( + p_memory[i].node.state[:1], + p_memory[i].current_block + ), + )\ + for i in chunk + ] + + batch = Batch.from_data_list(data_list).to(self.mdp.device) + policy_chunk = model.forward( + batch.x, + batch.edge_index, + batch.y, + batch.batch, + ) + policy.append(policy_chunk) + + del batch + del data_list + + policy = torch.cat(policy, 0) + + + for i, idx in enumerate(expandable): + node = p_memory[idx].node + p_policy = self.mdp.mask_policy(policy[i], node.state) + + node.expand(p_policy, self.mdp) + + for _ in range(self.args['num_rollouts']): + rollout = node.select_random() + value = rollout.simulate(self.mdp, p_memory[idx].current_block) + rollout.backpropagate(value) + +class PMemory: + """ + A class representing the memory used in parallel + MCTS during parallel search. + + Attributes + ---------- + state : list[tuple] + The state of the mdp. + current_block : int + The current block in the mdp. + memory : list + The memory of the search. + root : Node + The root node of the search tree. + node : Node + The current node in the search tree. + """ + def __init__(self, mdp, at_block): + self.state = [(mdp.start_node, mdp.start_node, 0)] + self.current_block = at_block + self.memory = [] + self.root = None + self.node = None diff --git a/rl_arb/rl_arb/mdp.py b/rl_arb/rl_arb/mdp.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 + +import networkx as nx +import torch +import numpy as np +from torch_geometric.data import Data + +from rl_arb.config import DEVICE, WETH +from rl_arb.logger import logging +logger = logging.getLogger('rl_circuit') + +class MDP: + """ + A class to represent the Markov Decision Process environment. + + Attributes: + ----------- + G : nx.MultiDiGraph + The graph representing the network. + edges : dict + A dictionary of edges with their weights. + nodes : list + A list of nodes in the graph. + edge_list : list + A list of edges in the graph. + data : Data + The data associated with the graph. + line_mapping : dict + A mapping of lines to their indices. + num_blocks : int + The number of blocks in the data. + current_block : int + The current block index. + device: torch.device + Device object. + start_node: + Starting node. + token_pool_mapping: + Dictonary with key=node value=line_mapping value. + + Methods: + -------- + set_current_block(block): + Sets the current block index. + get_next_state(state, action): + Returns the next state given the current state and action. + get_valid_actions(state): + Returns the valid actions from the current state. + check_win(state): + Checks if the current state is a terminal state. + get_value_and_terminated(state, at_block): + Returns the value and whether the state is terminal. + encode_state(state, at_block): + Encodes the current state into a tensor. + mask_policy(policy, state): + Masks the policy tensor based on valid actions. + """ + + def __init__( + self, + G: nx.MultiDiGraph, + data, + line_mapping, + args, + current_block=-1, + start_node=0 + ): + """ + Constructs all the necessary attributes for the NetGame object. + + Parameters: + ----------- + G : nx.MultiDiGraph + The graph representing the network. + data : torch_geometric.data.Data + The data associated with the graph. + line_mapping : dict + A mapping of lines to their indices. + num_blocks : int + The number of blocks in the data. + current_block : int, optional + The current block index (default is -1). + start_node: int, optional + WETH index label in the problem graph (default is 0). + """ + self.G = G + self.args = args + self.edges = {(i, j, w['k']): w['weight'] for i, j, w in G.edges(data=True)} + self.nodes = list(G.nodes) + self.edge_list = list(self.edges.keys()) + self.action_size = len(self.edge_list) + + self.data = data + self.line_mapping = line_mapping + self.line_mapping_keys = list(self.line_mapping.keys()) + self.num_blocks = len(list(self.edges.items())[0][1])-1 + self.current_block = self.num_blocks + self.device = DEVICE + self.start_node = start_node + + self.token_pool_mapping = {node: [] for node in self.nodes} + for node in self.nodes: + for k, v in line_mapping.items(): + if node == k[0]: + self.token_pool_mapping[node].append((v, 0)) + if node == k[1]: + self.token_pool_mapping[node].append((v, 1)) + + def set_current_block(self, block_index): + """ + Sets the current block index. + Parameters: + ----------- + block : int + The current block index. + """ + self.current_block = block_index + + def get_next_state(self, state, action): + """ + Returns the next state given the current state and action. + + Parameters: + ----------- + state : list + The current state represented as a list of nodes. + action : tuple + The action represented as an edge. + + Returns: + -------- + list + The next state. + """ + state.append(action) + return state + + def get_valid_actions(self, state): + """ + Returns the valid actions from the current state. + + Parameters: + ----------- + state : list + The current state represented as a list of nodes. + + Returns: + -------- + list + The valid actions represented as edges. + """ + current_node = state[-1][1] + filter_edges = [e for e in state[1:]] +\ + [(e[1], e[0], e[2]) for e in state[1:]] + + valid_actions = [e for e in self.G.edges(current_node, data='k') if e not in filter_edges] + return valid_actions + + def check_win(self, state): + """ + Checks if the current state is a terminal state. + + Parameters: + ----------- + state : list + The current state represented as a list of nodes. + + Returns: + -------- + bool + True if the state is terminal, False otherwise. + """ + return state[0][1] == state[-1][1] and len(state) > 1 + + def get_value_and_terminated(self, state, at_block): + """ + Returns the value and whether the state is terminal. + + Parameters: + ----------- + state : list + The current state represented as a list of nodes. + at_block: int + Block of state. + + Returns: + -------- + tuple + The value and whether the state is terminal. + """ + valid_actions = self.get_valid_actions(state) + + value = 0 + terminated = False + + if len(state) >= self.args['cutoff']: + terminated = True + + if self.check_win(state): + profit = self.calculate_profit(state, at_block) + if profit > 3: + profit = 1 + + value += np.log(profit)*self.args['M'] + + terminated = True + return value, terminated + + if len(valid_actions) == 0: + value = -1 + terminated = True + + return value, terminated + + def encode_state(self, state, at_block): + """ + Encodes the current state into a tensor. + + Parameters: + ----------- + state : list + The current state represented as a list of nodes. + at_block: int + Block of state. + + Returns: + -------- + torch.Tensor + The encoded state tensor. + """ + e_x = self.data.x.detach().clone() + e_x= torch.dstack([ + e_x[:, at_block], # exchange rate + e_x[:, self.num_blocks+1], # used binary + e_x[:, self.num_blocks+2], # t0 binary + e_x[:, self.num_blocks+3], # t1 binary + ]).squeeze(0) + + # encode the current t0 or t1 + line_mapping_keys = list(self.line_mapping.keys()) + start_node = state[0][1] + current_node= state[-1][1] + + for p, t01 in self.token_pool_mapping[start_node]: + e_x[p, t01+2] = 1 + + if len(state) > 1: + profit = np.log(self.calculate_profit(state, at_block)) + if state[-1] in line_mapping_keys: + line_state_current = state[-1] + else: + line_state_current = (state[-1][1], state[-1][0], state[-1][2]) + + e_x[self.line_mapping[line_state_current], + line_state_current[:2].index(current_node)+2] = profit + + # encode the used edges + edge_idx = [] + for e in state[1:]: + if e in line_mapping_keys: + edge_idx.append(self.line_mapping[e]) + if (e[1], e[0], e[2]) in line_mapping_keys: + edge_idx.append(self.line_mapping[(e[1], e[0], e[2])]) + e_x[edge_idx, 1] = 1 + + return e_x.to(self.device) + + def mask_policy(self, policy, state): + """ + Masks the policy tensor based on valid actions. + + Parameters: + ----------- + policy : torch.Tensor + The policy tensor. + state : list + The current state represented as a list of nodes. + + Returns: + -------- + torch.Tensor + The masked policy tensor. + """ + valid_actions = self.get_valid_actions(state) + valid_indices = torch.tensor( + [self.edge_list.index(e) for e in valid_actions] + ) + + mask = torch.zeros_like(policy) + mask[valid_indices] = 1 + + masked_policy = policy * mask + return masked_policy / masked_policy.sum() + + def calculate_profit(self, state, at_block): + return np.prod( + [self.edges[edge][at_block] for edge in state[1:]] + ) diff --git a/rl_arb/rl_arb/net.py b/rl_arb/rl_arb/net.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 + +import torch +from torch import nn +from torch.nn import functional as F +from torch_geometric.nn import BatchNorm, GATConv, LayerNorm, Sequential, Linear + +import logging + +from torch_geometric.nn.glob import global_mean_pool +logger = logging.getLogger('rl_circuit') + +from rl_arb.config import DEVICE + +class PolicyNet(nn.Module): + """ + A Residual Network model for graph-based data. + + Parameters + ---------- + args : dict + in_channels : int + Number of input channels. + emb_channels : int + Number of embedding channels. + num_heads : int, optional + Number of attention heads. Default is 4. + num_layers : int, optional + Number of layers in the network. Default is 3. + policy_mheads : int + Number of attention heads for the policy head. + value_mheads : int + Number of attention heads for the value head. + + Attributes + ---------- + encoder : Linear + Linear layer for encoding input features. + hidden_blocks : nn.ModuleList + List of hidden residual blocks. + policy_head : Sequential + Policy head for outputting policy logits. + """ + def __init__( + self, + args, + ): + super().__init__() + + self.encoder = Linear(args['in_channels'], args['emb_channels']) + + self.hidden_blocks = nn.ModuleList() + for _ in range(args['num_layers']): + self.hidden_blocks.append( + ResCovBlock(args) + ) + + self.policy_head = Sequential('x, edge_index, batch', [ + (GATConv( + 3*args['emb_channels'], + args['emb_channels'], + heads=args['policy_mheads'], + ), 'x, edge_index -> x'), + (LayerNorm(args['emb_channels']*args['policy_mheads']), 'x, batch -> x'), + nn.ReLU(), + (Linear(args['emb_channels']*args['policy_mheads'], 2, bias=False), 'x -> x'), + ]) + + def forward(self, node_attr, edge_index, y, batch=None): + """ + Forward pass of the ResNet model. + + Parameters + ---------- + node_attr : Tensor + Node attributes. + edge_index : Tensor + Edge indices. + + Returns + ------- + tuple + A tuple containing the value estimation and policy logits. + """ + x = self.encoder(node_attr) + x_1 = self.encoder(y) + for block in self.hidden_blocks: + x = block(x, edge_index, batch) + + mean = global_mean_pool(x, batch) + if batch != None: + x_n = [] + for i in batch.unique(): + count = (batch == i).sum() + x_n.append(mean[i].repeat(count, 1)) + x_n = torch.cat(x_n, 0) + else: + x_n = mean.repeat(x.shape[0], 1) + x_c = torch.cat((x, x_n, x_1), dim=1) + + policy = self.policy_head(x_c, edge_index, batch).flatten().unsqueeze(0) + + if batch != None: + policy = policy.view(len(batch.unique()), -1) + + policy = torch.softmax(policy, dim=1) + + return policy + +class ResCovBlock(nn.Module): + """ + A Residual Block used in the ResNet model. + + Parameters + ---------- + args : dict + in_channels : int + Number of input channels. + emb_channels : int + Number of embedding channels. + num_heads : int + Number of attention heads. + ff_dim : int + Dimension of the feed-forward network. + + Attributes + ---------- + in_layer : Sequential + Initial layer with GATConv, BatchNorm, and Linear layers. + feed_forward : nn.Sequential + Feed-forward network with Linear and ReLU layers. + batch_norm1 : BatchNorm + Batch normalization layer after the initial layer. + batch_norm2 : BatchNorm + Batch normalization layer after the feed-forward network. + """ + def __init__(self, args): + super().__init__() + self.in_layer = Sequential('x, edge_index', [ + (GATConv( + args['emb_channels'], + args['emb_channels'], + heads=args['num_heads'] + ), 'x, edge_index -> x'), + Linear( + args['emb_channels']*args['num_heads'], + args['emb_channels'], + bias=False + ) + ]) + + self.batch_norm1 = LayerNorm(args['emb_channels']) + + self.feed_forward = nn.Sequential( + Linear(args['emb_channels'], args['ff_dim']), + nn.ReLU(), + Linear(args['ff_dim'], args['emb_channels']) + ) + self.batch_norm2 = LayerNorm(args['emb_channels']) + + def forward(self, node_attr, edge_index, batch=None): + """ + Forward pass of the ResBlock. + + Parameters + ---------- + node_attr : Tensor + Node embeddings. + edge_index : Tensor + Edge indices. + + Returns + ------- + Tensor + Output tensor after applying the residual block. + """ + res = node_attr + x = self.in_layer(node_attr, edge_index) + x = self.batch_norm1(res + x, batch) + + res = x + x = self.feed_forward(x) + x = self.batch_norm2(res + x, batch) + + return x diff --git a/rl_arb/rl_arb/reinforce.py b/rl_arb/rl_arb/reinforce.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 + +import torch +import numpy as np +import pickle +from torch_geometric.data import Data, Batch + +from rl_arb.config import DEVICE + +import logging +logger = logging.getLogger('rl_circuit') + +class Reinforce: + def __init__(self, model, optimizer, mdp, mcts, args): + self.model = model + self.mdp = mdp + self.mcts = mcts + self.optimizer = optimizer + self.args = args + + @torch.no_grad() + def play(self, state, at_block, mode="learn"): + mcts_probs = [] + actions, rewards, transitions, g_values =[], [], [], [] + suc_states = 0 + while True: + probs = self.model( + self.mdp.encode_state(state, at_block), + self.mdp.data.edge_index, + self.mdp.encode_state(state[:1], at_block) + ) + + probs = probs.squeeze(0).detach().cpu() + + probs = self.mdp.mask_policy(probs, state).numpy() + + if mode == "test": + action_idx = np.argmax(probs) + else: + action_idx = np.random.choice(list( + range(len(self.mdp.edge_list)) + ), p=probs/probs.sum()) + + action = self.mdp.edge_list[action_idx] + + actions.append(action) + mcts_probs.append(probs) + + state = self.mdp.get_next_state(state, action) + + value, is_terminal = self.mdp.get_value_and_terminated( + state, + at_block + ) + + if is_terminal: + rewards.append(value) + + if value != -1: + suc_states +=1 + + for i in range(1, len(state)): + transitions.append((at_block, state[:i])) + g_values.append( + self.args['gamma']**( + len(state)-len(state[:i]) + )*value + ) + break + return mcts_probs, actions, rewards, transitions, g_values, suc_states + + def learn(self): + self.model.to(DEVICE) + + mcts_probs = [] + rewards = [] + g_values = [] + actions = [] + transitions = [] + suc_states = 0 + max_reward = 0 + mean_rewards = 0 + + suc_states_hist = [] + mean_rewards_hist = [] + test_rewards_hist = [] + + for itr in range(self.args['num_reinforce']): + if itr % 2 == 0: + at_block = 10 + else: + at_block = 11 + self.mdp.current_block = at_block + self.mcts.mdp.current_block = at_block + + state = [(self.mdp.start_node, self.mdp.start_node, 0)] + m, a, r, t, g, suc = self.play(state, at_block) + suc_states += suc + transitions += t + g_values += g + rewards += r + actions += a + mcts_probs += m + + if itr%300==0 and itr > 0: + state = [(self.mdp.start_node, self.mdp.start_node, 0)] + _, _, tr, _, _, _ = self.play(state, at_block, mode="test") + logger.info(f"mean rewards: {mean_rewards}") + logger.info(f"max rewards: {max_reward}") + logger.info(f"test rewards: {tr}") + logger.info(f"suc_states : {suc_states}") + + mean_rewards = np.mean(rewards) + + if max_reward < np.max(rewards): + max_reward = np.max(rewards) + + suc_states_hist.append(suc_states) + mean_rewards_hist.append(mean_rewards) + test_rewards_hist.append(tr) + + rewards = [] + suc_states = 0 + + + data_list = [ + Data( + x=self.mdp.encode_state(s, b), + edge_index=self.mdp.data.edge_index, + y=self.mdp.encode_state(s[:1], b), + )\ + for b, s in transitions + ] + batch = Batch.from_data_list(data_list).to(DEVICE) + + policy_outs = self.model.forward( + batch.x, + batch.edge_index, + batch.y, + batch.batch, + ) + + del batch + del data_list + + one_hot = torch.zeros_like(policy_outs).to(DEVICE) + for a, p in zip(actions, one_hot): + p[self.mdp.edge_list.index(a)] = 1 + + g_tensor = torch.tensor(g_values).to(DEVICE) + + ce = torch.mean(torch.log(policy_outs) * one_hot, dim=1) + loss = -torch.mean(ce * (g_tensor-mean_rewards)) + + self.optimizer.zero_grad() + loss.backward() + self.optimizer.step() + + message = f""" + ITR: {itr} + Mean CE Loss: {torch.mean(ce)} + REINFORCE Loss: {loss} + """ + logger.info(message) + + mcts_probs = [] + g_values = [] + actions = [] + transitions = [] + + with open(f"./reinforce.pickle", "wb") as f: + pickle.dump( + [ + suc_states_hist, + mean_rewards_hist, + test_rewards_hist + ], + f + ) diff --git a/rl_arb/rl_arb/rlearn.py b/rl_arb/rl_arb/rlearn.py @@ -0,0 +1,558 @@ +#!/usr/bin/env python3 + +import numpy as np +import torch +from torch.nn import functional as F +from torch_geometric.data import Batch, Data +import torch.multiprocessing as mp +from tqdm.contrib.telegram import tqdm +from torch.nn.parallel import DistributedDataParallel as DDP +from torch import distributed as dist +import os +import pickle + +import logging +logger = logging.getLogger('rl_circuit') + +from rl_arb.net import PolicyNet +from rl_arb.mcts import MCTSParallel, PMemory +from rl_arb.config import ( + DEVICE, + TELEGRAM_TOKEN, + TELEGRAM_CHAT_ID, + ARGS_MODEL, +) +from rl_arb.utils import ( + update_me, + send_telegram_message, +) + + +class AgentRLearn(): + def __init__(self, model, mdp, args): + """ + RL algorithm class for training and self-play using Monte Carlo Tree + Search (MCTS). + + Parameters + ---------- + model : torch.nn.Module + The neural network model used for policy and value prediction. + mdp : MDP + The MDP environment that provides the logic and state transitions. + args : dict + A dictionary of arguments and hyperparameters for training and self-play. + + Methods + ------- + self_play() + Executes self-play to generate training data. + train(memory) + Trains the model using the training data from self_play. + track_baseline(values, gamma_factors, blocks) + Updates the baseline_tracker and give baseline values for current iteration. + """ + self.model = model + self.args = args + self.mcts = MCTSParallel(mdp, self.args) + + self.pbar_play = False + self.values = [] + self.baseline_tracker = {} + self.avg_itr_value = [0.0] + self.test_values= [] + + def self_play(self): + """ + Executes self-play to generate training data. + + Returns + ------- + list + A list of tuples containing mdp states, policy probabilities, + value estimates, and block indices. + """ + return_mem = [] + at_block = np.random.choice(self.mcts.mdp.num_blocks) + self.mcts.mdp.current_block = at_block + p_memory = [PMemory(self.mcts.mdp, np.random.choice(self.mcts.mdp.num_blocks)) for _ in range(self.args['num_parallel'])] + + if self.pbar_play: + pbar = tqdm( + total=self.args['num_parallel'], + desc='self_play', + token=str(TELEGRAM_TOKEN), + chat_id=str(TELEGRAM_CHAT_ID), + disable=not self.args['telegram'] + ) + + while len(p_memory) > 0: + + states = [mem.state for mem in p_memory] + if self.pbar_play: + pbar.set_description(f"self_play state_len {len(states[0])}") + + self.mcts.search(self.model, states, p_memory) + + for i in range(len(p_memory))[::-1]: + mem = p_memory[i] + probs = np.zeros(len(self.mcts.mdp.edge_list)) + + for child in mem.root.children: + probs[ + self.mcts.mdp.edge_list.index(child.action_taken) + ] = child.value_best + + if np.sum(probs) == 0: + for child in mem.root.children: + probs[ + self.mcts.mdp.edge_list.index(child.action_taken) + ] = child.visit_count + + probs /= np.sum(probs) + + mem.memory.append(( + mem.state.copy(), + probs, + mem.current_block + )) + + action = self.mcts.mdp.edge_list[np.random.choice(len(probs), p=probs)] + mem.state = self.mcts.mdp.get_next_state(mem.state, action) + + value, is_terminal = self.mcts.mdp.get_value_and_terminated( + mem.state, + mem.current_block + ) + + if is_terminal: + if self.pbar_play: + pbar.update(1) + for hist_state, hist_probs, current_block in mem.memory: + return_mem.append(( + hist_state, + hist_probs, + value, + self.args['gamma']**(len(mem.state)-(len(hist_state)+1)), + action, + current_block, + )) + del p_memory[i] + + return return_mem + + def track_baseline(self, values, gamma_factors, blocks): + bs = np.array(blocks) + vs = np.array(values) + baseline = np.zeros_like(bs, dtype=float) + + for block in np.unique(bs): + idxs = np.where(bs==block)[0] + mean_val = np.mean(vs[idxs]) + + if block not in self.baseline_tracker: + self.baseline_tracker[block] = mean_val + else: + self.baseline_tracker[block] = 1/2 * (mean_val + self.baseline_tracker[block]) + + baseline[idxs] = self.baseline_tracker[block] + + + return baseline*np.array(gamma_factors) + + + def learn(self, optimizer): + """ + Main loop for the learning process, including self-play and training. + """ + torch.save(self.model.state_dict(), "./model/model_0.pt") + torch.save(optimizer.state_dict(), "./model/optimizer_0.pt") + for iteration in range(self.args['num_iterations']): + logger.info(f"Iterations: {iteration+1}/{self.args['num_iterations']}") + if self.args['telegram']: + send_telegram_message(f"Iterations: {iteration+1}/{self.args['num_iterations']}") + + memory = [] + self.model.eval() + if not self.args['multicore']: + # single core self-play + self.model.to(DEVICE) + self.mcts.mdp.device = DEVICE + for play_iter in tqdm( + range(self.args['num_self_play_iterations']//self.args['num_parallel']), + desc="self play", + token=str(TELEGRAM_TOKEN), + chat_id=str(TELEGRAM_CHAT_ID), + disable=not self.args['telegram'], + ): + memory += self.self_play() + + else: + cpu = torch.device('cpu') + self.model.to(cpu) + self.model.load_state_dict( + torch.load( + f"./model/model_{iteration}.pt", + weights_only = True, + map_location=cpu + ) + ) + self.model.share_memory() + self.mcts.mdp.device = cpu + # multicore self-play + play_iter = self.args['num_self_play_iterations'] + num_parallel = self.args['num_parallel'] + num_processes = self.args['num_processes'] + per_processor = play_iter//num_parallel//num_processes + # at each self play choose prices from a different block number + with mp.Pool(processes=num_processes) as pool: + results = pool.starmap( + self_play_num_times, + [(self, per_processor, 'cuda:0', True)] +\ + [(self, per_processor, 'cuda:0', False) for _ in range(num_processes//2 - 1)] +\ + [(self, per_processor, 'cuda:1', False) for _ in range(num_processes//2 - 1)] +\ + [(self, per_processor, 'cuda:1', True)] + ) + pool.terminate() + + for result in results: + memory += result + + states, _, values, gamma_factors, _, blocks = zip(*memory) + baseline = self.track_baseline(values, gamma_factors, blocks) + + memory = [(*it, float(baseline[i])) for i, it in enumerate(memory)] + + terminal_idxs = np.where(np.array(gamma_factors)==1)[0] + terminal_values = np.array(values)[terminal_idxs] + terminal_states = np.array([len(s)+1 for s in states])[terminal_idxs] + + self.values.append((np.mean(terminal_values), np.mean(terminal_states))) + if self.args['telegram']: + send_telegram_message(f""" + Average values {np.mean(terminal_values)} + Average state_len {np.mean(terminal_states)} + """) + + world_size = torch.cuda.device_count() + mp.spawn( + train, + args=(world_size, memory, self.mcts, iteration), + nprocs=world_size, + join=True + ) + + with open(f"./baseline/baseline_{iteration}.pickle", "wb") as f: + pickle.dump(self.baseline_tracker, f) + + with open("values.pickle", "wb") as f: + pickle.dump(self.values, f) + + test_values = self.test_model(iteration) + self.test_values.append(test_values) + average_value = float(np.mean(test_values)) + with open("test_values.pickle", "wb") as f: + pickle.dump(self.test_values, f) + + send_telegram_message(f"Average profit {average_value}") + + if self.args['telegram']: + send_telegram_message("DONE!") + + + def test_model(self, iteration): + cpu = torch.device('cpu') + self.model.to(cpu) + self.model.load_state_dict( + torch.load( + f"./model/model_{iteration+1}.pt", + weights_only = True, + map_location=cpu + ) + ) + self.model.share_memory() + self.mcts.mdp.device = cpu + # multicore self-play + play_iter = self.args['num_self_play_iterations'] + num_parallel = self.args['num_parallel'] + num_processes = play_iter//num_parallel + per_processor = play_iter//num_parallel//num_processes + + with mp.Pool(processes=num_processes) as pool: + results = pool.starmap( + test_blocks, + [(self, per_processor, 'cuda:0', True)] +\ + [(self, per_processor, 'cuda:0', False) for _ in range(num_processes//2 - 1)] +\ + [(self, per_processor, 'cuda:1', False) for _ in range(num_processes//2 - 1)] +\ + [(self, per_processor, 'cuda:1', True)] + ) + pool.terminate() + + values =[] + for result in results: + values += result + + return values + + def test_play(self): + """ + Executes self-play to generate training data. + + Returns + ------- + list + A list of tuples containing mdp states, policy probabilities, + value estimates, and block indices. + """ + return_mem = [] + p_memory = [PMemory(self.mcts.mdp, np.random.choice(self.mcts.mdp.num_blocks)) for _ in range(self.args['num_parallel'])] + + if self.pbar_play: + pbar = tqdm( + total=self.args['num_parallel'], + desc='test_play', + token=str(TELEGRAM_TOKEN), + chat_id=str(TELEGRAM_CHAT_ID), + disable=not self.args['telegram'] + ) + + while len(p_memory) > 0: + states = [mem.state for mem in p_memory] + + if self.pbar_play: + pbar.set_description(f"test_play state_len {len(states[0])}") + + self.mcts.search(self.model, states, p_memory) + + for i in range(len(p_memory))[::-1]: + mem = p_memory[i] + probs = np.zeros(len(self.mcts.mdp.edge_list)) + for child in mem.root.children: + probs[ + self.mcts.mdp.edge_list.index(child.action_taken) + ] = child.value_best + + if np.sum(probs) == 0: + for child in mem.root.children: + probs[ + self.mcts.mdp.edge_list.index(child.action_taken) + ] = child.visit_count + + probs /= np.sum(probs) + + action = self.mcts.mdp.edge_list[np.argmax(probs)] + mem.state = self.mcts.mdp.get_next_state(mem.state, action) + + value, is_terminal = self.mcts.mdp.get_value_and_terminated( + mem.state, + mem.current_block + ) + + if is_terminal: + if self.pbar_play: + pbar.update(1) + return_mem.append(value) + del p_memory[i] + + return return_mem + + def override_update(self, iteration): + + self.model.load_state_dict( + torch.load( + f"./model/model_{iteration}.pt", + weights_only = True, + ) + ) + optimizer = torch.optim.Adam(self.model.parameters(), lr=0.01) + optimizer.load_state_dict( + torch.load( + f"./model/optimizer_{iteration}.pt", + weights_only = True, + ) + ) + + torch.save(self.model.state_dict(), f"./model/model_{iteration+1}.pt") + torch.save(optimizer.state_dict(), f"./model/optimizer_{iteration+1}.pt") + + +def test_blocks(rlearn, times=100, device='cpu', pbar=False): + """ + Helper function to perform self-play multiple times while testing. + + Parameters + ---------- + rlearn: AgentRLearn + The AgentRLearn instance to use for self-play. + times : int, optional + The number of self-play iterations to perform (default is 100). + device: string, optional + Device to execute the code. + pbar: bool, optional + Print a tqdm bar or not. + + Returns + ------- + list + A list of tuples containing the rewards. + """ + values = [] + + rlearn.pbar_play = pbar + rlearn.model.to(device) + rlearn.mcts.mdp.device = device + for i in range(times): + values += rlearn.test_play() + return values + + +def self_play_num_times(rlearn, times=100, device='cpu', pbar=False): + """ + Helper function to perform self-play multiple times. + + Parameters + ---------- + rlearn: AgentRLearn + The AgentRLearn instance to use for self-play. + times : int, optional + The number of self-play iterations to perform (default is 100). + device: string, optional + Device to execute the code. + pbar: bool, optional + Print a tqdm bar or not. + + Returns + ------- + list + A list of tuples containing mdp states, policy probabilities, value estimates, and block indices. + """ + memory = [] + + rlearn.pbar_play = pbar + rlearn.model.to(device) + rlearn.mcts.mdp.device = device + for _ in range(times): + memory += rlearn.self_play() + return memory + + +def train(rank, world_size, memory, mcts, iteration): + """ + Distributed multi gpu training in pytorch. + + Parameters + ---------- + world_size: int + Number of cpus + memory : list + A list of containing mdp states, policy probabilities, + value estimates, and block indices. + mcts: MCTSParallel + Monte Carlo Tree search object. + iteration: int + Current search iteration + """ + os.environ['MASTER_ADDR'] = 'localhost' + os.environ['MASTER_PORT'] = '12355' + dist.init_process_group('nccl', rank=rank, world_size=world_size) + + dist.barrier() + model = PolicyNet(ARGS_MODEL) + model.load_state_dict( + torch.load( + f"./model/model_{iteration}.pt", + weights_only = True, + ) + ) + model.to(rank) + optimizer = torch.optim.Adam(model.parameters(), lr=0.01) + optimizer.load_state_dict( + torch.load( + f"./model/optimizer_{iteration}.pt", + weights_only = True, + ) + ) + + model = DDP(model, device_ids=[rank]) + + lm = len(memory) + memory = [memory[i: i+lm//world_size] for i in range(0, lm, lm//world_size)][rank] + + epoch_loss = [] + avg_state_len = [] + avg_rewards = [] + for epoch_iter in tqdm( + range(mcts.args['num_epochs']), + desc="epochs", + token=str(TELEGRAM_TOKEN), + chat_id=str(TELEGRAM_CHAT_ID), + disable=(not mcts.args['telegram'] and rank != 0), + ): + np.random.shuffle(memory) + for batch_idx in range(0, len(memory), mcts.args['batch_size']): + sample = memory[batch_idx:np.min([len(memory) - 1, batch_idx + mcts.args['batch_size']])] + try: + states , policy_targets, values, gamma_factors, actions, block_indices, baseline = zip(*sample) + except ValueError: # batch is len 0. + print(f"batch_idx {batch_idx}") + print(f"From-TO : {[batch_idx, np.min([len(memory) - 1, batch_idx + mcts.args['batch_size']])]}") + print(f"len memory {len(memory)}") + print(f"len sample {len(sample)}") + continue + + policy_targets, value_targets = np.array(policy_targets), np.array(values)*np.array(gamma_factors) + policy_targets = torch.tensor(policy_targets).to(rank).float() + value_targets = torch.tensor(value_targets).to(rank).float() + baseline = torch.tensor(baseline).to(rank) + + data_list = [ + Data( + x=mcts.mdp.encode_state(s, b), + edge_index=mcts.mdp.data.edge_index, + y=mcts.mdp.encode_state(s[:1], b), + )\ + for s, b in zip(states, block_indices) + ] + batch = Batch.from_data_list(data_list).to(rank) + + policy_outs = model.forward( + batch.x, + batch.edge_index, + batch.y, + batch.batch, + ) + + del batch + del data_list + + one_hot = torch.zeros_like(policy_outs).to(rank) + for a, p in zip(actions, one_hot): + p[mcts.mdp.edge_list.index(a)] = 1 + + cross_entropy = torch.sum(torch.log(policy_outs) * one_hot, dim=1) + loss = -torch.mean(cross_entropy * (value_targets - baseline)) + + epoch_loss.append(loss.item()) + avg_rewards.append(value_targets.mean().item()) + avg_state_len.append( + np.mean(np.array([len(s) for s in states])) + ) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + if mcts.args['telegram'] and rank == 0: + update_me( + np.mean(epoch_loss), + np.mean(avg_state_len), + np.mean(avg_rewards), + epoch_iter, + iteration, + ) + + if rank == 0: + torch.save(model.module.state_dict(), f"./model/model_{iteration+1}.pt") + torch.save(optimizer.state_dict(), f"./model/optimizer_{iteration+1}.pt") + + dist.destroy_process_group() diff --git a/rl_arb/rl_arb/utils.py b/rl_arb/rl_arb/utils.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python3 + +import pandas as pd +import networkx as nx +import numpy as np +import torch +import os +import pickle +import requests +from torch_geometric.nn import summary + +from rl_arb.config import TELEGRAM_CHAT_ID, TELEGRAM_SEND_URL, DEVICE +from rl_arb.logger import logging +logger = logging.getLogger('rl_circuit') + +def load_pools_and_tokens(path_pools, path_tokens): + """ + Load pool & token data into pd.DataFrame/s from csv files. + + Parameters + ---------- + path_pools : str + File path to csv file for the pools + path_tokens : str + File path to csv file for the tokens + + Returns + ------- + tuple + A tuple containing: + - pools (pd.DataFrame): pool data. + - tokens (pd.DataFrame): token data. + """ + pools = pd.read_csv( + path_pools, + names = ['index', 'address', 'version', 'token0', 'token1', 'fee', 'block_number', 'time_stamp', 'tick_spacing'], + header = None, + ).sort_index().drop_duplicates() + tokens = pd.read_csv( + path_tokens, + header = None, + names = ['index', 'address', 'name', 'symbol', 'decimals'] + ).sort_index().drop_duplicates() + + + return pools, tokens + + +def make_price(price): + """ + Calculate the price from price pd.DataFrame + Uniswapv3 price is t1/t0 -> sqrt_price_x96 = sqrt(reserve1/reserve0) * 2**96 + Uniswapv2 price we will define also as t1/t0 + + Parameters + ---------- + price : pd.DataFrame + + Returns + ------- + list: prices. + """ + block_price = [] + for _, p in price.iterrows(): + if (spx96 := p['sqrt_price_x96']) != None: + block_price.append((int(spx96) / 2**96)**2) + else: + t0 = int(p['reserve_t0']) + t1 = int(p['reserve_t1']) + if t0 == 0: + price = 0 + else: + price = t1/t0 + block_price.append(price) + return block_price + + +def pools_to_edge_list(pools, prices): + """ + Makes an edge list from pools & prices for the problem graph. + + Parameters + ---------- + pools : pd.DataFrame + pool data + prices : pd.DataFrame + price data + + Returns + ------- + list + List of edges in the form of a tuple (token0, token1, attributes). + Where the attributes is a type dict with keys: + - 'k' (int): Repeated count of the pool with the same tokens. + - 'weight' (list): The historical prices of the specific pool. + """ + edge_list = [] + cache = [] + for (_, pool) in pools.iterrows(): + + t0 = pool['token0'] + t1 = pool['token1'] + p = make_price(prices[prices['pool_address'] == pool['address']]) + + + k = 0 + for e in cache: + if (t0, t1) == e or (t1, t0) == e: + k += 1 + edge_list.append( + (t0, t1, + {'k': k, 'weight': p, 'address': pool['address'], 'fee': int(pool['fee'])/1e6}) + ) + cache.append((t0, t1)) + + return edge_list + + +def make_token_graph(pools, prices): + """ + Make a directed multi graph from pool and price data. + + Parameters + ---------- + pools : pd.DataFrame + Pool data. + prices : pd.DataFrame + Price data. + + Returns + ------- + nx.MultiDiGraph + A directed multi graph. Nodes represent tokens + edges represent pools with attributes generated + by the function pools_to_edge_list + """ + edge_list = pools_to_edge_list(pools, prices) + + G = nx.MultiDiGraph() + G.add_edges_from(edge_list) + return G + +def linear_node_relabel(G): + """ + Relabel the nodes linearly. Input node labels + are ETH addresses which are relabeled in a chronological order + to integer values starting from 0. + + Parameters + ---------- + G : nx.MultiDiGraph + Input graph. + + Returns + ------- + tuple + A tuple containing: + - G (nx.MultiDiGraph): Graph with relabeled nodes (automatically edges). + - mapping (dict): Mapping dictionary. + """ + mapping = {} + inv_mapping = {} + nodes = list(G.nodes()) + for i, node in enumerate(nodes): + mapping[node] = i + inv_mapping[i] = node + G = nx.relabel_nodes(G, mapping) + return G, mapping + + +def filter_pools_with_no_gradient(pools, prices): + """ + Filter out pools that have no change in price by computing the gradient of + the historical prices. + + Parameters + ---------- + pools : pd.DataFrame + Pool data. + prices : pd.DataFrame + Price data + + Returns + ------- + tuple + A tuple containing: + - filtered_pools (pd.DataFrame): Filtered pool data. + - filtered_prices (pd.DataFrame): Filtered price data. + """ + pools = pools[pools['address'].isin(set(prices['pool_address']))] + ticks = len(prices['block_number'].unique()) + mask = [] + for _, pool in pools.iterrows(): + t0 = pool['token0'] + t1 = pool['token1'] + p = make_price(prices[prices['pool_address'] == pool['address']]) + + pbn = len(list( + prices[prices['pool_address'] == pool['address']]['block_number'] + )) + if pbn != ticks or np.count_nonzero(np.gradient(p)) < ticks*2//3: + mask.append(False) + else: + mask.append(True) + + + pools = pools[mask] + prices = prices[prices['pool_address'].isin(list(pools['address']))] + return pools, prices + +def save_loss( + loss, + avg_state_len +): + if not os.path.exists('./model/loss'): + os.mkdir('./model/loss') + + with open(f'./model/loss/loss.pickle', "wb") as f: + pickle.dump(loss, f) + + with open(f'./model/loss/avg_state_len.pickle', "wb") as f: + pickle.dump(avg_state_len, f) + + +def update_me( + loss, + avg_state_len, + avg_rewards, + epoch_iter, + iteration, +): + """ + Sends a message through a telegram bot on current training progress + + Parameters + ---------- + loss: float + Loss function mean over the epoch. + avg_state_len: float + Average length of the states. + avg_rewards: float + Average rewards of the states. + epoch_ter: int + Current epoch. + iteration: int + Current iteration. + """ + + message = f""" + ITR: {iteration+1} | EPOCH: {epoch_iter} + REINFORCE Loss: {loss} + Average state length: {avg_state_len} + Average rewards: {avg_rewards} + """ + send_telegram_message(message) + + +def send_telegram_message(message): + """ + Send a message through a telegram-bot. + + Parameters + ---------- + message: str + String containing the message to be sent by the bot. + """ + requests.post(TELEGRAM_SEND_URL, json={'chat_id': TELEGRAM_CHAT_ID, 'text': message}) + + + +def print_summary(problem): + """ + Prints torchsummary.summary of the model. + + Parameters + ---------- + problem: Initializer + """ + problem.mdp.data.to(DEVICE) + problem.mdp.device = DEVICE + problem.model.to(DEVICE) + + state = [(0, 0, 0), (0, 5, 0)] + e_x = problem.mdp.encode_state(state, 0).to(DEVICE) + y = problem.mdp.encode_state(state, 0).to(DEVICE) + edge_index = problem.mdp.data.edge_index + + print(summary(problem.model, e_x, edge_index, y)) + +