diff --git a/.cargo/config.toml b/.cargo/config.toml index 7520df80..ab6cf419 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,3 @@ [env] RUSTC_BOOTSTRAP = "1" +CARGO_WORKSPACE_DIR = { value = "", relative = true } \ No newline at end of file diff --git a/.gitignore b/.gitignore index 022a78db..264d26c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ /target .local -.vscode - -# bundler spec test -/bundler-spec-test/keys/0x* \ No newline at end of file +.vscode \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 807057b0..042db548 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,39 +16,131 @@ dependencies = [ name = "aa-bundler" version = "0.1.0" dependencies = [ + "aa-bundler-grpc", + "aa-bundler-primitives", + "aa-bundler-rpc", + "anyhow", + "clap 4.2.4", + "dirs 4.0.0", + "ethers", + "expanded-pathbuf", + "jsonrpsee", + "tokio", + "tracing-subscriber", +] + +[[package]] +name = "aa-bundler-bundler" +version = "0.1.0" +dependencies = [ + "aa-bundler-contracts", + "aa-bundler-primitives", + "anyhow", + "ethers", + "tracing", +] + +[[package]] +name = "aa-bundler-contracts" +version = "0.1.0" +dependencies = [ + "aa-bundler-primitives", + "anyhow", + "ethers", + "ethers-providers", + "lazy_static", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "aa-bundler-grpc" +version = "0.1.0" +dependencies = [ + "aa-bundler-bundler", + "aa-bundler-contracts", + "aa-bundler-primitives", + "aa-bundler-uopool", "anyhow", "arrayref", "async-trait", - "clap 4.1.13", + "clap 4.2.4", "dashmap", - "dirs", + "ethers", + "parking_lot", + "prost", + "prost-build", + "protobuf-src", + "serde_json", + "tokio", + "tonic", + "tonic-build", + "tracing", +] + +[[package]] +name = "aa-bundler-primitives" +version = "0.1.0" +dependencies = [ + "anyhow", "educe", - "ethereum-interfaces", "ethers", - "ethers-providers", "expanded-pathbuf", - "hex", + "jsonrpsee", + "rustc-hex", + "serde", + "serde_json", +] + +[[package]] +name = "aa-bundler-rpc" +version = "0.1.0" +dependencies = [ + "aa-bundler-grpc", + "aa-bundler-primitives", + "anyhow", + "async-trait", + "ethers", + "jsonrpsee", + "serde_json", + "tonic", + "tracing", +] + +[[package]] +name = "aa-bundler-tests" +version = "0.1.0" +dependencies = [ + "aa-bundler-contracts", + "aa-bundler-primitives", + "anyhow", + "ethers", + "tempdir", + "tokio", +] + +[[package]] +name = "aa-bundler-uopool" +version = "0.1.0" +dependencies = [ + "aa-bundler-contracts", + "aa-bundler-primitives", + "anyhow", + "educe", + "ethers", "jsonrpsee", "lazy_static", "page_size 0.5.0", - "parking_lot", "prost", - "prost-build", - "protobuf-src", - "regex", "reth-db", "reth-libmdbx", - "ron", - "rustc-hex", "serde", - "serde_json", "tempdir", - "thiserror", "tokio", - "tonic", - "tonic-build", "tracing", - "tracing-subscriber", ] [[package]] @@ -111,6 +203,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -120,6 +221,55 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6342bd4f5a1205d7f41e94a41a901f5647c938cdfa96036338e8533c9d6c2450" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.70" @@ -149,9 +299,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad445822218ce64be7a341abfb0b1ea43b5c23aa83902542a4542e78309d8e5e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", @@ -160,13 +310,13 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -177,7 +327,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.10", + "syn 2.0.15", ] [[package]] @@ -240,9 +390,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.6.12" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f8ccfd9221ee7d1f3d4b33e1f8319b3a81ed8f61f2ea40b37b859794b4491" +checksum = "b70caf9f1b0c045f7da350636435b775a9733adf2df56e8aa2a29210fbc335d4" dependencies = [ "async-trait", "axum-core", @@ -268,9 +418,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f958c80c248b34b9a877a643811be8dbca03ca5ba827f2b63baf3a81e5fc4e" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", "bytes", @@ -415,9 +565,9 @@ dependencies = [ [[package]] name = "block-padding" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a90ec2df9600c28a01c56c4784c9207a96d2451833aeceb8cc97e4c9548bb78" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ "generic-array", ] @@ -449,9 +599,9 @@ checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "byte-slice-cast" @@ -515,9 +665,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", @@ -585,9 +735,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -613,17 +763,26 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.13" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c911b090850d79fc64fe9ea01e28e465f65e821e08813ced95bced72f7a8a9b" +checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" dependencies = [ - "bitflags", - "clap_derive 4.1.12", - "clap_lex 0.3.3", - "is-terminal", + "clap_builder", + "clap_derive 4.2.0", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex 0.4.1", "strsim", - "termcolor", ] [[package]] @@ -641,14 +800,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.1.12" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a932373bab67b984c790ddf2c9ca295d8e3af3b7ef92de5a5bacdccdee4b09b" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.10", + "syn 2.0.15", ] [[package]] @@ -662,12 +821,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "cobs" @@ -685,7 +841,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.10", + "syn 2.0.15", ] [[package]] @@ -719,21 +875,19 @@ dependencies = [ [[package]] name = "coins-bip39" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb68f3b6c3fee83828ecd8d463f360a397c32aaeb35bd931c01e5ddf5631c69" +checksum = "84f4d04ee18e58356accd644896aeb2094ddeafb6a713e056cef0c0a8e468c15" dependencies = [ "bitvec 0.17.4", "coins-bip32", "getrandom", - "hex", "hmac", "once_cell", "pbkdf2 0.12.1", "rand 0.8.5", "sha2 0.10.6", "thiserror", - "tracing", ] [[package]] @@ -756,6 +910,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "console" version = "0.14.1" @@ -812,15 +972,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -857,9 +1017,9 @@ checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -959,7 +1119,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.10", + "syn 2.0.15", ] [[package]] @@ -976,7 +1136,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.10", + "syn 2.0.15", ] [[package]] @@ -1029,9 +1189,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.1" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc906908ea6458456e5eaa160a9c08543ec3d1e6f71e2235cedd660cb65f9df0" +checksum = "05e58dffcdcc8ee7b22f0c1f71a69243d7c2d9ad87b5a14361f2424a1565c219" dependencies = [ "const-oid", "zeroize", @@ -1084,6 +1244,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle", ] @@ -1094,7 +1255,16 @@ version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ - "dirs-sys", + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dece029acd3353e3a58ac2e3eb3c8d6c35827a892edc6cc4138ef9c33df46ecd" +dependencies = [ + "dirs-sys 0.4.0", ] [[package]] @@ -1118,6 +1288,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b" +dependencies = [ + "libc", + "redox_users", + "windows-sys 0.45.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1131,17 +1312,18 @@ dependencies = [ [[package]] name = "dunce" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "ecdsa" -version = "0.16.1" +version = "0.16.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1b0a1222f8072619e8a6b667a854020a03d363738303203c09468b3424a420a" +checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" dependencies = [ "der", + "digest 0.10.6", "elliptic-curve", "rfc6979", "signature", @@ -1149,9 +1331,9 @@ dependencies = [ [[package]] name = "educe" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0188e3c3ba8df5753894d54461f0e39bc91741dc5b22e1c46999ec2c71f4e4" +checksum = "4af7804abe0786a9b69375115821fedc9995f21ab63ae285184b96b01ec50b1a" dependencies = [ "enum-ordinalize", "proc-macro2", @@ -1167,9 +1349,9 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" -version = "0.13.2" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea5a92946e8614bb585254898bb7dd1ddad241ace60c52149e3765e34cc039d" +checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" dependencies = [ "base16ct", "crypto-bigint", @@ -1248,18 +1430,18 @@ checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" dependencies = [ "proc-macro2", "quote", - "syn 2.0.10", + "syn 2.0.15", ] [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1326,21 +1508,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "ethereum-interfaces" -version = "0.1.0" -source = "git+https://github.com/ledgerwatch/interfaces#a26177c1499e7c40294a771e9701f316e1613a35" -dependencies = [ - "arrayref", - "ethereum-types", - "ethnum", - "prost", - "prost-build", - "protobuf-src", - "tonic", - "tonic-build", -] - [[package]] name = "ethereum-types" version = "0.14.1" @@ -1359,13 +1526,13 @@ dependencies = [ [[package]] name = "ethers" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c090b5c2d580dba00df2b3a3c79133ad45a1fdc4a16182f1dfb4b1dbec16d266" +checksum = "356eaf2e3947efa975c3f953e33f73193e5e21b9d8bab26c3ca532676931696f" dependencies = [ "ethers-addressbook", "ethers-contract", - "ethers-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethers-core 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethers-etherscan", "ethers-middleware", "ethers-providers", @@ -1375,11 +1542,11 @@ dependencies = [ [[package]] name = "ethers-addressbook" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12117b891bb7a568c6f827b78c030bb10d13561e14f2ceea6d74ac01850b413a" +checksum = "d56f1c92cb7c406794c8324158da4caf9e54018e28b776df8155085e1d06db75" dependencies = [ - "ethers-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethers-core 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "once_cell", "serde", "serde_json", @@ -1387,13 +1554,13 @@ dependencies = [ [[package]] name = "ethers-contract" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0d30b017666eb5f8763fefd72252a695ae96c953444bc2eae7fd90d044f1d9" +checksum = "2a328cb42014ac0ac577a8dac32eb658ee0f32b5a9a5317a0329ac1d4201f1c6" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", - "ethers-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethers-core 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethers-providers", "futures-util", "hex", @@ -1406,25 +1573,25 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33febb0b50573d9348866fe896e0684e885412b1a82d1eafc4c91be8d287a46" +checksum = "d96813e4b58b6c6b817367380db900dddbd67bfe27610ec89fd3263778d5a4aa" dependencies = [ "Inflector", "dunce", - "ethers-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethers-core 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethers-etherscan", "eyre", "getrandom", "hex", - "prettyplease", + "prettyplease 0.2.4", "proc-macro2", "quote", "regex", "reqwest", "serde", "serde_json", - "syn 1.0.109", + "syn 2.0.15", "tokio", "toml", "url", @@ -1433,29 +1600,30 @@ dependencies = [ [[package]] name = "ethers-contract-derive" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f69a3681044fc474fb6748c12902ce9c7a6a8958c8d02693d98e7fab4c734a7" +checksum = "373068bb24b4dea8fe0d1758aadab2dd4ec9de1d2c28316439cadcda3ed48eae" dependencies = [ + "Inflector", "ethers-contract-abigen", - "ethers-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethers-core 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "hex", "proc-macro2", "quote", - "syn 1.0.109", + "serde_json", + "syn 2.0.15", ] [[package]] name = "ethers-core" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a73bf2bbd4e19fd5c5dca74ea68d8799acc87f0e30ccda6609e79d7559f7317" +checksum = "3a5f8f85ba96698eab9a4782ed2215d0979b1981b99f1be0726c200ffdac22f5" dependencies = [ "arrayvec", "bytes", "cargo_metadata", "chrono", - "convert_case 0.6.0", "elliptic-curve", "ethabi", "generic-array", @@ -1465,13 +1633,12 @@ dependencies = [ "num_enum", "once_cell", "open-fastrlp", - "proc-macro2", "rand 0.8.5", "rlp", "serde", "serde_json", "strum", - "syn 1.0.109", + "syn 2.0.15", "tempfile", "thiserror", "tiny-keccak", @@ -1480,8 +1647,8 @@ dependencies = [ [[package]] name = "ethers-core" -version = "2.0.1" -source = "git+https://github.com/gakonst/ethers-rs#36dac5864c657719f1b134ccd100bb3bc99e5883" +version = "2.0.3" +source = "git+https://github.com/gakonst/ethers-rs#df5c8fb4e0b424a2c2e08e81f052055fac418eaf" dependencies = [ "arrayvec", "bytes", @@ -1507,11 +1674,11 @@ dependencies = [ [[package]] name = "ethers-etherscan" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f09835b5e52664cb08e5e4803509588b3c5fa81bb94f034d3b749eeeb2c41e3" +checksum = "7cfc3d36be9e16bac241e1d59ec9ace00e8e4241c09f604a0f65158eb37d4878" dependencies = [ - "ethers-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethers-core 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethers-solc", "getrandom", "reqwest", @@ -1524,14 +1691,14 @@ dependencies = [ [[package]] name = "ethers-middleware" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422b0b96262a461c92fd80e74c94a2c7aa9b8d74cd50b20c97990301c9aa9dab" +checksum = "1e6f3543b4f6679b2558901c4b323cecb06b19239439d2588fa8b489bac9675d" dependencies = [ "async-trait", "auto_impl", "ethers-contract", - "ethers-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethers-core 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethers-etherscan", "ethers-providers", "ethers-signers", @@ -1551,16 +1718,16 @@ dependencies = [ [[package]] name = "ethers-providers" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f366585debaab7ee0e0ea56a6fd3745a37f44357962bc33fee9a5a3ecb1410d" +checksum = "7c9d2cbed43cf618004dbe339e389e10dae46ea8e55872ab63a25fad25a6082a" dependencies = [ "async-trait", "auto_impl", "base64 0.21.0", "bytes", "enr", - "ethers-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethers-core 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures-channel", "futures-core", "futures-timer", @@ -1589,16 +1756,16 @@ dependencies = [ [[package]] name = "ethers-signers" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af32f2a3c0491636f79e63fff658ad48caace87926d9290689322f415fa917ae" +checksum = "d56871f70b97cc5cced5c39c88a0196c206d8f7fb8e1e0952fbf1fb73c033219" dependencies = [ "async-trait", "coins-bip32", "coins-bip39", "elliptic-curve", "eth-keystore", - "ethers-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethers-core 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "hex", "rand 0.8.5", "sha2 0.10.6", @@ -1608,13 +1775,13 @@ dependencies = [ [[package]] name = "ethers-solc" -version = "2.0.1" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c1f58948c120a475c3d83f63b9685e541cc101fac72034f70028d4b6ee3d2d6" +checksum = "6e967e60bca8fc83d640a7aebd1492d625c43e86b88a5a5bb08b1019472d5d6b" dependencies = [ "cfg-if", "dunce", - "ethers-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethers-core 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util", "getrandom", "glob", @@ -1641,17 +1808,11 @@ dependencies = [ "yansi", ] -[[package]] -name = "ethnum" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0198b9d0078e0f30dedc7acbb21c974e838fc8fae3ee170128658a98cb2c1c04" - [[package]] name = "expanded-pathbuf" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8bc789856dd90157bf3f040ea81e3a2b75559b026d2d5c987b5bb94670b55c" +checksum = "5e87ea7f4db701f0c2c0be7866dd733dbf7ee82ec2e2936280699a601799f40e" dependencies = [ "anyhow", "derive_more", @@ -1754,9 +1915,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -1769,9 +1930,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -1779,15 +1940,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -1796,9 +1957,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-locks" @@ -1812,26 +1973,26 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-timer" @@ -1845,9 +2006,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -1872,9 +2033,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1883,9 +2044,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "js-sys", @@ -1906,7 +2067,7 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.20", "bstr", "fnv", "log", @@ -1938,9 +2099,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.16" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -2005,7 +2166,7 @@ dependencies = [ "hash32", "rustc_version", "serde", - "spin 0.9.6", + "spin 0.9.8", "stable_deref_trait", ] @@ -2108,9 +2269,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -2157,9 +2318,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.54" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2283,31 +2444,31 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "ipnet" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2433,9 +2594,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955890845095ccf31ef83ad41a05aabb4d8cc23dc3cac5a9f5c89cf26dd0da75" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", "ecdsa", @@ -2456,9 +2617,9 @@ dependencies = [ [[package]] name = "lalrpop" -version = "0.19.9" +version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34313ec00c2eb5c3c87ca6732ea02dcf3af99c3ff7a8fb622ffb99c9d860a87" +checksum = "06ada7ece1f5bc6d36eec2a4dc204135f14888209b3773df8fefcfe990fd4cbc" dependencies = [ "ascii-canvas", "bit-set", @@ -2470,7 +2631,7 @@ dependencies = [ "petgraph", "pico-args", "regex", - "regex-syntax", + "regex-syntax 0.6.29", "string_cache", "term", "tiny-keccak", @@ -2479,9 +2640,9 @@ dependencies = [ [[package]] name = "lalrpop-util" -version = "0.19.9" +version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c1f7869c94d214466c5fd432dfed12c379fd87786768d36455892d46b18edd" +checksum = "3d3b45d694c8074f77bc24fc26e47633c862a9cd3b48dd51209c02ba4c434d68" dependencies = [ "regex", ] @@ -2500,9 +2661,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "libloading" @@ -2525,9 +2686,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" [[package]] name = "lock_api" @@ -2737,23 +2898,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -2876,7 +3037,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -3005,9 +3166,9 @@ dependencies = [ [[package]] name = "pico-args" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" @@ -3043,9 +3204,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d2820d87d2b008616e5c27212dd9e0e694fb4c6b522de06094106813328cb49" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", "spki", @@ -3105,6 +3266,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prettyplease" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" +dependencies = [ + "proc-macro2", + "syn 2.0.15", +] + [[package]] name = "primitive-types" version = "0.12.1" @@ -3155,18 +3326,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "prost" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48e50df39172a3e7eb17e14642445da64996989bc212b583015435d39a58537" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", "prost-derive", @@ -3174,9 +3345,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c828f93f5ca4826f97fedcbd3f9a536c16b12cff3dbbb4a007f932bbad95b12" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", "heck", @@ -3185,7 +3356,7 @@ dependencies = [ "log", "multimap", "petgraph", - "prettyplease", + "prettyplease 0.1.25", "prost", "prost-types", "regex", @@ -3196,9 +3367,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea9b0f8cbe5e15a8a042d030bd96668db28ecb567ec37d691971ff5731d2b1b" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", @@ -3209,9 +3380,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379119666929a1afd7a043aa6cf96fa67a6dce9af60c88095a4686dbce4c9c88" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ "prost", ] @@ -3344,6 +3515,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -3351,19 +3531,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.7.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ - "aho-corasick", + "aho-corasick 1.0.1", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -3372,6 +3552,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + [[package]] name = "remove_dir_all" version = "0.5.3" @@ -3383,9 +3569,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba30cc2c0cd02af1222ed216ba659cdb2f879dfe3181852fe7c50b1d0005949" +checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64 0.21.0", "bytes", @@ -3491,7 +3677,7 @@ source = "git+https://github.com/paradigmxyz/reth.git?rev=aa6f2cb0610fb4fa0926b4 dependencies = [ "async-trait", "bytes", - "ethers-core 2.0.1 (git+https://github.com/gakonst/ethers-rs)", + "ethers-core 2.0.3 (git+https://github.com/gakonst/ethers-rs)", "futures", "metrics", "pin-project", @@ -3588,7 +3774,7 @@ dependencies = [ "crc", "crunchy", "derive_more", - "ethers-core 2.0.1 (git+https://github.com/gakonst/ethers-rs)", + "ethers-core 2.0.3 (git+https://github.com/gakonst/ethers-rs)", "fixed-hash", "hash-db", "hex", @@ -3634,7 +3820,7 @@ source = "git+https://github.com/paradigmxyz/reth.git?rev=aa6f2cb0610fb4fa0926b4 dependencies = [ "proc-macro2", "quote", - "syn 2.0.10", + "syn 2.0.15", ] [[package]] @@ -3730,22 +3916,11 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "ron" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300a51053b1cb55c80b7a9fde4120726ddf25ca241a1cbb926626f62fb136bff" -dependencies = [ - "base64 0.13.1", - "bitflags", - "serde", -] - [[package]] name = "ruint" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad3a104dc8c3867f653b0fec89c65e00b0ceb752718ad282177a7e0f33257ac" +checksum = "9d470e29e933dac4101180fd6574971892315c414cf2961a192729089687cc9b" dependencies = [ "derive_more", "primitive-types", @@ -3785,16 +3960,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.11" +version = "0.37.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -3850,9 +4025,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61471dff9096de1d8b2319efed7162081e96793f5ebb147e50db10d50d648a4d" +checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97" dependencies = [ "cfg-if", "derive_more", @@ -3862,9 +4037,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219580e803a66b3f05761fd06f1f879a872444e49ce23f73694d26e5a954c7e6" +checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -3908,9 +4083,9 @@ dependencies = [ [[package]] name = "sec1" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" dependencies = [ "base16ct", "der", @@ -3962,29 +4137,29 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.158" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.10", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -4014,9 +4189,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85456ffac572dc8826334164f2fb6fb40a7c766aebe195a2a21ee69ee2885ecf" +checksum = "331bb8c3bf9b92457ab7abecf07078c13f7d270ba490103e84e8b014490cd0b0" dependencies = [ "base64 0.13.1", "chrono", @@ -4030,9 +4205,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cbcd6104f8a4ab6af7f6be2a0da6be86b9de3c401f6e86bb856ab2af739232f" +checksum = "859011bddcc11f289f07f467cc1fe01c7a941daa4d8f6c40d4d1c92eb6d9319c" dependencies = [ "darling", "proc-macro2", @@ -4090,9 +4265,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" dependencies = [ "digest 0.10.6", "keccak", @@ -4109,11 +4284,11 @@ dependencies = [ [[package]] name = "shellexpand" -version = "2.1.2" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" dependencies = [ - "dirs", + "dirs 5.0.0", ] [[package]] @@ -4133,9 +4308,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ "digest 0.10.6", "rand_core 0.6.4", @@ -4205,14 +4380,15 @@ dependencies = [ [[package]] name = "solang-parser" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff87dae6cdccacdbf3b19e99b271083556e808de0f59c74a01482f64fdbc61fc" +checksum = "8c5ead679f39243782be98c2689e592fc0fc9489ca2e47c9e027bd30f948df31" dependencies = [ "itertools", "lalrpop", "lalrpop-util", "phf", + "thiserror", "unicode-xid", ] @@ -4224,18 +4400,18 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.6" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5d6e0250b93c8427a177b849d144a96d5acc57006149479403d7861ab721e34" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ "lock_api", ] [[package]] name = "spki" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0445c905640145c7ea8c1993555957f65e7c46d0535b91ba501bc9bfc85522f" +checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" dependencies = [ "base64ct", "der", @@ -4366,9 +4542,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.10" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aad1363ed6d37b84299588d62d3a7d95b5a5c2d9aad5c85609fda12afaa1f40" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -4399,15 +4575,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -4463,7 +4639,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.10", + "syn 2.0.15", ] [[package]] @@ -4529,14 +4705,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.26.0" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot", @@ -4559,13 +4734,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -4693,7 +4868,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" dependencies = [ - "prettyplease", + "prettyplease 0.1.25", "proc-macro2", "prost-build", "quote", @@ -4747,13 +4922,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -4789,9 +4964,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -4918,6 +5093,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "0.8.2" @@ -5105,11 +5286,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.46.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.0", ] [[package]] @@ -5118,13 +5299,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -5133,7 +5314,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -5142,13 +5332,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -5157,42 +5362,84 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winnow" version = "0.4.1" @@ -5247,9 +5494,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" [[package]] name = "zip" @@ -5292,16 +5539,11 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.7+zstd.1.5.4" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", "pkg-config", ] - -[[patch.unused]] -name = "revm" -version = "3.0.0" -source = "git+https://github.com/bluealloy/revm?rev=3d8ca6641d2e72448c23f4596f769c8fd1c784d1#3d8ca6641d2e72448c23f4596f769c8fd1c784d1" diff --git a/Cargo.toml b/Cargo.toml index 2181ce94..60adfd76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,73 +1,20 @@ -[package] -name = "aa-bundler" -version = "0.1.0" -authors = ["Vid Kersic "] -edition = "2021" -description = "AA - Bundler implementation" -default-run = "bundler" -rust-version = "1.67.1" - -[dependencies] -anyhow = "1" -arrayref = "0.3" -async-trait = "0.1" -clap = { version = "4", features = ["derive"] } -dashmap = "5.4.0" -dirs = "4.0" -educe = { version = "0.4", features = ["Debug", "Default"] } -ethereum-interfaces = { git = "https://github.com/ledgerwatch/interfaces" } -ethers = { version = "2.0.1", features = ["solc-full"] } -ethers-providers = "2.0.1" -expanded-pathbuf = "0.1" -hex = { version = "0.4.3", default-features = false, features = ["std"] } -jsonrpsee = { version = "0.16", features = ["server", "macros"] } -lazy_static = "1.4.0" -page_size = "0.5.0" -parking_lot = "0.12" -prost = "0.11" -regex = "1" -reth-db = { git = "https://github.com/paradigmxyz/reth.git", rev = "aa6f2cb0610fb4fa0926b42cfed7f8ff51e0db8a" } -reth-libmdbx = { git = "https://github.com/paradigmxyz/reth.git", rev = "aa6f2cb0610fb4fa0926b42cfed7f8ff51e0db8a" } -ron = "0.8" -rustc-hex = "^2.0.1" -serde = "1" -serde_json = "1" -thiserror = "1" -tokio = { version = "1.18", features = ["full"] } -tonic = { version = "0.8", default-features = false, features = [ - "codegen", - "prost", - "transport", -] } -tracing = "0.1" -tracing-subscriber = "0.3" - -[dev-dependencies] -tempdir = "0.3.7" - -[build-dependencies] -anyhow = "1" -ethers = { version = "2.0.1", features = ["solc-full"] } -prost-build = "0.11" -protobuf-src = "1.1.0" -tonic-build = "0.8" - -[[bin]] -path = "bin/bundler.rs" -name = "bundler" - -[[bin]] -path = "bin/bundler-uopool.rs" -name = "bundler-uopool" - -[[bin]] -path = "bin/bundler-rpc.rs" -name = "bundler-rpc" - -[[bin]] -path = "bin/create-wallet.rs" -name = "create-wallet" - +[workspace] +members = [ + "bin/bundler", + "crates/bundler", + "crates/contracts", + "crates/grpc", + "crates/primitives", + "crates/rpc", + "crates/uopool", + "tests", +] +default-members = ["bin/bundler"] + +[profile.debug-fast] +inherits = "release" +debug = true + +[patch] [patch.crates-io] -revm = { git = "https://github.com/bluealloy/revm", rev = "3d8ca6641d2e72448c23f4596f769c8fd1c784d1" } revm-primitives = { git = "https://github.com/bluealloy/revm", rev = "3d8ca6641d2e72448c23f4596f769c8fd1c784d1" } diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 00000000..f8e5e5ea --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 00000000..db500a21 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022-2023 Reth Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile index 8ab500f2..f54cba92 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ setup-thirdparty: cd thirdparty/bundler && yarn install --frozen-lockfile --immutable && yarn preprocess && cd ../.. test: - cargo test + cargo test --workspace format: cargo fmt --all diff --git a/README.md b/README.md index ef94a7ee..72bb156d 100644 --- a/README.md +++ b/README.md @@ -73,3 +73,10 @@ make test ## Contact The best place for the discussion is the dedicated [Telegram channel](https://t.me/aabundler). + +## Licenses + +This project is dual-licensed under Apache 2.0 and MIT terms: + +- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT License ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) \ No newline at end of file diff --git a/bin/bundler/Cargo.toml b/bin/bundler/Cargo.toml new file mode 100644 index 00000000..8e88e7a9 --- /dev/null +++ b/bin/bundler/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "aa-bundler" +version = "0.1.0" +authors = ["Vid Kersic "] +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/Vid201/aa-bundler" +readme = "README.md" +description = """ +AA (ERC-4337) Bundler +""" +default-run = "bundler" +rust-version = "1.67.1" + +[dependencies] +aa-bundler-grpc = { path = "../../crates/grpc" } +aa-bundler-primitives = { path = "../../crates/primitives" } +aa-bundler-rpc = { path = "../../crates/rpc" } + +anyhow = "1" +clap = { version = "4", features = ["derive"] } +dirs = "4.0" +ethers = { version = "2.0.1", features = ["solc-full"] } +expanded-pathbuf = "0.1" +jsonrpsee = { version = "0.16", features = ["server", "macros"] } +tokio = { version = "1.18", features = ["full"] } +tracing-subscriber = "0.3" + +[[bin]] +path = "src/bundler.rs" +name = "bundler" + +[[bin]] +path = "src/bundler-uopool.rs" +name = "bundler-uopool" + +[[bin]] +path = "src/bundler-rpc.rs" +name = "bundler-rpc" + +[[bin]] +path = "src/create-wallet.rs" +name = "create-wallet" \ No newline at end of file diff --git a/bin/bundler-rpc.rs b/bin/bundler/src/bundler-rpc.rs similarity index 87% rename from bin/bundler-rpc.rs rename to bin/bundler/src/bundler-rpc.rs index c9ad00f4..dee3bd72 100644 --- a/bin/bundler-rpc.rs +++ b/bin/bundler/src/bundler-rpc.rs @@ -1,18 +1,10 @@ +use aa_bundler_grpc::{bundler_client::BundlerClient, uo_pool_client::UoPoolClient}; +use aa_bundler_rpc::{DebugApiServer, DebugApiServerImpl, EthApiServer, EthApiServerImpl}; use anyhow::Result; use clap::Parser; use jsonrpsee::{core::server::rpc_module::Methods, server::ServerBuilder, tracing::info}; use std::{collections::HashSet, future::pending}; -use aa_bundler::{ - rpc::{ - debug::DebugApiServerImpl, debug_api::DebugApiServer, eth::EthApiServerImpl, - eth_api::EthApiServer, - }, - uopool::server::{ - bundler::bundler_client::BundlerClient, uopool::uo_pool_client::UoPoolClient, - }, -}; - #[derive(Parser)] #[clap( name = "aa-bundler-rpc", diff --git a/bin/bundler-uopool.rs b/bin/bundler/src/bundler-uopool.rs similarity index 86% rename from bin/bundler-uopool.rs rename to bin/bundler/src/bundler-uopool.rs index c04cff0c..f1f5e3ca 100644 --- a/bin/bundler-uopool.rs +++ b/bin/bundler/src/bundler-uopool.rs @@ -1,4 +1,5 @@ -use aa_bundler::utils::{parse_address, parse_u256}; +use aa_bundler_grpc::{uopool_service_run, UoPoolServiceOpts}; +use aa_bundler_primitives::{parse_address, parse_u256}; use anyhow::Result; use clap::Parser; use ethers::{ @@ -15,7 +16,7 @@ use std::{future::pending, sync::Arc}; )] pub struct Opt { #[clap(flatten)] - pub uopool_opts: aa_bundler::uopool::UoPoolOpts, + pub uopool_opts: UoPoolServiceOpts, #[clap(long, value_delimiter=',', value_parser=parse_address)] pub entry_points: Vec
, @@ -41,7 +42,7 @@ async fn main() -> Result<()> { eth_provider.client_version().await? ); - aa_bundler::uopool::run( + uopool_service_run( opt.uopool_opts, opt.entry_points, eth_provider, diff --git a/bin/bundler.rs b/bin/bundler/src/bundler.rs similarity index 91% rename from bin/bundler.rs rename to bin/bundler/src/bundler.rs index 84ebdcb7..0c03fcb3 100644 --- a/bin/bundler.rs +++ b/bin/bundler/src/bundler.rs @@ -1,15 +1,9 @@ -use aa_bundler::{ - bundler::BundlerService, - models::wallet::Wallet, - rpc::{ - debug::DebugApiServerImpl, debug_api::DebugApiServer, eth::EthApiServerImpl, - eth_api::EthApiServer, - }, - uopool::server::{ - bundler::bundler_client::BundlerClient, uopool::uo_pool_client::UoPoolClient, - }, - utils::{parse_address, parse_u256}, +use aa_bundler_grpc::{ + bundler_client::BundlerClient, bundler_service_run, uo_pool_client::UoPoolClient, + uopool_service_run, BundlerService, BundlerServiceOpts, UoPoolServiceOpts, }; +use aa_bundler_primitives::{parse_address, parse_u256, Wallet}; +use aa_bundler_rpc::{DebugApiServer, DebugApiServerImpl, EthApiServer, EthApiServerImpl}; use anyhow::Result; use clap::Parser; use ethers::{ @@ -36,7 +30,7 @@ pub struct Opt { pub no_uopool: bool, #[clap(flatten)] - pub uopool_opts: aa_bundler::uopool::UoPoolOpts, + pub uopool_opts: UoPoolServiceOpts, #[clap(long, value_parser=parse_u256)] pub max_verification_gas: U256, @@ -55,7 +49,7 @@ pub struct Opt { pub eth_client_address: String, #[clap(flatten)] - pub bundler_opts: aa_bundler::bundler::BundlerOpts, + pub bundler_opts: BundlerServiceOpts, } fn main() -> Result<()> { @@ -92,7 +86,7 @@ fn main() -> Result<()> { if !opt.no_uopool { info!("Starting op pool with bundler"); - aa_bundler::uopool::run( + uopool_service_run( opt.uopool_opts, opt.entry_points.clone(), eth_provider, @@ -120,7 +114,7 @@ fn main() -> Result<()> { info!("Starting bundler manager"); bundler_service.start_bundling(opt.bundler_opts.bundle_interval); info!("Starting bundler rpc server"); - aa_bundler::bundler::service::run_server( + bundler_service_run( bundler_service, opt.bundler_opts.bundler_grpc_listen_address, ); diff --git a/bin/create-wallet.rs b/bin/bundler/src/create-wallet.rs similarity index 93% rename from bin/create-wallet.rs rename to bin/bundler/src/create-wallet.rs index f9d04e2b..f567dfa7 100644 --- a/bin/create-wallet.rs +++ b/bin/bundler/src/create-wallet.rs @@ -1,4 +1,4 @@ -use aa_bundler::{models::wallet::Wallet, utils::parse_u256}; +use aa_bundler_primitives::{parse_u256, Wallet}; use anyhow::Result; use clap::Parser; use dirs::home_dir; diff --git a/build.rs b/build.rs deleted file mode 100644 index fe7a0a0e..00000000 --- a/build.rs +++ /dev/null @@ -1,55 +0,0 @@ -#![allow(clippy::unwrap_used)] -use std::{env, path::PathBuf}; - -use ethers::solc::{Project, ProjectPathsConfig}; - -fn config() -> prost_build::Config { - let mut config = prost_build::Config::new(); - config.bytes(["."]); - config -} - -fn make_protos(protos: &[&str]) { - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - tonic_build::configure() - .server_mod_attribute("uopool", r#"#[allow(clippy::unwrap_used)]"#) - .server_mod_attribute("bundler", r#"#[allow(clippy::unwrap_used)]"#) - .file_descriptor_set_path(out_dir.join("descriptor.bin")) - .compile_with_config(config(), protos, &["./src/proto"]) - .unwrap(); -} - -fn compile_aa_interfaces() -> anyhow::Result<()> { - let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("thirdparty/account-abstraction"); - let target_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - let build_path_config = ProjectPathsConfig::builder() - // only interfaces are needed - .sources(root.join("contracts").join("interfaces")) - .artifacts(target_path) - .build_infos(root.join("contracts").join("build-info")) - .root(root) - .build()?; - let project = Project::builder().paths(build_path_config).build()?; - project.rerun_if_sources_changed(); - let compiled = project.compile()?; - assert!( - !compiled.has_compiler_errors(), - "Compiling EIP-4337 interfaces failed: {:?}", - compiled.output().errors - ); - Ok(()) -} - -fn main() { - std::env::set_var("PROTOC", protobuf_src::protoc()); - - let protos = vec![ - "types/types.proto", - "uopool/uopool.proto", - "bundler/bundler.proto", - ]; - - make_protos(&protos); - - compile_aa_interfaces().expect("Compiling EIP-4337 interfaces should pass."); -} diff --git a/crates/bundler/Cargo.toml b/crates/bundler/Cargo.toml new file mode 100644 index 00000000..31acb38c --- /dev/null +++ b/crates/bundler/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "aa-bundler-bundler" +version = "0.1.0" +authors = ["Vid Kersic "] +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/Vid201/aa-bundler" +readme = "README.md" +description = """ +AA (ERC-4337) Bundler bundling implementation +""" +rust-version = "1.67.1" + +[dependencies] +aa-bundler-contracts = { path = "../contracts" } +aa-bundler-primitives = { path = "../primitives" } + +anyhow = "1" +ethers = { version = "2.0.1", features = ["solc-full"] } +tracing = "0.1" \ No newline at end of file diff --git a/crates/bundler/src/bundler.rs b/crates/bundler/src/bundler.rs new file mode 100644 index 00000000..5dd2f78a --- /dev/null +++ b/crates/bundler/src/bundler.rs @@ -0,0 +1,76 @@ +use std::{sync::Arc, time::Duration}; + +use aa_bundler_contracts::EntryPointAPI; +use aa_bundler_primitives::{UserOperation, Wallet}; +use ethers::{ + prelude::SignerMiddleware, + providers::{Http, Middleware, Provider}, + signers::Signer, + types::{transaction::eip2718::TypedTransaction, Address, H256, U256}, +}; +use tracing::{info, trace}; + +#[derive(Clone)] +pub struct Bundler { + pub wallet: Wallet, + pub beneficiary: Address, + pub entry_point: Address, + pub chain_id: U256, + pub eth_client_address: String, +} + +impl Bundler { + pub fn new( + wallet: Wallet, + beneficiary: Address, + entry_point: Address, + chain_id: U256, + eth_client_address: String, + ) -> Self { + Self { + wallet, + beneficiary, + entry_point, + chain_id, + eth_client_address, + } + } + + pub async fn send_next_bundle(&self, bundle: &Vec) -> anyhow::Result { + info!( + "Creating the next bundle, got {} user operations", + bundle.len() + ); + let provider = Provider::::try_from(self.eth_client_address.clone())?; + let client = Arc::new(SignerMiddleware::new( + provider.clone(), + self.wallet.signer.clone(), + )); + let entry_point = EntryPointAPI::new(self.entry_point, client.clone()); + let nonce = client + .clone() + .get_transaction_count(self.wallet.signer.address(), None) + .await?; + let mut tx: TypedTransaction = entry_point + .handle_ops( + bundle.clone().into_iter().map(Into::into).collect(), + self.beneficiary, + ) + .tx + .clone(); + tx.set_nonce(nonce).set_chain_id(self.chain_id.as_u64()); + + trace!("Prepare the transaction {tx:?} send to execution client!"); + let tx = client + .send_transaction(tx, None) + .await? + .interval(Duration::from_millis(75)); + let tx_hash = tx.tx_hash(); + trace!("Send bundle with transaction: {tx:?}"); + + let tx_receipt = tx.await?; + trace!("Bundle transaction receipt: {tx_receipt:?}"); + + Ok(tx_hash) + } +} diff --git a/crates/bundler/src/lib.rs b/crates/bundler/src/lib.rs new file mode 100644 index 00000000..515de5cd --- /dev/null +++ b/crates/bundler/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(dead_code)] + +mod bundler; + +pub use bundler::Bundler; diff --git a/crates/contracts/Cargo.toml b/crates/contracts/Cargo.toml new file mode 100644 index 00000000..8a1b310a --- /dev/null +++ b/crates/contracts/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "aa-bundler-contracts" +version = "0.1.0" +authors = ["Vid Kersic "] +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/Vid201/aa-bundler" +readme = "README.md" +description = """ +AA (ERC-4337) Bundler smart contract interfaces +""" +rust-version = "1.67.1" + +[dependencies] +aa-bundler-primitives = { path = "../primitives" } + +anyhow = "1" +ethers = { version = "2.0.1", features = ["solc-full"] } +ethers-providers = "2.0.1" +lazy_static = "1.4.0" +serde = "1" +serde_json = "1" +thiserror = "1" +tracing = "0.1" + +[dev-dependencies] +tokio = { version = "1.18", features = ["full"] } + +[build-dependencies] +anyhow = "1" +ethers = { version = "2.0.1", features = ["solc-full"] } diff --git a/crates/contracts/build.rs b/crates/contracts/build.rs new file mode 100644 index 00000000..c1029ecf --- /dev/null +++ b/crates/contracts/build.rs @@ -0,0 +1,27 @@ +use ethers::solc::{Project, ProjectPathsConfig}; +use std::{env, path::PathBuf}; + +fn compile_aa_interfaces() -> anyhow::Result<()> { + let root = PathBuf::from(env!("CARGO_WORKSPACE_DIR")).join("thirdparty/account-abstraction"); + let target_path = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set")); + let build_path_config = ProjectPathsConfig::builder() + // only interfaces are needed + .sources(root.join("contracts").join("interfaces")) + .artifacts(target_path) + .build_infos(root.join("contracts").join("build-info")) + .root(root) + .build()?; + let project = Project::builder().paths(build_path_config).build()?; + project.rerun_if_sources_changed(); + let compiled = project.compile()?; + assert!( + !compiled.has_compiler_errors(), + "Compiling EIP-4337 interfaces failed: {:?}", + compiled.output().errors + ); + Ok(()) +} + +fn main() { + compile_aa_interfaces().expect("Compiling EIP-4337 interfaces should pass."); +} diff --git a/src/contracts/entrypoint.rs b/crates/contracts/src/entry_point.rs similarity index 99% rename from src/contracts/entrypoint.rs rename to crates/contracts/src/entry_point.rs index 7fce36e1..5c385ba7 100644 --- a/src/contracts/entrypoint.rs +++ b/crates/contracts/src/entry_point.rs @@ -293,7 +293,7 @@ mod tests { types::{Address, Bytes, GethTrace, U256}, }; - use crate::types::user_operation::UserOperation; + use aa_bundler_primitives::UserOperation; use super::*; use std::{str::FromStr, sync::Arc}; diff --git a/src/contracts/gen.rs b/crates/contracts/src/gen.rs similarity index 100% rename from src/contracts/gen.rs rename to crates/contracts/src/gen.rs diff --git a/crates/contracts/src/lib.rs b/crates/contracts/src/lib.rs new file mode 100644 index 00000000..7c003dc3 --- /dev/null +++ b/crates/contracts/src/lib.rs @@ -0,0 +1,14 @@ +#![allow(dead_code)] + +mod entry_point; +mod gen; +mod tracer; +mod utils; + +pub use entry_point::{EntryPoint, EntryPointErr, SimulateValidationResult}; +pub use gen::{ + EntryPointAPI, EntryPointAPIEvents, UserOperationEventFilter, ValidatePaymasterUserOpReturn, + CONTRACTS_FUNCTIONS, +}; +pub use tracer::{Call, CallEntry, JsTracerFrame, JS_TRACER}; +pub use utils::parse_from_input_data; diff --git a/src/contracts/tracer.rs b/crates/contracts/src/tracer.rs similarity index 100% rename from src/contracts/tracer.rs rename to crates/contracts/src/tracer.rs diff --git a/crates/contracts/src/utils.rs b/crates/contracts/src/utils.rs new file mode 100644 index 00000000..bfee012f --- /dev/null +++ b/crates/contracts/src/utils.rs @@ -0,0 +1,67 @@ +use aa_bundler_primitives::UserOperation; +use ethers::{abi::AbiDecode, types::Bytes}; + +use crate::gen::entry_point_api::{self, EntryPointAPICalls}; + +impl From for entry_point_api::UserOperation { + fn from(user_operation: UserOperation) -> Self { + Self { + sender: user_operation.sender, + nonce: user_operation.nonce, + init_code: user_operation.init_code, + call_data: user_operation.call_data, + call_gas_limit: user_operation.call_gas_limit, + verification_gas_limit: user_operation.verification_gas_limit, + pre_verification_gas: user_operation.pre_verification_gas, + max_fee_per_gas: user_operation.max_fee_per_gas, + max_priority_fee_per_gas: user_operation.max_priority_fee_per_gas, + paymaster_and_data: user_operation.paymaster_and_data, + signature: user_operation.signature, + } + } +} + +impl From for UserOperation { + fn from(value: entry_point_api::UserOperation) -> Self { + Self { + sender: value.sender, + nonce: value.nonce, + init_code: value.init_code, + call_data: value.call_data, + call_gas_limit: value.call_gas_limit, + verification_gas_limit: value.verification_gas_limit, + pre_verification_gas: value.pre_verification_gas, + max_fee_per_gas: value.max_fee_per_gas, + max_priority_fee_per_gas: value.max_priority_fee_per_gas, + paymaster_and_data: value.paymaster_and_data, + signature: value.signature, + } + } +} + +pub fn parse_from_input_data(data: Bytes) -> Option> { + EntryPointAPICalls::decode(data) + .ok() + .and_then(|call| match call { + EntryPointAPICalls::HandleOps(ops) => { + Some(ops.ops.into_iter().map(|op| op.into()).collect()) + } + _ => None, + }) +} + +#[cfg(test)] +mod tests { + use ethers::types::Bytes; + use std::str::FromStr; + + use super::*; + + #[test] + fn parse_input_data() { + let data = Bytes::from_str("0x1fad948c0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000690b9a9e9aa1c9db991c7721a92d351db4fac990000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000001ec271771e84999634e5e0330970feeb1c75f35200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000077359400000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024a9e966b7000000000000000000000000000000000000000000000000000000000010f4470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002face000000000000000000000000000000000000000000000000000000000000") + .unwrap(); + let res = parse_from_input_data(data); + assert!(matches!(res, Some(..)), "No user operation found") + } +} diff --git a/crates/grpc/Cargo.toml b/crates/grpc/Cargo.toml new file mode 100644 index 00000000..41b4f84a --- /dev/null +++ b/crates/grpc/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "aa-bundler-grpc" +version = "0.1.0" +authors = ["Vid Kersic "] +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/Vid201/aa-bundler" +readme = "README.md" +description = """ +AA (ERC-4337) Bundler gRPC interfaces +""" +rust-version = "1.67.1" + +[dependencies] +aa-bundler-bundler = { path = "../bundler" } +aa-bundler-contracts = { path = "../contracts" } +aa-bundler-primitives = { path = "../primitives" } +aa-bundler-uopool = { path = "../uopool" } + +anyhow = "1" +arrayref = "0.3" +async-trait = "0.1" +clap = { version = "4", features = ["derive"] } +dashmap = "5.4.0" +ethers = { version = "2.0.1", features = ["solc-full"] } +parking_lot = "0.12" +prost = "0.11" +serde_json = "1" +tokio = { version = "1.18", features = ["full"] } +tonic = { version = "0.8", default-features = false, features = [ + "codegen", + "prost", + "transport", +] } +tracing = "0.1" + +[dev-dependencies] + +[build-dependencies] +prost-build = "0.11" +protobuf-src = "1.1.0" +tonic-build = "0.8" \ No newline at end of file diff --git a/crates/grpc/build.rs b/crates/grpc/build.rs new file mode 100644 index 00000000..9691e6de --- /dev/null +++ b/crates/grpc/build.rs @@ -0,0 +1,29 @@ +use std::{env, path::PathBuf}; + +fn config() -> prost_build::Config { + let mut config = prost_build::Config::new(); + config.bytes(["."]); + config +} + +fn make_protos(protos: &[&str]) { + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIT not set")); + tonic_build::configure() + .server_mod_attribute("uopool", r#"#[allow(clippy::unwrap_used)]"#) + .server_mod_attribute("bundler", r#"#[allow(clippy::unwrap_used)]"#) + .file_descriptor_set_path(out_dir.join("descriptor.bin")) + .compile_with_config(config(), protos, &["./src/protos"]) + .expect("Failed to compile protos"); +} + +fn main() { + std::env::set_var("PROTOC", protobuf_src::protoc()); + + let protos = vec![ + "src/protos/types/types.proto", + "src/protos/uopool/uopool.proto", + "src/protos/bundler/bundler.proto", + ]; + + make_protos(&protos); +} diff --git a/src/bundler/mod.rs b/crates/grpc/src/bundler.rs similarity index 55% rename from src/bundler/mod.rs rename to crates/grpc/src/bundler.rs index dc8af4c7..14458fe7 100644 --- a/src/bundler/mod.rs +++ b/crates/grpc/src/bundler.rs @@ -1,51 +1,22 @@ -pub mod service; - use std::{net::SocketAddr, sync::Arc, time::Duration}; +use aa_bundler_bundler::Bundler as BundlerCore; +use aa_bundler_primitives::{parse_address, parse_u256, UserOperation, Wallet}; +use async_trait::async_trait; use clap::Parser; -use ethers::{ - prelude::SignerMiddleware, - providers::{Http, Middleware, Provider}, - signers::Signer, - types::{transaction::eip2718::TypedTransaction, Address, H256, U256}, -}; +use ethers::types::{Address, H256, U256}; use parking_lot::Mutex; -use serde::Deserialize; -use tonic::Request; -use tracing::{error, info, trace, warn}; - -use crate::{ - contracts::gen::EntryPointAPI, - models::wallet::Wallet, - types::user_operation::UserOperation, - uopool::server::{ - bundler::Mode as GrpcMode, - uopool::{uo_pool_client::UoPoolClient, GetSortedRequest, HandlePastEventRequest}, - }, - utils::{parse_address, parse_u256}, -}; - -pub const DEFAULT_INTERVAL: u64 = 10; - -#[derive(Debug, Deserialize)] -pub enum Mode { - #[serde(rename = "auto")] - Auto, - #[serde(rename = "manual")] - Manual, -} +use tonic::Response; +use tracing::{error, info, warn}; -impl From for GrpcMode { - fn from(value: Mode) -> Self { - match value { - Mode::Auto => Self::Auto, - Mode::Manual => Self::Manual, - } - } -} +use crate::proto::uopool::{GetSortedRequest, HandlePastEventRequest}; +use crate::{GetChainIdResponse, GetSupportedEntryPointsResponse}; + +use crate::proto::bundler::*; +use crate::uo_pool_client::UoPoolClient; #[derive(Debug, Parser, PartialEq)] -pub struct BundlerOpts { +pub struct BundlerServiceOpts { #[clap(long, value_parser=parse_address)] pub beneficiary: Address, @@ -62,107 +33,10 @@ pub struct BundlerOpts { pub bundle_interval: u64, } -#[derive(Clone)] -pub struct Bundler { - pub wallet: Wallet, - pub beneficiary: Address, - pub uopool_grpc_client: UoPoolClient, - pub entry_point: Address, - pub chain_id: U256, - pub eth_client_address: String, -} - -impl Bundler { - pub fn new( - wallet: Wallet, - beneficiary: Address, - uopool_grpc_client: UoPoolClient, - entry_point: Address, - chain_id: U256, - eth_client_address: String, - ) -> Self { - Self { - wallet, - beneficiary, - uopool_grpc_client, - entry_point, - chain_id, - eth_client_address, - } - } - - async fn create_bundle(&self) -> anyhow::Result> { - let request = tonic::Request::new(GetSortedRequest { - entry_point: Some(self.entry_point.into()), - }); - let response = self - .uopool_grpc_client - .clone() - .get_sorted_user_operations(request) - .await?; - let user_operations: Vec = response - .into_inner() - .user_operations - .into_iter() - .map(|u| u.into()) - .collect(); - Ok(user_operations) - } - - async fn send_next_bundle(&self, bundle: &Vec) -> anyhow::Result { - info!( - "Creating the next bundle, got {} user operations", - bundle.len() - ); - let provider = Provider::::try_from(self.eth_client_address.clone())?; - let client = Arc::new(SignerMiddleware::new( - provider.clone(), - self.wallet.signer.clone(), - )); - let entry_point = EntryPointAPI::new(self.entry_point, client.clone()); - let nonce = client - .clone() - .get_transaction_count(self.wallet.signer.address(), None) - .await?; - let mut tx: TypedTransaction = entry_point - .handle_ops( - bundle.clone().into_iter().map(Into::into).collect(), - self.beneficiary, - ) - .tx - .clone(); - tx.set_nonce(nonce).set_chain_id(self.chain_id.as_u64()); - - trace!("Prepare the transaction {tx:?} send to execution client!"); - let tx = client - .send_transaction(tx, None) - .await? - .interval(Duration::from_millis(100)); - let tx_hash = tx.tx_hash(); - trace!("Send bundle with transaction: {tx:?}"); - - let tx_receipt = tx.await?; - trace!("Bundle transaction receipt: {tx_receipt:?}"); - - info!("Send handlePastEvents request"); - if let Some(e) = self - .uopool_grpc_client - .clone() - .handle_past_events(Request::new(HandlePastEventRequest { - entry_point: Some(self.entry_point.into()), - })) - .await - .err() - { - warn!("Failed to handle past events: {:?}", e) - }; - Ok(tx_hash) - } -} - pub struct BundlerService { - pub bundlers: Vec, + pub bundlers: Vec, pub running: Arc>, + pub uopool_grpc_client: UoPoolClient, } fn is_running(running: Arc>) -> bool { @@ -179,13 +53,12 @@ impl BundlerService { chain_id: U256, eth_client_address: String, ) -> Self { - let bundlers: Vec = entry_points + let bundlers: Vec = entry_points .iter() .map(|entry_point| { - Bundler::new( + BundlerCore::new( wallet.clone(), beneficiary, - uopool_grpc_client.clone(), *entry_point, chain_id, eth_client_address.clone(), @@ -196,18 +69,42 @@ impl BundlerService { Self { bundlers, running: Arc::new(Mutex::new(false)), + uopool_grpc_client, } } + async fn create_bundle( + uopool_grpc_client: &UoPoolClient, + entry_point: &Address, + ) -> anyhow::Result> { + let request = tonic::Request::new(GetSortedRequest { + entry_point: Some((*entry_point).into()), + }); + let response = uopool_grpc_client + .clone() + .get_sorted_user_operations(request) + .await?; + let user_operations: Vec = response + .into_inner() + .user_operations + .into_iter() + .map(|u| u.into()) + .collect(); + Ok(user_operations) + } + pub async fn send_bundles_now(&self) -> anyhow::Result { info!("Sending bundles now"); let mut tx_hashes: Vec = vec![]; for bundler in self.bundlers.iter() { info!("Sending bundle for entry point: {:?}", bundler.entry_point); - let bundle = bundler.create_bundle().await?; + let bundle = + Self::create_bundle(&self.uopool_grpc_client, &bundler.entry_point).await?; let tx_hash = bundler.send_next_bundle(&bundle).await?; + Self::handle_past_events(&self.uopool_grpc_client, &bundler.entry_point).await?; + tx_hashes.push(tx_hash) } @@ -229,6 +126,28 @@ impl BundlerService { is_running(self.running.clone()) } + async fn handle_past_events( + uopool_grpc_client: &UoPoolClient, + entry_point: &Address, + ) -> anyhow::Result<()> { + info!("Send handlePastEvents request"); + + let request = tonic::Request::new(HandlePastEventRequest { + entry_point: Some((*entry_point).into()), + }); + + if let Some(e) = uopool_grpc_client + .clone() + .handle_past_events(request) + .await + .err() + { + warn!("Failed to handle past events: {:?}", e) + }; + + Ok(()) + } + pub fn start_bundling(&self, interval: u64) { if !self.is_running() { for bundler in self.bundlers.iter() { @@ -238,6 +157,7 @@ impl BundlerService { ); let bundler_own = bundler.clone(); let running_lock = self.running.clone(); + let uopool_grpc_client = self.uopool_grpc_client.clone(); tokio::spawn(async move { let mut interval = tokio::time::interval(Duration::from_secs(interval)); loop { @@ -246,11 +166,21 @@ impl BundlerService { } interval.tick().await; - match bundler_own.create_bundle().await { + match Self::create_bundle(&uopool_grpc_client, &bundler_own.entry_point) + .await + { Ok(bundle) => { if let Err(e) = bundler_own.send_next_bundle(&bundle).await { error!("Error while sending bundle: {e:?}"); } + if let Err(e) = Self::handle_past_events( + &uopool_grpc_client, + &bundler_own.entry_point, + ) + .await + { + error!("Error while handling past events: {e:?}"); + } } Err(e) => { error!("Error while creating bundle: {e:?}"); @@ -262,6 +192,68 @@ impl BundlerService { } } } + +#[async_trait] +impl bundler_server::Bundler for BundlerService { + async fn chain_id( + &self, + _request: tonic::Request<()>, + ) -> Result, tonic::Status> { + todo!() + } + + async fn supported_entry_points( + &self, + _request: tonic::Request<()>, + ) -> Result, tonic::Status> { + todo!() + } + + async fn set_bundler_mode( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let req = request.into_inner(); + match req.mode() { + Mode::Manual => { + info!("Stopping auto bundling"); + self.stop_bundling(); + Ok(Response::new(SetModeResponse { + result: SetModeResult::Ok.into(), + })) + } + Mode::Auto => { + let interval = req.interval; + self.start_bundling(interval); + Ok(Response::new(SetModeResponse { + result: SetModeResult::Ok.into(), + })) + } + } + } + + async fn send_bundle_now( + &self, + _request: tonic::Request<()>, + ) -> Result, tonic::Status> { + let res = self.send_bundles_now().await.map_err(|e| { + error!("Send bundle manually with response {e:?}"); + tonic::Status::internal(format!("Send bundle now with error: {e:?}")) + })?; + Ok(Response::new(SendBundleNowResponse { + result: Some(res.into()), + })) + } +} + +pub fn bundler_service_run(bundler_service: BundlerService, listen_address: SocketAddr) { + tokio::spawn(async move { + let mut builder = tonic::transport::Server::builder(); + let svc = bundler_server::BundlerServer::new(bundler_service); + builder.add_service(svc).serve(listen_address).await + }); +} + #[cfg(test)] mod tests { use super::*; @@ -286,7 +278,7 @@ mod tests { "10", ]; assert_eq!( - BundlerOpts { + BundlerServiceOpts { beneficiary: Address::from_str("0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990") .unwrap(), gas_factor: U256::from(600), @@ -297,7 +289,7 @@ mod tests { ), bundle_interval: 10, }, - BundlerOpts::try_parse_from(args).unwrap() + BundlerServiceOpts::try_parse_from(args).unwrap() ); } } diff --git a/crates/grpc/src/lib.rs b/crates/grpc/src/lib.rs new file mode 100644 index 00000000..1f56a997 --- /dev/null +++ b/crates/grpc/src/lib.rs @@ -0,0 +1,12 @@ +#![allow(dead_code)] + +mod bundler; +mod proto; +mod uopool; + +pub use proto::bundler::*; +pub use proto::types::*; +pub use proto::uopool::*; + +pub use bundler::{bundler_service_run, BundlerService, BundlerServiceOpts}; +pub use uopool::{uopool_service_run, UoPoolServiceOpts}; diff --git a/src/uopool/server.rs b/crates/grpc/src/proto.rs similarity index 86% rename from src/uopool/server.rs rename to crates/grpc/src/proto.rs index 7a9fa03f..99959d6b 100644 --- a/src/uopool/server.rs +++ b/crates/grpc/src/proto.rs @@ -3,7 +3,7 @@ pub mod types { use std::str::FromStr; - use crate::types::user_operation::UserOperationHash; + use aa_bundler_primitives::UserOperationHash; use arrayref::array_ref; use ethers::types::{Address, Bloom, Bytes, U256}; use prost::bytes::Buf; @@ -96,8 +96,8 @@ pub mod types { } } - impl From for UserOperation { - fn from(user_operation: crate::types::user_operation::UserOperation) -> Self { + impl From for UserOperation { + fn from(user_operation: aa_bundler_primitives::UserOperation) -> Self { Self { sender: Some(user_operation.sender.into()), nonce: user_operation.nonce.as_u64(), @@ -116,7 +116,7 @@ pub mod types { } } - impl From for crate::types::user_operation::UserOperation { + impl From for aa_bundler_primitives::UserOperation { fn from(user_operation: UserOperation) -> Self { Self { sender: { @@ -140,24 +140,24 @@ pub mod types { } } - impl From for ReputationEntry { - fn from(reputation_entry: crate::types::reputation::ReputationEntry) -> Self { + impl From for ReputationEntry { + fn from(reputation_entry: aa_bundler_primitives::ReputationEntry) -> Self { Self { address: Some(reputation_entry.address.into()), uo_seen: reputation_entry.uo_seen, uo_included: reputation_entry.uo_included, status: match reputation_entry.status { - crate::types::reputation::ReputationStatus::OK => ReputationStatus::Ok, - crate::types::reputation::ReputationStatus::THROTTLED => { + aa_bundler_primitives::ReputationStatus::OK => ReputationStatus::Ok, + aa_bundler_primitives::ReputationStatus::THROTTLED => { ReputationStatus::Throttled } - crate::types::reputation::ReputationStatus::BANNED => ReputationStatus::Banned, + aa_bundler_primitives::ReputationStatus::BANNED => ReputationStatus::Banned, } as i32, } } } - impl From for crate::types::reputation::ReputationEntry { + impl From for aa_bundler_primitives::ReputationEntry { fn from(reputation_entry: ReputationEntry) -> Self { Self { address: { @@ -171,15 +171,15 @@ pub mod types { uo_included: reputation_entry.uo_included, status: match reputation_entry.status { _ if reputation_entry.status == ReputationStatus::Ok as i32 => { - crate::types::reputation::ReputationStatus::OK + aa_bundler_primitives::ReputationStatus::OK } _ if reputation_entry.status == ReputationStatus::Throttled as i32 => { - crate::types::reputation::ReputationStatus::THROTTLED + aa_bundler_primitives::ReputationStatus::THROTTLED } _ if reputation_entry.status == ReputationStatus::Banned as i32 => { - crate::types::reputation::ReputationStatus::BANNED + aa_bundler_primitives::ReputationStatus::BANNED } - _ => crate::types::reputation::ReputationStatus::OK, + _ => aa_bundler_primitives::ReputationStatus::OK, }, } } @@ -275,6 +275,12 @@ pub mod types { } } } + + impl From for H256 { + fn from(value: UserOperationHash) -> Self { + Self::from(value.0) + } + } } pub mod uopool { @@ -282,5 +288,25 @@ pub mod uopool { } pub mod bundler { + use aa_bundler_primitives::Mode as GrpcMode; + tonic::include_proto!("bundler"); + + impl From for GrpcMode { + fn from(value: Mode) -> Self { + match value { + Mode::Auto => Self::Auto, + Mode::Manual => Self::Manual, + } + } + } + + impl From for Mode { + fn from(value: GrpcMode) -> Self { + match value { + GrpcMode::Auto => Self::Auto, + GrpcMode::Manual => Self::Manual, + } + } + } } diff --git a/src/proto/bundler/bundler.proto b/crates/grpc/src/protos/bundler/bundler.proto similarity index 100% rename from src/proto/bundler/bundler.proto rename to crates/grpc/src/protos/bundler/bundler.proto diff --git a/src/proto/types/types.proto b/crates/grpc/src/protos/types/types.proto similarity index 100% rename from src/proto/types/types.proto rename to crates/grpc/src/protos/types/types.proto diff --git a/src/proto/uopool/uopool.proto b/crates/grpc/src/protos/uopool/uopool.proto similarity index 100% rename from src/proto/uopool/uopool.proto rename to crates/grpc/src/protos/uopool/uopool.proto diff --git a/src/uopool/service.rs b/crates/grpc/src/uopool.rs similarity index 87% rename from src/uopool/service.rs rename to crates/grpc/src/uopool.rs index 3b24caa3..35cb8e41 100644 --- a/src/uopool/service.rs +++ b/crates/grpc/src/uopool.rs @@ -1,54 +1,54 @@ -use crate::{ - chain::gas::Overhead, - contracts::{ - gen::EntryPointAPIEvents, gen::UserOperationEventFilter, EntryPointErr, - SimulateValidationResult, - }, - types::{ - reputation::{ReputationStatus, THROTTLED_MAX_INCLUDE}, - simulation::{SimulateValidationError, SimulationError}, - user_operation::{ - parse_from_input_data, UserOperation, UserOperationGasEstimation, UserOperationHash, - }, - }, - uopool::{ - mempool_id, - server::{ - types::{GetChainIdResponse, GetSupportedEntryPointsResponse}, - uopool::{ - uo_pool_server::UoPool, AddRequest, AddResponse, AddResult, ClearResponse, - ClearResult, EstimateUserOperationGasRequest, EstimateUserOperationGasResponse, - EstimateUserOperationGasResult, GetAllReputationRequest, GetAllReputationResponse, - GetAllReputationResult, GetAllRequest, GetAllResponse, GetAllResult, - GetSortedRequest, GetSortedResponse, GetUserOperationByHashResponse, - HandlePastEventRequest, RemoveRequest, RemoveResponse, RemoveResult, - SetReputationRequest, SetReputationResponse, SetReputationResult, - }, - }, - utils::get_addr, - MempoolId, - }, +use std::{ + collections::{HashMap, HashSet}, + net::SocketAddr, + sync::Arc, + time::Duration, +}; + +use aa_bundler_contracts::{ + parse_from_input_data, EntryPoint, EntryPointAPIEvents, EntryPointErr, + SimulateValidationResult, UserOperationEventFilter, }; +use aa_bundler_primitives::{ + get_addr, parse_u256, ReputationStatus, SimulationError, UserOperation, + UserOperationGasEstimation, BAN_SLACK, MIN_INCLUSION_RATE_DENOMINATOR, THROTTLED_MAX_INCLUDE, + THROTTLING_SLACK, +}; +use aa_bundler_uopool::{ + canonical::simulation::SimulateValidationError, mempool_id, MemoryMempool, MemoryReputation, + MempoolId, Overhead, Reputation, UoPool as UserOperationPool, +}; +use anyhow::Result; use async_trait::async_trait; +use clap::Parser; use dashmap::DashMap; use ethers::{ prelude::LogMeta, - providers::Middleware, + providers::{Http, Middleware, Provider}, types::{Address, H256, U256, U64}, }; -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, -}; use tonic::Response; -use tracing::{debug, trace, warn}; +use tracing::{debug, info, trace, warn}; const LATEST_SCAN_DEPTH: u64 = 1000; -use super::{ - server::uopool::{GetUserOperationReceiptResponse, UserOperationHashRequest}, - UoPool as UserOperationPool, -}; +use crate::proto::types::{GetChainIdResponse, GetSupportedEntryPointsResponse}; +use crate::proto::uopool::*; + +#[derive(Clone, Copy, Debug, Parser, PartialEq)] +pub struct UoPoolServiceOpts { + #[clap(long, default_value = "127.0.0.1:3001")] + pub uopool_grpc_listen_address: SocketAddr, + + #[clap(long, value_parser=parse_u256, default_value = "1")] + pub min_stake: U256, + + #[clap(long, value_parser=parse_u256, default_value = "0")] + pub min_unstake_delay: U256, + + #[clap(long, value_parser=parse_u256, default_value = "0")] + pub min_priority_fee_per_gas: U256, +} pub struct UoPoolService { pub mempools: Arc>>, @@ -69,20 +69,6 @@ impl UoPoolService { } } - pub fn remove_user_operation( - &self, - uopool: &mut UserOperationPool, - user_operation_hash: &UserOperationHash, - ) -> Option<()> { - uopool.mempool.remove(user_operation_hash).ok(); - None - } - - pub fn include_address(&self, uopool: &mut UserOperationPool, addr: Address) -> Option<()> { - uopool.reputation.increment_included(&addr); - Some(()) - } - pub async fn find_user_operation_event( &self, user_operation_hash: H256, @@ -102,7 +88,7 @@ impl UoPoolService { } #[async_trait] -impl UoPool for UoPoolService +impl uo_pool_server::UoPool for UoPoolService where EntryPointErr: From<::Error>, { @@ -145,7 +131,8 @@ where if let Some(user_operation_hash) = verification_result.sanity_check_result.user_operation_hash { - self.remove_user_operation(&mut uopool, &user_operation_hash) + uopool + .remove_user_operation(&user_operation_hash) .unwrap_or_else(|| { trace!( "Unable to remove user operation {:?} from mempool {:?}", @@ -575,24 +562,22 @@ where for event in events { match event { EntryPointAPIEvents::UserOperationEventFilter(user_operation_event) => { - self.remove_user_operation( - &mut uopool, - &user_operation_event.user_op_hash.into(), - ) - .unwrap_or_else(|| { - // This could be possible when other bundler submit the user operations - trace!( - "Unable to remove user operation {:?} from mempool {:?}", - user_operation_event.user_op_hash, - mempool_id - ) - }); - self.include_address(&mut uopool, user_operation_event.sender); - self.include_address(&mut uopool, user_operation_event.paymaster); + uopool + .remove_user_operation(&user_operation_event.user_op_hash.into()) + .unwrap_or_else(|| { + // This could be possible when other bundler submit the user operations + trace!( + "Unable to remove user operation {:?} from mempool {:?}", + user_operation_event.user_op_hash, + mempool_id + ) + }); + uopool.include_address(user_operation_event.sender); + uopool.include_address(user_operation_event.paymaster); // TODO: include event aggregator } EntryPointAPIEvents::AccountDeployedFilter(account_deploy_event) => { - self.include_address(&mut uopool, account_deploy_event.factory); + uopool.include_address(account_deploy_event.factory); } EntryPointAPIEvents::SignatureAggregatorChangedFilter(_) => { warn!("Aggregate signature is not supported currently"); @@ -838,3 +823,73 @@ where Err(tonic::Status::invalid_argument("missing entry point")) } } + +pub async fn uopool_service_run( + opts: UoPoolServiceOpts, + entry_points: Vec
, + eth_provider: Arc>, + max_verification_gas: U256, +) -> Result<()> { + let chain_id = eth_provider.get_chainid().await?; + + tokio::spawn(async move { + let mut builder = tonic::transport::Server::builder(); + + let mempools_map = Arc::new(DashMap::>>::new()); + + for entry_point in entry_points { + let id = mempool_id(&entry_point, &chain_id); + + let mut reputation = Box::::default(); + reputation.init( + MIN_INCLUSION_RATE_DENOMINATOR, + THROTTLING_SLACK, + BAN_SLACK, + opts.min_stake, + opts.min_unstake_delay, + ); + + mempools_map.insert( + id, + UserOperationPool::>::new( + EntryPoint::>::new(eth_provider.clone(), entry_point), + Box::::default(), + reputation, + eth_provider.clone(), + max_verification_gas, + opts.min_priority_fee_per_gas, + chain_id, + ), + ); + } + + let svc = uo_pool_server::UoPoolServer::new(UoPoolService::new( + mempools_map.clone(), + eth_provider.clone(), + chain_id, + )); + + tokio::spawn(async move { + loop { + mempools_map + .iter_mut() + .for_each(|mut mempool| mempool.value_mut().reputation.update_hourly()); + tokio::time::sleep(Duration::from_secs(60 * 60)).await; + } + }); + + info!( + "UoPool gRPC server starting on {}", + opts.uopool_grpc_listen_address + ); + + builder + .add_service(svc) + .serve(opts.uopool_grpc_listen_address) + .await + }); + + tokio::time::sleep(Duration::from_secs(1)).await; + + Ok(()) +} diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml new file mode 100644 index 00000000..66b8e9c8 --- /dev/null +++ b/crates/primitives/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "aa-bundler-primitives" +version = "0.1.0" +authors = ["Vid Kersic "] +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/Vid201/aa-bundler" +readme = "README.md" +description = """ +AA (ERC-4337) Bundler commonly used types +""" +rust-version = "1.67.1" + +[dependencies] +anyhow = "1" +educe = { version = "0.4", features = ["Debug", "Default"] } +ethers = { version = "2.0.1", features = ["solc-full"] } +expanded-pathbuf = "0.1" +jsonrpsee = { version = "0.16", features = ["server", "macros"] } +rustc-hex = "^2.0.1" +serde = "1" +serde_json = "1" + +[features] +test-utils = [] \ No newline at end of file diff --git a/crates/primitives/src/bundler.rs b/crates/primitives/src/bundler.rs new file mode 100644 index 00000000..97e2dc63 --- /dev/null +++ b/crates/primitives/src/bundler.rs @@ -0,0 +1,11 @@ +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub enum Mode { + #[serde(rename = "auto")] + Auto, + #[serde(rename = "manual")] + Manual, +} + +pub const DEFAULT_INTERVAL: u64 = 10; diff --git a/crates/primitives/src/error_codes.rs b/crates/primitives/src/error_codes.rs new file mode 100644 index 00000000..ef99394a --- /dev/null +++ b/crates/primitives/src/error_codes.rs @@ -0,0 +1,13 @@ +// simulation +pub const SIMULATE_VALIDATION_ERROR_CODE: i32 = -32500; +pub const OPCODE_VALIDATION_ERROR_CODE: i32 = -32502; + +// reputation +pub const ENTITY_BANNED_ERROR_CODE: i32 = -32504; +pub const STAKE_TOO_LOW_ERROR_CODE: i32 = -32505; + +// sanity check +pub const USER_OPERATION_HASH_ERROR_CODE: i32 = -32601; +pub const SANITY_CHECK_ERROR_CODE: i32 = -32602; + +pub const EXECUTION_ERROR_CODE: i32 = -32521; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs new file mode 100644 index 00000000..508aca67 --- /dev/null +++ b/crates/primitives/src/lib.rs @@ -0,0 +1,25 @@ +#![allow(dead_code)] + +mod bundler; +mod error_codes; +mod reputation; +mod sanity_check; +mod simulation; +mod user_operation; +mod utils; +mod wallet; + +pub use bundler::{Mode, DEFAULT_INTERVAL}; +pub use error_codes::*; +pub use reputation::{ + BadReputationError, ReputationEntry, ReputationStatus, StakeInfo, BAN_SLACK, + MIN_INCLUSION_RATE_DENOMINATOR, THROTTLED_MAX_INCLUDE, THROTTLING_SLACK, +}; +pub use sanity_check::SanityCheckError; +pub use simulation::{CodeHash, SimulationError}; +pub use user_operation::{ + UserOperation, UserOperationByHash, UserOperationGasEstimation, UserOperationHash, + UserOperationPartial, UserOperationReceipt, +}; +pub use utils::{get_addr, parse_address, parse_u256}; +pub use wallet::Wallet; diff --git a/src/types/reputation.rs b/crates/primitives/src/reputation.rs similarity index 97% rename from src/types/reputation.rs rename to crates/primitives/src/reputation.rs index dcca3713..1e50d288 100644 --- a/src/types/reputation.rs +++ b/crates/primitives/src/reputation.rs @@ -7,11 +7,11 @@ use jsonrpsee::types::{error::ErrorCode, ErrorObject}; use serde::{Deserialize, Serialize}; use serde_json::json; +use crate::error_codes::{ENTITY_BANNED_ERROR_CODE, STAKE_TOO_LOW_ERROR_CODE}; + pub const MIN_INCLUSION_RATE_DENOMINATOR: u64 = 10; pub const THROTTLING_SLACK: u64 = 10; pub const BAN_SLACK: u64 = 50; -const ENTITY_BANNED_ERROR_CODE: i32 = -32504; -const STAKE_TOO_LOW_ERROR_CODE: i32 = -32505; // If the paymaster is throttle, maximum amount in one bundle is 1. pub const THROTTLED_MAX_INCLUDE: u64 = 1; diff --git a/crates/primitives/src/sanity_check.rs b/crates/primitives/src/sanity_check.rs new file mode 100644 index 00000000..dc6ca092 --- /dev/null +++ b/crates/primitives/src/sanity_check.rs @@ -0,0 +1,3 @@ +use jsonrpsee::types::ErrorObject; + +pub type SanityCheckError = ErrorObject<'static>; diff --git a/crates/primitives/src/simulation.rs b/crates/primitives/src/simulation.rs new file mode 100644 index 00000000..aa00a763 --- /dev/null +++ b/crates/primitives/src/simulation.rs @@ -0,0 +1,26 @@ +use ethers::{ + prelude::{EthAbiCodec, EthAbiType}, + types::{Address, H256}, +}; +use jsonrpsee::types::ErrorObject; +use serde::{Deserialize, Serialize}; + +pub type SimulationError = ErrorObject<'static>; + +#[derive( + Debug, + Default, + Clone, + Eq, + PartialEq, + PartialOrd, + Ord, + Serialize, + Deserialize, + EthAbiCodec, + EthAbiType, +)] +pub struct CodeHash { + pub address: Address, + pub hash: H256, +} diff --git a/src/types/user_operation.rs b/crates/primitives/src/user_operation.rs similarity index 77% rename from src/types/user_operation.rs rename to crates/primitives/src/user_operation.rs index f6855c09..68fb41b7 100644 --- a/src/types/user_operation.rs +++ b/crates/primitives/src/user_operation.rs @@ -1,14 +1,12 @@ -use crate::contracts::gen::entry_point_api::{self, EntryPointAPICalls}; -use ethers::abi::{AbiDecode, AbiEncode}; -use ethers::prelude::{EthAbiCodec, EthAbiType}; -use ethers::types::{Address, BlockNumber, Bytes, Log, TransactionReceipt, H256, U256}; -use ethers::utils::keccak256; -use reth_db::table::{Compress, Decode, Decompress, Encode}; +use ethers::{ + abi::AbiEncode, + prelude::{EthAbiCodec, EthAbiType}, + types::{Address, BlockNumber, Bytes, Log, TransactionReceipt, H256, U256}, + utils::keccak256, +}; use rustc_hex::FromHexError; use serde::{Deserialize, Serialize}; -use std::ops::Deref; -use std::str::FromStr; -use std::vec; +use std::{ops::Deref, str::FromStr}; use super::utils::as_checksum; @@ -35,12 +33,6 @@ impl From<[u8; 32]> for UserOperationHash { } } -impl From for crate::uopool::server::types::H256 { - fn from(value: UserOperationHash) -> Self { - Self::from(value.0) - } -} - impl FromStr for UserOperationHash { type Err = FromHexError; fn from_str(s: &str) -> Result { @@ -48,20 +40,41 @@ impl FromStr for UserOperationHash { } } -impl Decode for UserOperationHash { - fn decode>(value: B) -> Result { - Ok(H256::from_slice(value.into().as_ref()).into()) +impl UserOperationHash { + #[inline] + pub const fn as_fixed_bytes(&self) -> &[u8; 32] { + &self.0 .0 } -} -impl Encode for UserOperationHash { - type Encoded = [u8; 32]; - fn encode(self) -> Self::Encoded { - *self.0.as_fixed_bytes() + #[inline] + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + &mut self.0 .0 + } + + #[inline] + pub const fn repeat_byte(byte: u8) -> UserOperationHash { + UserOperationHash(H256([byte; 32])) + } + + #[inline] + pub const fn zero() -> UserOperationHash { + UserOperationHash::repeat_byte(0u8) + } + + pub fn assign_from_slice(&mut self, src: &[u8]) { + self.as_bytes_mut().copy_from_slice(src); + } + + pub fn from_slice(src: &[u8]) -> Self { + let mut ret = Self::zero(); + ret.assign_from_slice(src); + ret } } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, EthAbiCodec, EthAbiType)] +#[derive( + Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, EthAbiCodec, EthAbiType, +)] #[serde(rename_all = "camelCase")] pub struct UserOperation { #[serde(serialize_with = "as_checksum")] @@ -78,42 +91,6 @@ pub struct UserOperation { pub signature: Bytes, } -impl From for entry_point_api::UserOperation { - fn from(user_operation: UserOperation) -> Self { - Self { - sender: user_operation.sender, - nonce: user_operation.nonce, - init_code: user_operation.init_code, - call_data: user_operation.call_data, - call_gas_limit: user_operation.call_gas_limit, - verification_gas_limit: user_operation.verification_gas_limit, - pre_verification_gas: user_operation.pre_verification_gas, - max_fee_per_gas: user_operation.max_fee_per_gas, - max_priority_fee_per_gas: user_operation.max_priority_fee_per_gas, - paymaster_and_data: user_operation.paymaster_and_data, - signature: user_operation.signature, - } - } -} - -impl From for UserOperation { - fn from(value: entry_point_api::UserOperation) -> Self { - Self { - sender: value.sender, - nonce: value.nonce, - init_code: value.init_code, - call_data: value.call_data, - call_gas_limit: value.call_gas_limit, - verification_gas_limit: value.verification_gas_limit, - pre_verification_gas: value.pre_verification_gas, - max_fee_per_gas: value.max_fee_per_gas, - max_priority_fee_per_gas: value.max_priority_fee_per_gas, - paymaster_and_data: value.paymaster_and_data, - signature: value.signature, - } - } -} - impl UserOperation { pub fn pack(&self) -> Bytes { Bytes::from(self.clone().encode()) @@ -144,7 +121,7 @@ impl UserOperation { .into() } - #[cfg(test)] + #[cfg(feature = "test-utils")] pub fn random() -> Self { Self { sender: Address::random(), @@ -162,19 +139,6 @@ impl UserOperation { } } -impl Compress for UserOperation { - type Compressed = Bytes; - fn compress(self) -> Self::Compressed { - self.pack() - } -} - -impl Decompress for UserOperation { - fn decompress>(value: B) -> Result { - Self::decode(value.into()).map_err(|_e| reth_db::Error::DecodeError) - } -} - #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct UserOperationReceipt { @@ -300,17 +264,6 @@ pub struct UserOperationGasEstimation { pub call_gas_limit: U256, } -pub fn parse_from_input_data(data: Bytes) -> Option> { - EntryPointAPICalls::decode(data) - .ok() - .and_then(|call| match call { - EntryPointAPICalls::HandleOps(ops) => { - Some(ops.ops.into_iter().map(|op| op.into()).collect()) - } - _ => None, - }) -} - #[cfg(test)] mod tests { use std::str::FromStr; @@ -438,12 +391,4 @@ mod tests { .into() ); } - - #[test] - fn parse_input_data() { - let data = Bytes::from_str("0x1fad948c0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000690b9a9e9aa1c9db991c7721a92d351db4fac990000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000001ec271771e84999634e5e0330970feeb1c75f35200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000493e00000000000000000000000000000000000000000000000000000000077359400000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024a9e966b7000000000000000000000000000000000000000000000000000000000010f4470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002face000000000000000000000000000000000000000000000000000000000000") - .unwrap(); - let res = parse_from_input_data(data); - assert!(matches!(res, Some(..)), "No user operation found") - } } diff --git a/crates/primitives/src/utils.rs b/crates/primitives/src/utils.rs new file mode 100644 index 00000000..486ffcc3 --- /dev/null +++ b/crates/primitives/src/utils.rs @@ -0,0 +1,28 @@ +use ethers::{ + types::{Address, U256}, + utils::to_checksum, +}; +use std::str::FromStr; + +pub fn as_checksum(val: &Address, serializer: S) -> Result +where + S: serde::Serializer, +{ + serializer.serialize_str(&to_checksum(val, None)) +} + +// Try to get the address from first 20 bytes. Return None if length of bytes < 20. +pub fn get_addr(bytes: &[u8]) -> Option
{ + if bytes.len() >= 20 { + Some(Address::from_slice(&bytes[0..20])) + } else { + None + } +} + +pub fn parse_address(s: &str) -> Result { + Address::from_str(s).map_err(|_| format!("Adress {s} is not a valid address")) +} +pub fn parse_u256(s: &str) -> Result { + U256::from_str_radix(s, 10).map_err(|_| format!("{s} is not a valid U256")) +} diff --git a/src/models/wallet.rs b/crates/primitives/src/wallet.rs similarity index 100% rename from src/models/wallet.rs rename to crates/primitives/src/wallet.rs diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml new file mode 100644 index 00000000..f2eadf5a --- /dev/null +++ b/crates/rpc/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "aa-bundler-rpc" +version = "0.1.0" +authors = ["Vid Kersic "] +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/Vid201/aa-bundler" +readme = "README.md" +description = """ +AA (ERC-4337) Bundler RPC implementation +""" +rust-version = "1.67.1" + +[dependencies] +aa-bundler-primitives = { path = "../primitives" } +aa-bundler-grpc = { path = "../grpc" } + +anyhow = "1" +async-trait = "0.1" +ethers = { version = "2.0.1", features = ["solc-full"] } +jsonrpsee = { version = "0.16", features = ["server", "macros"] } +serde_json = "1" +tracing = "0.1" +tonic = { version = "0.8", default-features = false, features = [ + "transport", +] } \ No newline at end of file diff --git a/src/rpc/debug.rs b/crates/rpc/src/debug.rs similarity index 90% rename from src/rpc/debug.rs rename to crates/rpc/src/debug.rs index e41cd459..e2ba8d34 100644 --- a/src/rpc/debug.rs +++ b/crates/rpc/src/debug.rs @@ -1,22 +1,17 @@ -use super::debug_api::DebugApiServer; -use crate::{ - bundler::{Mode, DEFAULT_INTERVAL}, - types::{reputation::ReputationEntry, user_operation::UserOperation}, - uopool::server::{ - bundler::{bundler_client::BundlerClient, Mode as GrpcMode, SetModeRequest}, - uopool::{ - uo_pool_client::UoPoolClient, ClearResult, GetAllReputationRequest, - GetAllReputationResult, GetAllRequest, GetAllResult, SetReputationRequest, - SetReputationResult, - }, - }, +use aa_bundler_grpc::{ + bundler_client::BundlerClient, uo_pool_client::UoPoolClient, ClearResult, + GetAllReputationRequest, GetAllReputationResult, GetAllRequest, GetAllResult, Mode as GrpcMode, + SetModeRequest, SetReputationRequest, SetReputationResult, }; +use aa_bundler_primitives::{Mode, ReputationEntry, UserOperation, DEFAULT_INTERVAL}; use anyhow::format_err; use async_trait::async_trait; use ethers::types::{Address, H256}; use jsonrpsee::core::RpcResult; use tracing::{debug, trace}; +use crate::debug_api::DebugApiServer; + pub struct DebugApiServerImpl { pub uopool_grpc_client: UoPoolClient, pub bundler_grpc_client: BundlerClient, diff --git a/src/rpc/debug_api.rs b/crates/rpc/src/debug_api.rs similarity index 89% rename from src/rpc/debug_api.rs rename to crates/rpc/src/debug_api.rs index eec5c18c..2f2099ee 100644 --- a/src/rpc/debug_api.rs +++ b/crates/rpc/src/debug_api.rs @@ -1,7 +1,4 @@ -use crate::{ - bundler::Mode, - types::{reputation::ReputationEntry, user_operation::UserOperation}, -}; +use aa_bundler_primitives::{Mode, ReputationEntry, UserOperation}; use ethers::types::{Address, H256}; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; diff --git a/src/rpc/eth.rs b/crates/rpc/src/eth.rs similarity index 95% rename from src/rpc/eth.rs rename to crates/rpc/src/eth.rs index 45e98f9d..8ef2f070 100644 --- a/src/rpc/eth.rs +++ b/crates/rpc/src/eth.rs @@ -1,15 +1,12 @@ use std::str::FromStr; -use crate::{ - rpc::eth_api::EthApiServer, - types::user_operation::{ - UserOperation, UserOperationByHash, UserOperationGasEstimation, UserOperationHash, - UserOperationPartial, UserOperationReceipt, - }, - uopool::server::uopool::{ - uo_pool_client::UoPoolClient, AddRequest, AddResult, EstimateUserOperationGasRequest, - EstimateUserOperationGasResult, UserOperationHashRequest, - }, +use aa_bundler_grpc::{ + uo_pool_client::UoPoolClient, AddRequest, AddResult, EstimateUserOperationGasRequest, + EstimateUserOperationGasResult, UserOperationHashRequest, +}; +use aa_bundler_primitives::{ + UserOperation, UserOperationByHash, UserOperationGasEstimation, UserOperationHash, + UserOperationPartial, UserOperationReceipt, USER_OPERATION_HASH_ERROR_CODE, }; use anyhow::format_err; use async_trait::async_trait; @@ -23,7 +20,7 @@ use jsonrpsee::{ }; use tracing::{debug, trace}; -const USER_OPERATION_HASH_ERROR_CODE: i32 = -32601; +use crate::eth_api::EthApiServer; pub struct EthApiServerImpl { pub call_gas_limit: u64, diff --git a/src/rpc/eth_api.rs b/crates/rpc/src/eth_api.rs similarity index 97% rename from src/rpc/eth_api.rs rename to crates/rpc/src/eth_api.rs index f1a594a3..edee2adf 100644 --- a/src/rpc/eth_api.rs +++ b/crates/rpc/src/eth_api.rs @@ -1,10 +1,9 @@ -use ethers::types::{Address, U64}; -use jsonrpsee::{core::RpcResult, proc_macros::rpc}; - -use crate::types::user_operation::{ +use aa_bundler_primitives::{ UserOperation, UserOperationByHash, UserOperationGasEstimation, UserOperationHash, UserOperationPartial, UserOperationReceipt, }; +use ethers::types::{Address, U64}; +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; #[rpc(server, namespace = "eth")] pub trait EthApi { diff --git a/crates/rpc/src/lib.rs b/crates/rpc/src/lib.rs new file mode 100644 index 00000000..61d9e421 --- /dev/null +++ b/crates/rpc/src/lib.rs @@ -0,0 +1,11 @@ +#![allow(dead_code)] + +mod debug; +mod debug_api; +mod eth; +mod eth_api; + +pub use debug::DebugApiServerImpl; +pub use debug_api::DebugApiServer; +pub use eth::EthApiServerImpl; +pub use eth_api::EthApiServer; diff --git a/crates/uopool/Cargo.toml b/crates/uopool/Cargo.toml new file mode 100644 index 00000000..89e69065 --- /dev/null +++ b/crates/uopool/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "aa-bundler-uopool" +version = "0.1.0" +authors = ["Vid Kersic "] +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/Vid201/aa-bundler" +readme = "README.md" +description = """ +AA (ERC-4337) Bundler uopool implementation +""" +rust-version = "1.67.1" + +[dependencies] +aa-bundler-contracts = { path = "../contracts" } +aa-bundler-primitives = { path = "../primitives" } + +anyhow = "1" +educe = { version = "0.4", features = ["Debug", "Default"] } +ethers = { version = "2.0.1", features = ["solc-full"] } +jsonrpsee = { version = "0.16", features = ["server", "macros"] } +lazy_static = "1.4.0" +page_size = "0.5.0" +prost = "0.11" +reth-db = { git = "https://github.com/paradigmxyz/reth.git", rev = "aa6f2cb0610fb4fa0926b42cfed7f8ff51e0db8a" } +reth-libmdbx = { git = "https://github.com/paradigmxyz/reth.git", rev = "aa6f2cb0610fb4fa0926b42cfed7f8ff51e0db8a" } +serde = "1" +tokio = { version = "1.18", features = ["full"] } +tracing = "0.1" + +[dev-dependencies] +aa-bundler-primitives = { path = "../primitives", features = ["test-utils"] } +tempdir = "0.3.7" \ No newline at end of file diff --git a/crates/uopool/src/canonical/mod.rs b/crates/uopool/src/canonical/mod.rs new file mode 100644 index 00000000..3cd7a1b2 --- /dev/null +++ b/crates/uopool/src/canonical/mod.rs @@ -0,0 +1,2 @@ +pub mod sanity_check; +pub mod simulation; diff --git a/src/uopool/sanity_check.rs b/crates/uopool/src/canonical/sanity_check.rs similarity index 73% rename from src/uopool/sanity_check.rs rename to crates/uopool/src/canonical/sanity_check.rs index e1dba488..46436f86 100644 --- a/src/uopool/sanity_check.rs +++ b/crates/uopool/src/canonical/sanity_check.rs @@ -1,21 +1,181 @@ -use crate::{ - chain::gas::{calculate_valid_gas, Overhead}, - types::{ - reputation::{ReputationStatus, StakeInfo}, - sanity_check::BadUserOperationError, - user_operation::{UserOperation, UserOperationHash}, - }, +use aa_bundler_primitives::{ + ReputationStatus, SanityCheckError, StakeInfo, UserOperation, UserOperationHash, + EXECUTION_ERROR_CODE, SANITY_CHECK_ERROR_CODE, }; use ethers::{ providers::Middleware, - types::{Address, U256}, + types::{Address, Bytes, U256}, }; +use jsonrpsee::types::error::ErrorCode; -use super::UoPool; +use crate::{ + utils::{calculate_valid_gas, Overhead}, + UoPool, +}; const MAX_UOS_PER_UNSTAKED_SENDER: usize = 4; const GAS_INCREASE_PERC: u64 = 10; +#[derive(Debug)] +pub enum BadUserOperationError { + SenderOrInitCode { + sender: Address, + init_code: Bytes, + }, + FactoryVerification { + init_code: Bytes, + }, + HighVerificationGasLimit { + verification_gas_limit: U256, + max_verification_gas: U256, + }, + LowPreVerificationGas { + pre_verification_gas: U256, + calculated_pre_verification_gas: U256, + }, + PaymasterVerification { + paymaster_and_data: Bytes, + }, + LowCallGasLimit { + call_gas_limit: U256, + call_gas_estimation: U256, + }, + LowMaxFeePerGas { + max_fee_per_gas: U256, + max_fee_per_gas_estimated: U256, + }, + HighMaxPriorityFeePerGas { + max_priority_fee_per_gas: U256, + max_fee_per_gas: U256, + }, + LowMaxPriorityFeePerGas { + max_priority_fee_per_gas: U256, + min_priority_fee_per_gas: U256, + }, + SenderVerification { + sender: Address, + }, + UserOperationExecution { + message: String, + }, + Middleware(M::Error), + UnknownError { + error: String, + }, +} + +impl From> for SanityCheckError { + fn from(error: BadUserOperationError) -> Self { + match error { + BadUserOperationError::SenderOrInitCode { sender, init_code } => { + SanityCheckError::owned( + SANITY_CHECK_ERROR_CODE, + format!( + "Either the sender {sender} is an existing contract, or the initCode {init_code} is not empty (but not both)", + ), + None::, + ) + }, + BadUserOperationError::FactoryVerification { init_code } => SanityCheckError::owned( + SANITY_CHECK_ERROR_CODE, + format!("Init code {init_code} is not valid (factory check)",), + None::, + ), + BadUserOperationError::HighVerificationGasLimit { + verification_gas_limit, + max_verification_gas, + } => SanityCheckError::owned( + SANITY_CHECK_ERROR_CODE, + format!( + "Verification gas limit {verification_gas_limit} is higher than max verification gas {max_verification_gas}", + ), + None::, + ), + BadUserOperationError::LowPreVerificationGas { + pre_verification_gas, + calculated_pre_verification_gas, + } => SanityCheckError::owned( + SANITY_CHECK_ERROR_CODE, + format!( + "Pre-verification gas {pre_verification_gas} is lower than calculated pre-verification gas {calculated_pre_verification_gas}", + ), + None::, + ), + BadUserOperationError::PaymasterVerification { paymaster_and_data } => { + SanityCheckError::owned( + SANITY_CHECK_ERROR_CODE, + format!( + "Paymaster and data {paymaster_and_data} is invalid (paymaster check)", + ), + None::, + ) + }, + BadUserOperationError::LowCallGasLimit { + call_gas_limit, + call_gas_estimation, + } => SanityCheckError::owned( + SANITY_CHECK_ERROR_CODE, + format!( + "Call gas limit {call_gas_limit} is lower than call gas estimation {call_gas_estimation}", + ), + None::, + ), + BadUserOperationError::LowMaxFeePerGas { + max_fee_per_gas, + max_fee_per_gas_estimated, + } => SanityCheckError::owned( + SANITY_CHECK_ERROR_CODE, + format!( + "Max fee per gas {max_fee_per_gas} is lower than estimated max fee per gas {max_fee_per_gas_estimated}", + ), + None::, + ), + BadUserOperationError::HighMaxPriorityFeePerGas { + max_priority_fee_per_gas, + max_fee_per_gas, + } => SanityCheckError::owned( + SANITY_CHECK_ERROR_CODE, + format!( + "Max priority fee per gas {max_priority_fee_per_gas} is higher than max fee per gas {max_fee_per_gas}", + ), + None::, + ), + BadUserOperationError::LowMaxPriorityFeePerGas { + max_priority_fee_per_gas, + min_priority_fee_per_gas, + } => SanityCheckError::owned( + SANITY_CHECK_ERROR_CODE, + format!( + "Max priority fee per gas {max_priority_fee_per_gas} is lower than min priority fee per gas {min_priority_fee_per_gas}", + ), + None::, + ), + BadUserOperationError::SenderVerification { sender } => SanityCheckError::owned( + SANITY_CHECK_ERROR_CODE, + format!("Sender {sender} is invalid (sender check)",), + None::, + ), + BadUserOperationError::UserOperationExecution { message } => { + SanityCheckError::owned( + EXECUTION_ERROR_CODE, + message, + None::, + ) + }, + BadUserOperationError::Middleware(_) => { + SanityCheckError::from(ErrorCode::InternalError) + }, + BadUserOperationError::UnknownError { error } => { + SanityCheckError::owned( + SANITY_CHECK_ERROR_CODE, + error, + None::, + ) + }, + } + } +} + #[derive(Debug)] pub struct SanityCheckResult { pub user_operation_hash: Option, @@ -267,17 +427,19 @@ impl UoPool { #[cfg(test)] mod tests { - use crate::{ - contracts::EntryPoint, - types::reputation::{BAN_SLACK, MIN_INCLUSION_RATE_DENOMINATOR, THROTTLING_SLACK}, - uopool::{memory_mempool::MemoryMempool, memory_reputation::MemoryReputation, Reputation}, - }; + use aa_bundler_contracts::EntryPoint; + use aa_bundler_primitives::{BAN_SLACK, MIN_INCLUSION_RATE_DENOMINATOR, THROTTLING_SLACK}; use ethers::{ providers::{Http, Provider}, types::{Address, Bytes, U256}, }; use std::{str::FromStr, sync::Arc}; + use crate::{ + memory::{mempool::MemoryMempool, reputation::MemoryReputation}, + reputation::Reputation, + }; + use super::*; #[tokio::test] diff --git a/src/uopool/simulation.rs b/crates/uopool/src/canonical/simulation.rs similarity index 81% rename from src/uopool/simulation.rs rename to crates/uopool/src/canonical/simulation.rs index d5ad79de..bec5d002 100644 --- a/src/uopool/simulation.rs +++ b/crates/uopool/src/canonical/simulation.rs @@ -1,20 +1,10 @@ -use super::UoPool; -use crate::{ - contracts::{ - gen::{ValidatePaymasterUserOpReturn, CONTRACTS_FUNCTIONS}, - tracer::{Call, CallEntry, JsTracerFrame}, - EntryPointErr, SimulateValidationResult, - }, - types::{ - reputation::StakeInfo, - simulation::{ - CodeHash, SimulateValidationError, CREATE2_OPCODE, CREATE_OPCODE, FORBIDDEN_OPCODES, - LEVEL_TO_ENTITY, NUMBER_LEVELS, PAYMASTER_VALIDATION_FUNCTION, RETURN_OPCODE, - REVERT_OPCODE, - }, - user_operation::UserOperation, - }, - uopool::utils::equal_code_hashes, +use aa_bundler_contracts::{ + Call, CallEntry, EntryPointErr, JsTracerFrame, SimulateValidationResult, + ValidatePaymasterUserOpReturn, CONTRACTS_FUNCTIONS, +}; +use aa_bundler_primitives::{ + CodeHash, SimulationError, StakeInfo, UserOperation, EXECUTION_ERROR_CODE, + OPCODE_VALIDATION_ERROR_CODE, SIMULATE_VALIDATION_ERROR_CODE, }; use ethers::{ abi::AbiDecode, @@ -22,10 +12,91 @@ use ethers::{ types::{Address, Bytes, GethTrace, H256, U256}, utils::keccak256, }; +use jsonrpsee::types::error::ErrorCode; +use lazy_static::lazy_static; use std::collections::{HashMap, HashSet}; use tokio::task::JoinSet; use tracing::trace; +use crate::{utils::equal_code_hashes, UoPool}; + +// https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/core/EntryPoint.sol#L514 +// 0 - factory, 1 - sender/account, 2 - paymaster +// opcode NUMBER is marker between levels +const NUMBER_LEVELS: usize = 3; +const LEVEL_TO_ENTITY: [&str; NUMBER_LEVELS] = ["factory", "account", "paymaster"]; + +lazy_static! { + static ref FORBIDDEN_OPCODES: HashSet = { + let mut set = HashSet::new(); + set.insert("GASPRICE".to_string()); + set.insert("GASLIMIT".to_string()); + set.insert("DIFFICULTY".to_string()); + set.insert("TIMESTAMP".to_string()); + set.insert("BASEFEE".to_string()); + set.insert("BLOCKHASH".to_string()); + set.insert("NUMBER".to_string()); + set.insert("SELFBALANCE".to_string()); + set.insert("BALANCE".to_string()); + set.insert("ORIGIN".to_string()); + set.insert("GAS".to_string()); + set.insert("CREATE".to_string()); + set.insert("COINBASE".to_string()); + set.insert("SELFDESTRUCT".to_string()); + set.insert("RANDOM".to_string()); + set.insert("PREVRANDAO".to_string()); + set + }; + static ref CREATE2_OPCODE: String = "CREATE2".to_string(); + static ref RETURN_OPCODE: String = "RETURN".to_string(); + static ref REVERT_OPCODE: String = "REVERT".to_string(); + static ref CREATE_OPCODE: String = "CREATE".to_string(); + static ref PAYMASTER_VALIDATION_FUNCTION: String = "validatePaymasterUserOp".to_string(); +} + +#[derive(Debug)] +pub enum SimulateValidationError { + UserOperationRejected { message: String }, + OpcodeValidation { entity: String, opcode: String }, + UserOperationExecution { message: String }, + StorageAccessValidation { slot: String }, + CallStackValidation { message: String }, + CodeHashesValidation { message: String }, + UnknownError { error: String }, +} + +impl From for SimulationError { + fn from(error: SimulateValidationError) -> Self { + match error { + SimulateValidationError::UserOperationRejected { message } => { + SimulationError::owned(SIMULATE_VALIDATION_ERROR_CODE, message, None::) + } + SimulateValidationError::OpcodeValidation { entity, opcode } => SimulationError::owned( + OPCODE_VALIDATION_ERROR_CODE, + format!("{entity} uses banned opcode: {opcode}"), + None::, + ), + SimulateValidationError::UserOperationExecution { message } => { + SimulationError::owned(EXECUTION_ERROR_CODE, message, None::) + } + SimulateValidationError::StorageAccessValidation { slot } => SimulationError::owned( + OPCODE_VALIDATION_ERROR_CODE, + format!("Storage access validation failed for slot: {slot}"), + None::, + ), + SimulateValidationError::CallStackValidation { message } => { + SimulationError::owned(OPCODE_VALIDATION_ERROR_CODE, message, None::) + } + SimulateValidationError::CodeHashesValidation { message } => { + SimulationError::owned(OPCODE_VALIDATION_ERROR_CODE, message, None::) + } + SimulateValidationError::UnknownError { error } => { + SimulationError::owned(ErrorCode::InternalError.code(), error, None::) + } + } + } +} + #[derive(Debug)] pub struct SimulationResult { pub simulate_validation_result: SimulateValidationResult, diff --git a/src/uopool/database_mempool.rs b/crates/uopool/src/database/mempool.rs similarity index 77% rename from src/uopool/database_mempool.rs rename to crates/uopool/src/database/mempool.rs index 04cfd0fe..8fb828ee 100644 --- a/src/uopool/database_mempool.rs +++ b/crates/uopool/src/database/mempool.rs @@ -1,11 +1,4 @@ -use std::{fmt::Display, path::PathBuf}; - -use super::Mempool; -use crate::types::{ - simulation::CodeHash, - user_operation::{UserOperation, UserOperationHash}, - utils::WrapAddress, -}; +use aa_bundler_primitives::{CodeHash, UserOperation, UserOperationHash}; use ethers::types::{Address, U256}; use reth_db::{ cursor::{DbCursorRO, DbDupCursorRO}, @@ -21,21 +14,26 @@ use reth_db::{ transaction::{DbTx, DbTxMut}, Error, TableType, }; +use std::{fmt::Display, path::PathBuf}; + +use crate::mempool::Mempool; + +use super::utils::{WrapAddress, WrapCodeHash, WrapUserOperation, WrapUserOperationHash}; table!( /// UserOperation DB - ( UserOperationDB ) UserOperationHash | UserOperation + ( UserOperationDB ) WrapUserOperationHash | WrapUserOperation ); table!( /// SenderUserOperation DB /// Benefit for merklization is that hashed addresses/keys are sorted. - ( SenderUserOperationDB ) WrapAddress | UserOperation + ( SenderUserOperationDB ) WrapAddress | WrapUserOperation ); dupsort!( /// CodeHash DB - ( CodeHashDB ) UserOperationHash | [WrapAddress] CodeHash + ( CodeHashDB ) WrapUserOperationHash | [WrapAddress] WrapCodeHash ); /// Default tables that should be present inside database. @@ -114,8 +112,12 @@ impl Mempool for DatabaseMempool { ) -> Result { let hash = user_operation.hash(entry_point, chain_id); let tx = self.env.tx_mut()?; - tx.put::(hash, user_operation.clone())?; - tx.put::(user_operation.sender.into(), user_operation)?; + + let wrap_user_operation_hash: WrapUserOperationHash = hash.into(); + let wrap_user_operation: WrapUserOperation = user_operation.clone().into(); + + tx.put::(wrap_user_operation_hash, wrap_user_operation.clone())?; + tx.put::(user_operation.sender.into(), wrap_user_operation)?; tx.commit()?; Ok(hash) } @@ -124,10 +126,13 @@ impl Mempool for DatabaseMempool { &self, user_operation_hash: &UserOperationHash, ) -> Result, DBError> { + let wrap_user_operation_hash: WrapUserOperationHash = (*user_operation_hash).into(); + let tx = self.env.tx()?; - let res = tx.get::(*user_operation_hash)?; + let res = tx.get::(wrap_user_operation_hash)?; tx.commit()?; - Ok(res) + + Ok(res.map(|uo| uo.into())) } fn get_all_by_sender(&self, sender: &Address) -> Self::UserOperations { @@ -136,9 +141,9 @@ impl Mempool for DatabaseMempool { .tx() .and_then(|tx| { let mut cursor = tx.cursor_dup_read::()?; - let res = cursor + let res: Vec = cursor .walk_dup(Some(wrap_sender.clone()), Some(Address::default().into()))? - .map(|a| a.map(|(_, v)| v)) + .map(|a| a.map(|(_, v)| v.into())) .collect::, _>>()?; tx.commit()?; Ok(res) @@ -165,20 +170,27 @@ impl Mempool for DatabaseMempool { &self, user_operation_hash: &UserOperationHash, ) -> anyhow::Result { + let wrap_user_operation_hash: WrapUserOperationHash = (*user_operation_hash).into(); + let tx = self.env.tx()?; - let res = tx.get::(*user_operation_hash)?; + let res = tx.get::(wrap_user_operation_hash)?; tx.commit()?; Ok(res.is_some()) } fn get_code_hashes(&self, user_operation_hash: &UserOperationHash) -> Self::CodeHashes { + let wrap_user_operation_hash: WrapUserOperationHash = (*user_operation_hash).into(); + self.env .tx() .and_then(|tx| { let mut cursor = tx.cursor_dup_read::()?; - let res = cursor - .walk_dup(Some(*user_operation_hash), Some(Address::default().into()))? - .map(|a| a.map(|(_, v)| v)) + let res: Vec = cursor + .walk_dup( + Some(wrap_user_operation_hash), + Some(Address::default().into()), + )? + .map(|a| a.map(|(_, v)| v.into())) .collect::, _>>()?; tx.commit()?; Ok(res) @@ -191,24 +203,28 @@ impl Mempool for DatabaseMempool { user_operation_hash: &UserOperationHash, code_hashes: &Self::CodeHashes, ) -> anyhow::Result<(), Self::Error> { + let wrap_user_operation_hash: WrapUserOperationHash = (*user_operation_hash).into(); + let tx = self.env.tx_mut()?; - let res = tx.get::(*user_operation_hash)?; + let res = tx.get::(wrap_user_operation_hash.clone())?; if res.is_some() { - tx.delete::(*user_operation_hash, None)?; + tx.delete::(wrap_user_operation_hash.clone(), None)?; } for code_hash in code_hashes { - tx.put::(*user_operation_hash, code_hash.clone())?; + tx.put::(wrap_user_operation_hash.clone(), code_hash.clone().into())?; } tx.commit()?; Ok(()) } fn remove(&mut self, user_operation_hash: &UserOperationHash) -> Result<(), DBError> { + let wrap_user_operation_hash: WrapUserOperationHash = (*user_operation_hash).into(); + let tx = self.env.tx_mut()?; - if let Some(user_op) = tx.get::(*user_operation_hash)? { - tx.delete::(*user_operation_hash, None)?; - tx.delete::(user_op.sender.into(), Some(user_op))?; - tx.delete::(*user_operation_hash, None)?; + if let Some(user_op) = tx.get::(wrap_user_operation_hash.clone())? { + tx.delete::(wrap_user_operation_hash.clone(), None)?; + tx.delete::(user_op.0.sender.into(), Some(user_op))?; + tx.delete::(wrap_user_operation_hash, None)?; tx.commit()?; Ok(()) } else { @@ -221,9 +237,9 @@ impl Mempool for DatabaseMempool { .tx() .and_then(|tx| { let mut cursor = tx.cursor_read::()?; - let mut user_ops = cursor - .walk(Some(UserOperationHash::default()))? - .map(|a| a.map(|(_, uo)| uo)) + let mut user_ops: Vec = cursor + .walk(Some(WrapUserOperationHash::default()))? + .map(|a| a.map(|(_, uo)| uo.into())) .collect::, _>>()?; user_ops.sort_by(|a, b| { if a.max_priority_fee_per_gas != b.max_priority_fee_per_gas { @@ -242,9 +258,9 @@ impl Mempool for DatabaseMempool { .tx() .and_then(|tx| { let mut c = tx.cursor_read::()?; - let res = c - .walk(Some(UserOperationHash::default()))? - .map(|a| a.map(|(_, v)| v)) + let res: Vec = c + .walk(Some(WrapUserOperationHash::default()))? + .map(|a| a.map(|(_, v)| v.into())) .collect::, _>>()?; tx.commit()?; Ok(res) @@ -329,9 +345,8 @@ impl DatabaseMempool { #[cfg(test)] mod tests { - use crate::uopool::utils::tests::mempool_test_case; - use super::*; + use crate::utils::tests::mempool_test_case; use reth_db::mdbx::NoWriteMap; use tempdir::TempDir; diff --git a/crates/uopool/src/database/mod.rs b/crates/uopool/src/database/mod.rs new file mode 100644 index 00000000..5f58804d --- /dev/null +++ b/crates/uopool/src/database/mod.rs @@ -0,0 +1,2 @@ +pub mod mempool; +mod utils; diff --git a/crates/uopool/src/database/utils.rs b/crates/uopool/src/database/utils.rs new file mode 100644 index 00000000..1d1964ec --- /dev/null +++ b/crates/uopool/src/database/utils.rs @@ -0,0 +1,102 @@ +use aa_bundler_primitives::{CodeHash, UserOperation, UserOperationHash}; +use ethers::{ + abi::{AbiDecode, AbiEncode}, + prelude::{EthAbiCodec, EthAbiType}, + types::{Address, Bytes}, +}; +use reth_db::table::{Compress, Decode, Decompress, Encode}; +use serde::{Deserialize, Serialize}; + +macro_rules! construct_wrap_hash { + ($type:ty, $name:ident, $n_bytes:expr ) => { + #[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)] + pub struct $name($type); + + impl Decode for $name { + fn decode>(value: B) -> Result { + Ok(<$type>::from_slice(value.into().as_ref()).into()) + } + } + + impl Encode for $name { + type Encoded = [u8; $n_bytes]; + fn encode(self) -> Self::Encoded { + *self.0.as_fixed_bytes() + } + } + + impl From<$type> for $name { + fn from(value: $type) -> Self { + Self(value) + } + } + + impl From<$name> for $type { + fn from(value: $name) -> Self { + value.0 + } + } + + impl Compress for $name { + type Compressed = Bytes; + fn compress(self) -> Self::Compressed { + Bytes::from(self.encode()) + } + } + + impl Decompress for $name { + fn decompress>(value: B) -> Result { + Self::decode(value.into()).map_err(|_e| reth_db::Error::DecodeError) + } + } + }; +} + +macro_rules! construct_wrap_struct { + ($type:ty, $name:ident ) => { + #[derive( + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Clone, + Serialize, + Deserialize, + EthAbiCodec, + EthAbiType, + )] + pub struct $name(pub $type); + + impl Compress for $name { + type Compressed = Bytes; + fn compress(self) -> Self::Compressed { + Bytes::from(self.encode()) + } + } + + impl Decompress for $name { + fn decompress>(value: B) -> Result { + Self::decode(value.into()).map_err(|_e| reth_db::Error::DecodeError) + } + } + + impl From<$type> for $name { + fn from(value: $type) -> Self { + Self(value) + } + } + + impl From<$name> for $type { + fn from(value: $name) -> Self { + value.0 + } + } + }; +} + +construct_wrap_hash!(Address, WrapAddress, 20); +construct_wrap_hash!(UserOperationHash, WrapUserOperationHash, 32); + +construct_wrap_struct!(CodeHash, WrapCodeHash); +construct_wrap_struct!(UserOperation, WrapUserOperation); diff --git a/crates/uopool/src/lib.rs b/crates/uopool/src/lib.rs new file mode 100644 index 00000000..ae8db2df --- /dev/null +++ b/crates/uopool/src/lib.rs @@ -0,0 +1,18 @@ +#![allow(dead_code)] + +mod database; +mod memory; +mod mempool; +mod reputation; +mod uopool; +mod utils; + +pub use database::mempool::DatabaseMempool; +pub use memory::{mempool::MemoryMempool, reputation::MemoryReputation}; +pub use mempool::{mempool_id, MempoolId}; +pub use reputation::Reputation; +pub use uopool::UoPool; +pub use utils::Overhead; + +// canonical mempool +pub mod canonical; diff --git a/src/uopool/memory_mempool.rs b/crates/uopool/src/memory/mempool.rs similarity index 96% rename from src/uopool/memory_mempool.rs rename to crates/uopool/src/memory/mempool.rs index 5c65b734..c235008f 100644 --- a/src/uopool/memory_mempool.rs +++ b/crates/uopool/src/memory/mempool.rs @@ -1,13 +1,9 @@ +use aa_bundler_primitives::{CodeHash, UserOperation, UserOperationHash}; use educe::Educe; use ethers::types::{Address, U256}; use std::collections::{HashMap, HashSet}; -use crate::types::{ - simulation::CodeHash, - user_operation::{UserOperation, UserOperationHash}, -}; - -use super::Mempool; +use crate::mempool::Mempool; #[derive(Default, Educe)] #[educe(Debug)] @@ -145,7 +141,8 @@ impl Mempool for MemoryMempool { #[cfg(test)] mod tests { use super::*; - use crate::uopool::utils::tests::mempool_test_case; + use crate::utils::tests::mempool_test_case; + #[allow(clippy::unit_cmp)] #[tokio::test] async fn memory_mempool() { diff --git a/crates/uopool/src/memory/mod.rs b/crates/uopool/src/memory/mod.rs new file mode 100644 index 00000000..4ffaf941 --- /dev/null +++ b/crates/uopool/src/memory/mod.rs @@ -0,0 +1,2 @@ +pub mod mempool; +pub mod reputation; diff --git a/src/uopool/memory_reputation.rs b/crates/uopool/src/memory/reputation.rs similarity index 97% rename from src/uopool/memory_reputation.rs rename to crates/uopool/src/memory/reputation.rs index f25d861b..da16ca5e 100644 --- a/src/uopool/memory_reputation.rs +++ b/crates/uopool/src/memory/reputation.rs @@ -1,10 +1,9 @@ +use aa_bundler_primitives::{BadReputationError, ReputationEntry, ReputationStatus, StakeInfo}; use educe::Educe; use ethers::types::{Address, U256}; use std::collections::{HashMap, HashSet}; -use crate::types::reputation::{BadReputationError, ReputationEntry, ReputationStatus, StakeInfo}; - -use super::Reputation; +use crate::reputation::Reputation; #[derive(Default, Educe)] #[educe(Debug)] @@ -209,7 +208,7 @@ impl Reputation for MemoryReputation { #[cfg(test)] mod tests { - use crate::uopool::{BAN_SLACK, MIN_INCLUSION_RATE_DENOMINATOR, THROTTLING_SLACK}; + use aa_bundler_primitives::{BAN_SLACK, MIN_INCLUSION_RATE_DENOMINATOR, THROTTLING_SLACK}; use super::*; diff --git a/crates/uopool/src/mempool.rs b/crates/uopool/src/mempool.rs new file mode 100644 index 00000000..ef15377b --- /dev/null +++ b/crates/uopool/src/mempool.rs @@ -0,0 +1,52 @@ +use aa_bundler_primitives::{CodeHash, UserOperation, UserOperationHash}; +use ethers::{ + abi::AbiEncode, + types::{Address, H256, U256}, + utils::{keccak256, to_checksum}, +}; +use jsonrpsee::types::ErrorObject; +use std::fmt::Debug; + +pub type MempoolId = H256; + +pub type MempoolBox = + Box + Send + Sync>; + +pub type UoPoolError = ErrorObject<'static>; + +pub fn mempool_id(entry_point: &Address, chain_id: &U256) -> MempoolId { + H256::from_slice( + keccak256([to_checksum(entry_point, None).encode(), chain_id.encode()].concat()).as_slice(), + ) +} + +pub trait Mempool: Debug { + type UserOperations: IntoIterator; + type CodeHashes: IntoIterator; + type Error; + fn add( + &mut self, + user_operation: UserOperation, + entry_point: &Address, + chain_id: &U256, + ) -> Result; + fn get( + &self, + user_operation_hash: &UserOperationHash, + ) -> Result, Self::Error>; + fn get_all_by_sender(&self, sender: &Address) -> Self::UserOperations; + fn get_number_by_sender(&self, sender: &Address) -> usize; + fn has_code_hashes(&self, user_operation_hash: &UserOperationHash) + -> Result; + fn set_code_hashes( + &mut self, + user_operation_hash: &UserOperationHash, + code_hashes: &Self::CodeHashes, + ) -> Result<(), Self::Error>; + fn get_code_hashes(&self, user_operation_hash: &UserOperationHash) -> Self::CodeHashes; + fn remove(&mut self, user_operation_hash: &UserOperationHash) -> Result<(), Self::Error>; + // Get UserOperations sorted by max_priority_fee_per_gas without dup sender + fn get_sorted(&self) -> Result; + fn get_all(&self) -> Self::UserOperations; + fn clear(&mut self); +} diff --git a/crates/uopool/src/reputation.rs b/crates/uopool/src/reputation.rs new file mode 100644 index 00000000..88e4c39f --- /dev/null +++ b/crates/uopool/src/reputation.rs @@ -0,0 +1,55 @@ +use std::fmt::Debug; + +use aa_bundler_primitives::{ + get_addr, BadReputationError, ReputationEntry, ReputationStatus, StakeInfo, +}; +use ethers::types::{Address, Bytes, U256}; +use lazy_static::__Deref; + +pub type ReputationBox = Box + Send + Sync>; + +pub trait Reputation: Debug { + type ReputationEntries: IntoIterator; + + fn init( + &mut self, + min_inclusion_denominator: u64, + throttling_slack: u64, + ban_slack: u64, + min_stake: U256, + min_unstake_delay: U256, + ); + fn get(&mut self, address: &Address) -> ReputationEntry; + fn increment_seen(&mut self, address: &Address); + fn increment_included(&mut self, address: &Address); + fn update_hourly(&mut self); + fn add_whitelist(&mut self, address: &Address) -> bool; + fn remove_whitelist(&mut self, address: &Address) -> bool; + fn is_whitelist(&self, address: &Address) -> bool; + fn add_blacklist(&mut self, address: &Address) -> bool; + fn remove_blacklist(&mut self, address: &Address) -> bool; + fn is_blacklist(&self, address: &Address) -> bool; + fn get_status(&self, address: &Address) -> ReputationStatus; + fn update_handle_ops_reverted(&mut self, address: &Address); + fn verify_stake( + &self, + title: &str, + stake_info: Option, + ) -> Result<(), BadReputationError>; + + // Try to get the reputation status from a sequence of bytes which the first 20 bytes should be the address + // This is useful in getting the reputation directly from paymaster_and_data field and init_code field in user operation. + // If the address is not found in the first 20 bytes, it would return ReputationStatus::OK directly. + fn get_status_from_bytes(&self, bytes: &Bytes) -> ReputationStatus { + let address_opt = get_addr(bytes.deref()); + if let Some(address) = address_opt { + self.get_status(&address) + } else { + ReputationStatus::OK + } + } + + fn set(&mut self, reputation_entries: Self::ReputationEntries); + fn get_all(&self) -> Self::ReputationEntries; + fn clear(&mut self); +} diff --git a/crates/uopool/src/uopool.rs b/crates/uopool/src/uopool.rs new file mode 100644 index 00000000..fb88032f --- /dev/null +++ b/crates/uopool/src/uopool.rs @@ -0,0 +1,108 @@ +use std::sync::Arc; + +use aa_bundler_contracts::{EntryPoint, UserOperationEventFilter}; +use aa_bundler_primitives::{CodeHash, ReputationEntry, UserOperation, UserOperationHash}; +use ethers::{ + prelude::LogMeta, + providers::Middleware, + types::{Address, H256, U256}, +}; +use jsonrpsee::types::ErrorObject; +use tracing::warn; + +use crate::{ + canonical::{sanity_check::SanityCheckResult, simulation::SimulationResult}, + mempool::MempoolBox, + reputation::ReputationBox, +}; + +type VecUo = Vec; +type VecCh = Vec; + +#[derive(Debug)] +pub struct VerificationResult { + pub sanity_check_result: SanityCheckResult, + pub simulation_result: SimulationResult, +} + +pub struct UoPool { + pub entry_point: EntryPoint, + pub mempool: MempoolBox, + pub reputation: ReputationBox>, + pub eth_provider: Arc, + pub max_verification_gas: U256, + pub min_priority_fee_per_gas: U256, + pub chain_id: U256, +} + +impl UoPool { + pub fn new( + entry_point: EntryPoint, + mempool: MempoolBox, + reputation: ReputationBox>, + eth_provider: Arc, + max_verification_gas: U256, + min_priority_fee_per_gas: U256, + chain_id: U256, + ) -> Self { + Self { + entry_point, + mempool, + reputation, + eth_provider, + max_verification_gas, + min_priority_fee_per_gas, + chain_id, + } + } + + pub async fn verify_user_operation( + &self, + user_operation: &UserOperation, + ) -> Result> { + // sanity check + let sanity_check_result = self.validate_user_operation(user_operation).await?; + + // simulation + let simulation_result = self.simulate_user_operation(user_operation).await?; + + Ok(VerificationResult { + sanity_check_result, + simulation_result, + }) + } + + pub async fn get_user_operation_event_meta( + &self, + user_operation_hash: H256, + ) -> anyhow::Result> { + let mut event: Option<(UserOperationEventFilter, LogMeta)> = None; + let filter = self + .entry_point + .entry_point_api() + .event::() + .topic1(user_operation_hash); + let res: Vec<(UserOperationEventFilter, LogMeta)> = filter.query_with_meta().await?; + if res.len() >= 2 { + warn!( + "There are duplicate user operations with the same hash: {user_operation_hash:x?}" + ); + } + // It is possible have two same user operatation in same bundle + // see https://twitter.com/leekt216/status/1636414866662785024 + for log_meta in res.iter() { + event = Some(log_meta.clone()); + } + Ok(event) + } + + pub fn include_address(&mut self, addr: Address) -> Option<()> { + self.reputation.increment_included(&addr); + Some(()) + } + + pub fn remove_user_operation(&mut self, user_operation_hash: &UserOperationHash) -> Option<()> { + self.mempool.remove(user_operation_hash).ok(); + None + } +} diff --git a/src/uopool/utils.rs b/crates/uopool/src/utils.rs similarity index 52% rename from src/uopool/utils.rs rename to crates/uopool/src/utils.rs index 3cb24ee8..f4553d91 100644 --- a/src/uopool/utils.rs +++ b/crates/uopool/src/utils.rs @@ -1,18 +1,8 @@ +use aa_bundler_primitives::{CodeHash, UserOperation}; +use ethers::types::{u256_from_f64_saturating, Address, H256, U256}; +use lazy_static::__Deref; use std::collections::HashMap; -use ethers::types::{Address, H256}; - -use crate::types::simulation::CodeHash; - -// Try to get the address from first 20 bytes. Return None if length of bytes < 20. -pub fn get_addr(bytes: &[u8]) -> Option
{ - if bytes.len() >= 20 { - Some(Address::from_slice(&bytes[0..20])) - } else { - None - } -} - pub fn equal_code_hashes(code_hashes: &Vec, prev_code_hashes: &Vec) -> bool { if prev_code_hashes.len() != code_hashes.len() { return false; @@ -36,16 +26,95 @@ pub fn equal_code_hashes(code_hashes: &Vec, prev_code_hashes: &Vec Self { + Self { + fixed: U256::from(21000), + per_user_op: U256::from(18300), + per_user_op_word: U256::from(4), + zero_byte: U256::from(4), + non_zero_byte: U256::from(16), + bundle_size: U256::from(1), + sig_size: U256::from(65), + } + } +} + +impl Overhead { + pub fn calculate_pre_verification_gas(&self, user_operation: &UserOperation) -> U256 { + let user_operation_packed = user_operation.pack(); + let call_data_cost: U256 = U256::from( + user_operation_packed + .deref() + .iter() + .map(|&x| { + if x == 0 { + self.zero_byte.as_u128() + } else { + self.non_zero_byte.as_u128() + } + }) + .sum::(), + ); + u256_from_f64_saturating( + (self.fixed.as_u128() as f64) / (self.bundle_size.as_u128() as f64) + + (call_data_cost + + self.per_user_op + + self.per_user_op_word * user_operation_packed.len()) + .as_u128() as f64, + ) + } +} + +pub fn calculate_valid_gas(gas_price: U256, gas_increase_perc: U256) -> U256 { + let gas_price = gas_price.as_u64() as f64; + let gas_increase_perc = gas_increase_perc.as_u64() as f64; + U256::from((gas_price * (1.0 + gas_increase_perc / 100.0)).ceil() as u64) +} + #[cfg(test)] pub mod tests { - use std::fmt::Debug; - - use ethers::types::{Address, H256, U256}; + use std::{fmt::Debug, str::FromStr}; + + use aa_bundler_primitives::{UserOperation, UserOperationHash}; + use ethers::types::{Address, Bytes, H256, U256}; + + use super::*; + use crate::mempool::Mempool; + + #[test] + fn pre_verification_gas_calculation() { + let gas_overhead = Overhead::default(); + let user_operation = UserOperation { + sender: "0xAB7e2cbFcFb6A5F33A75aD745C3E5fB48d689B54".parse().unwrap(), + nonce: U256::zero(), + init_code: Bytes::from_str("0xe19e9755942bb0bd0cccce25b1742596b8a8250b3bf2c3e70000000000000000000000001d9a2cb3638c2fc8bf9c01d088b79e75cd188b17000000000000000000000000789d9058feecf1948af429793e7f1eb4a75db2220000000000000000000000000000000000000000000000000000000000000000").unwrap(), + call_data: Bytes::from_str("0x80c5c7d0000000000000000000000000ab7e2cbfcfb6a5f33a75ad745c3e5fb48d689b5400000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000").unwrap(), + call_gas_limit: U256::from(21900), + verification_gas_limit: U256::from(1218343), + pre_verification_gas: U256::from(50780), + max_fee_per_gas: U256::from(10064120791_u64), + max_priority_fee_per_gas: U256::from(1620899097), + paymaster_and_data: Bytes::default(), + signature: Bytes::from_str("0x4e69eb5e02d47ba28878655d61c59c20c3e9a2e6905381305626f6a5a2892ec12bd8dd59179f0642731e0e853af54a71ce422a1a234548c9dd1c559bd07df4461c").unwrap(), + }; - use crate::{ - types::user_operation::{UserOperation, UserOperationHash}, - uopool::Mempool, - }; + assert_eq!( + gas_overhead.calculate_pre_verification_gas(&user_operation), + U256::from(48684) + ); + } pub fn mempool_test_case(mut mempool: T, not_found_error_message: &str) where diff --git a/src/bundler/service.rs b/src/bundler/service.rs deleted file mode 100644 index 71bc929d..00000000 --- a/src/bundler/service.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::net::SocketAddr; - -use async_trait::async_trait; -use tonic::Response; -use tracing::{error, info}; - -use crate::uopool::server::{ - bundler::{ - bundler_server::{Bundler, BundlerServer}, - Mode, SendBundleNowResponse, SetModeRequest, SetModeResponse, SetModeResult, - }, - types::{GetChainIdResponse, GetSupportedEntryPointsResponse}, -}; - -use super::BundlerService; - -#[async_trait] -impl Bundler for BundlerService { - async fn chain_id( - &self, - _request: tonic::Request<()>, - ) -> Result, tonic::Status> { - todo!() - } - - async fn supported_entry_points( - &self, - _request: tonic::Request<()>, - ) -> Result, tonic::Status> { - todo!() - } - - async fn set_bundler_mode( - &self, - request: tonic::Request, - ) -> Result, tonic::Status> { - let req = request.into_inner(); - match req.mode() { - Mode::Manual => { - info!("Stopping auto bundling"); - self.stop_bundling(); - Ok(Response::new(SetModeResponse { - result: SetModeResult::Ok.into(), - })) - } - Mode::Auto => { - let interval = req.interval; - self.start_bundling(interval); - Ok(Response::new(SetModeResponse { - result: SetModeResult::Ok.into(), - })) - } - } - } - - async fn send_bundle_now( - &self, - _request: tonic::Request<()>, - ) -> Result, tonic::Status> { - let res = self.send_bundles_now().await.map_err(|e| { - error!("Send bundle manually with response {e:?}"); - tonic::Status::internal(format!("Send bundle now with error: {e:?}")) - })?; - Ok(Response::new(SendBundleNowResponse { - result: Some(res.into()), - })) - } -} - -pub fn run_server(bundler_service: BundlerService, listen_address: SocketAddr) { - tokio::spawn(async move { - let mut builder = tonic::transport::Server::builder(); - let svc = BundlerServer::new(bundler_service); - builder.add_service(svc).serve(listen_address).await - }); -} diff --git a/src/chain/gas.rs b/src/chain/gas.rs deleted file mode 100644 index b1eca180..00000000 --- a/src/chain/gas.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::types::user_operation::UserOperation; -use ethers::types::{u256_from_f64_saturating, U256}; -use std::ops::Deref; - -// https://github.com/eth-infinitism/bundler/blob/main/packages/sdk/src/calcPreVerificationGas.ts#L44-L52 -pub struct Overhead { - pub fixed: U256, - pub per_user_op: U256, - pub per_user_op_word: U256, - pub zero_byte: U256, - pub non_zero_byte: U256, - pub bundle_size: U256, - pub sig_size: U256, -} - -impl Default for Overhead { - fn default() -> Self { - Self { - fixed: U256::from(21000), - per_user_op: U256::from(18300), - per_user_op_word: U256::from(4), - zero_byte: U256::from(4), - non_zero_byte: U256::from(16), - bundle_size: U256::from(1), - sig_size: U256::from(65), - } - } -} - -impl Overhead { - pub fn calculate_pre_verification_gas(&self, user_operation: &UserOperation) -> U256 { - let user_operation_packed = user_operation.pack(); - let call_data_cost: U256 = U256::from( - user_operation_packed - .deref() - .iter() - .map(|&x| { - if x == 0 { - self.zero_byte.as_u128() - } else { - self.non_zero_byte.as_u128() - } - }) - .sum::(), - ); - u256_from_f64_saturating( - (self.fixed.as_u128() as f64) / (self.bundle_size.as_u128() as f64) - + (call_data_cost - + self.per_user_op - + self.per_user_op_word * user_operation_packed.len()) - .as_u128() as f64, - ) - } -} - -pub fn calculate_valid_gas(gas_price: U256, gas_increase_perc: U256) -> U256 { - let gas_price = gas_price.as_u64() as f64; - let gas_increase_perc = gas_increase_perc.as_u64() as f64; - U256::from((gas_price * (1.0 + gas_increase_perc / 100.0)).ceil() as u64) -} - -#[cfg(test)] -mod tests { - use super::*; - use ethers::types::Bytes; - use std::str::FromStr; - - #[test] - fn pre_verification_gas_calculation() { - let gas_overhead = Overhead::default(); - let user_operation = UserOperation { - sender: "0xAB7e2cbFcFb6A5F33A75aD745C3E5fB48d689B54".parse().unwrap(), - nonce: U256::zero(), - init_code: Bytes::from_str("0xe19e9755942bb0bd0cccce25b1742596b8a8250b3bf2c3e70000000000000000000000001d9a2cb3638c2fc8bf9c01d088b79e75cd188b17000000000000000000000000789d9058feecf1948af429793e7f1eb4a75db2220000000000000000000000000000000000000000000000000000000000000000").unwrap(), - call_data: Bytes::from_str("0x80c5c7d0000000000000000000000000ab7e2cbfcfb6a5f33a75ad745c3e5fb48d689b5400000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000").unwrap(), - call_gas_limit: U256::from(21900), - verification_gas_limit: U256::from(1218343), - pre_verification_gas: U256::from(50780), - max_fee_per_gas: U256::from(10064120791_u64), - max_priority_fee_per_gas: U256::from(1620899097), - paymaster_and_data: Bytes::default(), - signature: Bytes::from_str("0x4e69eb5e02d47ba28878655d61c59c20c3e9a2e6905381305626f6a5a2892ec12bd8dd59179f0642731e0e853af54a71ce422a1a234548c9dd1c559bd07df4461c").unwrap(), - }; - - assert_eq!( - gas_overhead.calculate_pre_verification_gas(&user_operation), - U256::from(48684) - ); - } -} diff --git a/src/chain/mod.rs b/src/chain/mod.rs deleted file mode 100644 index 8826db29..00000000 --- a/src/chain/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod gas; diff --git a/src/contracts/mod.rs b/src/contracts/mod.rs deleted file mode 100644 index 2151888a..00000000 --- a/src/contracts/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod entrypoint; -pub mod gen; -pub mod tracer; - -pub use entrypoint::*; diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index c71e56dd..00000000 --- a/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod bundler; -pub mod chain; -pub mod contracts; -pub mod models; -pub mod rpc; -pub mod types; -pub mod uopool; -pub mod utils; diff --git a/src/models/chainspec.rs b/src/models/chainspec.rs deleted file mode 100644 index b503acd1..00000000 --- a/src/models/chainspec.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(deny_unknown_fields)] -pub struct ChainSpec { - pub name: String, -} diff --git a/src/models/mod.rs b/src/models/mod.rs deleted file mode 100644 index d6eaecd5..00000000 --- a/src/models/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod chainspec; -pub mod wallet; diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs deleted file mode 100644 index d6489a25..00000000 --- a/src/rpc/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod debug; -pub mod debug_api; -pub mod eth; -pub mod eth_api; diff --git a/src/types/mod.rs b/src/types/mod.rs deleted file mode 100644 index 66f538fa..00000000 --- a/src/types/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod reputation; -pub mod sanity_check; -pub mod simulation; -pub mod user_operation; -pub mod utils; diff --git a/src/types/sanity_check.rs b/src/types/sanity_check.rs deleted file mode 100644 index 27d61e7d..00000000 --- a/src/types/sanity_check.rs +++ /dev/null @@ -1,170 +0,0 @@ -use ethers::{ - providers::Middleware, - types::{Address, Bytes, U256}, -}; -use jsonrpsee::types::{error::ErrorCode, ErrorObject}; - -const SANITY_CHECK_ERROR_CODE: i32 = -32602; -const SANITY_CHECK_EXECUTION_ERROR_CODE: i32 = -32521; - -pub type SanityCheckError = ErrorObject<'static>; - -#[derive(Debug)] -pub enum BadUserOperationError { - SenderOrInitCode { - sender: Address, - init_code: Bytes, - }, - FactoryVerification { - init_code: Bytes, - }, - HighVerificationGasLimit { - verification_gas_limit: U256, - max_verification_gas: U256, - }, - LowPreVerificationGas { - pre_verification_gas: U256, - calculated_pre_verification_gas: U256, - }, - PaymasterVerification { - paymaster_and_data: Bytes, - }, - LowCallGasLimit { - call_gas_limit: U256, - call_gas_estimation: U256, - }, - LowMaxFeePerGas { - max_fee_per_gas: U256, - max_fee_per_gas_estimated: U256, - }, - HighMaxPriorityFeePerGas { - max_priority_fee_per_gas: U256, - max_fee_per_gas: U256, - }, - LowMaxPriorityFeePerGas { - max_priority_fee_per_gas: U256, - min_priority_fee_per_gas: U256, - }, - SenderVerification { - sender: Address, - }, - UserOperationExecution { - message: String, - }, - Middleware(M::Error), - UnknownError { - error: String, - }, -} - -impl From> for SanityCheckError { - fn from(error: BadUserOperationError) -> Self { - match error { - BadUserOperationError::SenderOrInitCode { sender, init_code } => { - SanityCheckError::owned( - SANITY_CHECK_ERROR_CODE, - format!( - "Either the sender {sender} is an existing contract, or the initCode {init_code} is not empty (but not both)", - ), - None::, - ) - }, - BadUserOperationError::FactoryVerification { init_code } => SanityCheckError::owned( - SANITY_CHECK_ERROR_CODE, - format!("Init code {init_code} is not valid (factory check)",), - None::, - ), - BadUserOperationError::HighVerificationGasLimit { - verification_gas_limit, - max_verification_gas, - } => SanityCheckError::owned( - SANITY_CHECK_ERROR_CODE, - format!( - "Verification gas limit {verification_gas_limit} is higher than max verification gas {max_verification_gas}", - ), - None::, - ), - BadUserOperationError::LowPreVerificationGas { - pre_verification_gas, - calculated_pre_verification_gas, - } => SanityCheckError::owned( - SANITY_CHECK_ERROR_CODE, - format!( - "Pre-verification gas {pre_verification_gas} is lower than calculated pre-verification gas {calculated_pre_verification_gas}", - ), - None::, - ), - BadUserOperationError::PaymasterVerification { paymaster_and_data } => { - SanityCheckError::owned( - SANITY_CHECK_ERROR_CODE, - format!( - "Paymaster and data {paymaster_and_data} is invalid (paymaster check)", - ), - None::, - ) - }, - BadUserOperationError::LowCallGasLimit { - call_gas_limit, - call_gas_estimation, - } => SanityCheckError::owned( - SANITY_CHECK_ERROR_CODE, - format!( - "Call gas limit {call_gas_limit} is lower than call gas estimation {call_gas_estimation}", - ), - None::, - ), - BadUserOperationError::LowMaxFeePerGas { - max_fee_per_gas, - max_fee_per_gas_estimated, - } => SanityCheckError::owned( - SANITY_CHECK_ERROR_CODE, - format!( - "Max fee per gas {max_fee_per_gas} is lower than estimated max fee per gas {max_fee_per_gas_estimated}", - ), - None::, - ), - BadUserOperationError::HighMaxPriorityFeePerGas { - max_priority_fee_per_gas, - max_fee_per_gas, - } => SanityCheckError::owned( - SANITY_CHECK_ERROR_CODE, - format!( - "Max priority fee per gas {max_priority_fee_per_gas} is higher than max fee per gas {max_fee_per_gas}", - ), - None::, - ), - BadUserOperationError::LowMaxPriorityFeePerGas { - max_priority_fee_per_gas, - min_priority_fee_per_gas, - } => SanityCheckError::owned( - SANITY_CHECK_ERROR_CODE, - format!( - "Max priority fee per gas {max_priority_fee_per_gas} is lower than min priority fee per gas {min_priority_fee_per_gas}", - ), - None::, - ), - BadUserOperationError::SenderVerification { sender } => SanityCheckError::owned( - SANITY_CHECK_ERROR_CODE, - format!("Sender {sender} is invalid (sender check)",), - None::, - ), - BadUserOperationError::UserOperationExecution { message } => { - SanityCheckError::owned( - SANITY_CHECK_EXECUTION_ERROR_CODE, - message, - None::, - ) - }, - BadUserOperationError::Middleware(_) => { - SanityCheckError::from(ErrorCode::InternalError) - }, - BadUserOperationError::UnknownError { error } => { - SanityCheckError::owned( - SANITY_CHECK_ERROR_CODE, - error, - None::, - ) - }, - } - } -} diff --git a/src/types/simulation.rs b/src/types/simulation.rs deleted file mode 100644 index 85e42016..00000000 --- a/src/types/simulation.rs +++ /dev/null @@ -1,118 +0,0 @@ -use ethers::abi::{AbiDecode, AbiEncode}; -use ethers::prelude::EthAbiType; -use ethers::{ - prelude::EthAbiCodec, - types::{Address, Bytes, H256, U256}, -}; -use jsonrpsee::types::{error::ErrorCode, ErrorObject}; -use lazy_static::lazy_static; -use reth_db::table::{Compress, Decompress}; -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; - -const SIMULATE_VALIDATION_ERROR_CODE: i32 = -32500; -const OPCODE_VALIDATION_ERROR_CODE: i32 = -32502; -const SIMULATION_EXECUTION_ERROR_CODE: i32 = -32521; - -pub type SimulationError = ErrorObject<'static>; - -// https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/core/EntryPoint.sol#L514 -// 0 - factory, 1 - sender/account, 2 - paymaster -// opcode NUMBER is marker between levels -pub const NUMBER_LEVELS: usize = 3; -pub const LEVEL_TO_ENTITY: [&str; NUMBER_LEVELS] = ["factory", "account", "paymaster"]; - -lazy_static! { - pub static ref FORBIDDEN_OPCODES: HashSet = { - let mut set = HashSet::new(); - set.insert("GASPRICE".to_string()); - set.insert("GASLIMIT".to_string()); - set.insert("DIFFICULTY".to_string()); - set.insert("TIMESTAMP".to_string()); - set.insert("BASEFEE".to_string()); - set.insert("BLOCKHASH".to_string()); - set.insert("NUMBER".to_string()); - set.insert("SELFBALANCE".to_string()); - set.insert("BALANCE".to_string()); - set.insert("ORIGIN".to_string()); - set.insert("GAS".to_string()); - set.insert("CREATE".to_string()); - set.insert("COINBASE".to_string()); - set.insert("SELFDESTRUCT".to_string()); - set.insert("RANDOM".to_string()); - set.insert("PREVRANDAO".to_string()); - set - }; - pub static ref CREATE2_OPCODE: String = "CREATE2".to_string(); - pub static ref RETURN_OPCODE: String = "RETURN".to_string(); - pub static ref REVERT_OPCODE: String = "REVERT".to_string(); - pub static ref CREATE_OPCODE: String = "CREATE".to_string(); - pub static ref PAYMASTER_VALIDATION_FUNCTION: String = "validatePaymasterUserOp".to_string(); -} - -pub struct StakeInfo { - pub address: Address, - pub stake: (U256, U256), -} - -#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize, EthAbiCodec, EthAbiType)] -pub struct CodeHash { - pub address: Address, - pub hash: H256, -} - -impl Compress for CodeHash { - type Compressed = Bytes; - fn compress(self) -> Self::Compressed { - Bytes::from(self.encode()) - } -} - -impl Decompress for CodeHash { - fn decompress>(value: B) -> Result { - Self::decode(value.into()).map_err(|_e| reth_db::Error::DecodeError) - } -} - -#[derive(Debug)] -pub enum SimulateValidationError { - UserOperationRejected { message: String }, - OpcodeValidation { entity: String, opcode: String }, - UserOperationExecution { message: String }, - StorageAccessValidation { slot: String }, - CallStackValidation { message: String }, - CodeHashesValidation { message: String }, - UnknownError { error: String }, -} - -impl From for SimulationError { - fn from(error: SimulateValidationError) -> Self { - match error { - SimulateValidationError::UserOperationRejected { message } => { - SimulationError::owned(SIMULATE_VALIDATION_ERROR_CODE, message, None::) - } - SimulateValidationError::OpcodeValidation { entity, opcode } => SimulationError::owned( - OPCODE_VALIDATION_ERROR_CODE, - format!("{entity} uses banned opcode: {opcode}"), - None::, - ), - SimulateValidationError::UserOperationExecution { message } => { - SimulationError::owned(SIMULATION_EXECUTION_ERROR_CODE, message, None::) - } - SimulateValidationError::StorageAccessValidation { slot } => SimulationError::owned( - OPCODE_VALIDATION_ERROR_CODE, - format!("Storage access validation failed for slot: {slot}"), - None::, - ), - SimulateValidationError::CallStackValidation { message } => { - SimulationError::owned(OPCODE_VALIDATION_ERROR_CODE, message, None::) - } - SimulateValidationError::CodeHashesValidation { message } => { - SimulationError::owned(OPCODE_VALIDATION_ERROR_CODE, message, None::) - } - SimulateValidationError::UnknownError { error } => { - SimulationError::owned(ErrorCode::InternalError.code(), error, None::) - } - } - } -} diff --git a/src/types/utils.rs b/src/types/utils.rs deleted file mode 100644 index a24f3b0f..00000000 --- a/src/types/utils.rs +++ /dev/null @@ -1,60 +0,0 @@ -use ethers::{ - types::{Address, Bytes}, - utils::to_checksum, -}; -use reth_db::table::{Compress, Decode, Decompress, Encode}; -use serde::{Deserialize, Serialize}; - -pub fn as_checksum(val: &Address, serializer: S) -> Result -where - S: serde::Serializer, -{ - serializer.serialize_str(&to_checksum(val, None)) -} - -macro_rules! construct_wrap_hash { - ($type:ty, $name:ident, $n_bytes:expr ) => { - #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)] - pub struct $name($type); - - impl Decode for $name { - fn decode>(value: B) -> Result { - Ok(<$type>::from_slice(value.into().as_ref()).into()) - } - } - - impl Encode for $name { - type Encoded = [u8; $n_bytes]; - fn encode(self) -> Self::Encoded { - *self.0.as_fixed_bytes() - } - } - - impl From<$type> for $name { - fn from(value: $type) -> Self { - Self(value) - } - } - - impl From<$name> for $type { - fn from(value: $name) -> Self { - value.0 - } - } - - impl Compress for $name { - type Compressed = Bytes; - fn compress(self) -> Self::Compressed { - Bytes::from(self.encode()) - } - } - - impl Decompress for $name { - fn decompress>(value: B) -> Result { - Self::decode(value.into()).map_err(|_e| reth_db::Error::DecodeError) - } - } - }; -} - -construct_wrap_hash!(Address, WrapAddress, 20); diff --git a/src/uopool/mod.rs b/src/uopool/mod.rs deleted file mode 100644 index a93f7161..00000000 --- a/src/uopool/mod.rs +++ /dev/null @@ -1,296 +0,0 @@ -use self::{sanity_check::SanityCheckResult, simulation::SimulationResult}; -use crate::{ - contracts::{gen::UserOperationEventFilter, EntryPoint}, - types::{ - reputation::{ - BadReputationError, ReputationEntry, ReputationStatus, StakeInfo, BAN_SLACK, - MIN_INCLUSION_RATE_DENOMINATOR, THROTTLING_SLACK, - }, - simulation::CodeHash, - user_operation::{UserOperation, UserOperationHash}, - }, - uopool::{ - memory_mempool::MemoryMempool, memory_reputation::MemoryReputation, - server::uopool::uo_pool_server::UoPoolServer, service::UoPoolService, - }, - utils::parse_u256, -}; -use anyhow::Result; -use clap::Parser; -use dashmap::DashMap; -use ethers::{ - abi::AbiEncode, - prelude::LogMeta, - providers::{Http, Middleware, Provider}, - types::{Address, Bytes, H256, U256}, - utils::{keccak256, to_checksum}, -}; -use jsonrpsee::{tracing::info, types::ErrorObject}; -use lazy_static::__Deref; -use std::{fmt::Debug, net::SocketAddr, sync::Arc, time::Duration}; -use tracing::warn; - -pub mod database_mempool; -pub mod memory_mempool; -pub mod memory_reputation; -pub mod sanity_check; -pub mod server; -pub mod service; -pub mod simulation; -pub mod utils; - -pub type MempoolId = H256; - -pub type MempoolBox = - Box + Send + Sync>; -pub type ReputationBox = Box + Send + Sync>; - -pub type UoPoolError = ErrorObject<'static>; -type VecUo = Vec; -type VecCh = Vec; - -pub fn mempool_id(entry_point: &Address, chain_id: &U256) -> MempoolId { - H256::from_slice( - keccak256([to_checksum(entry_point, None).encode(), chain_id.encode()].concat()).as_slice(), - ) -} - -pub trait Mempool: Debug { - type UserOperations: IntoIterator; - type CodeHashes: IntoIterator; - type Error; - fn add( - &mut self, - user_operation: UserOperation, - entry_point: &Address, - chain_id: &U256, - ) -> Result; - fn get( - &self, - user_operation_hash: &UserOperationHash, - ) -> Result, Self::Error>; - fn get_all_by_sender(&self, sender: &Address) -> Self::UserOperations; - fn get_number_by_sender(&self, sender: &Address) -> usize; - fn has_code_hashes(&self, user_operation_hash: &UserOperationHash) - -> Result; - fn set_code_hashes( - &mut self, - user_operation_hash: &UserOperationHash, - code_hashes: &Self::CodeHashes, - ) -> Result<(), Self::Error>; - fn get_code_hashes(&self, user_operation_hash: &UserOperationHash) -> Self::CodeHashes; - fn remove(&mut self, user_operation_hash: &UserOperationHash) -> Result<(), Self::Error>; - // Get UserOperations sorted by max_priority_fee_per_gas without dup sender - fn get_sorted(&self) -> Result; - fn get_all(&self) -> Self::UserOperations; - fn clear(&mut self); -} - -pub trait Reputation: Debug { - type ReputationEntries: IntoIterator; - - fn init( - &mut self, - min_inclusion_denominator: u64, - throttling_slack: u64, - ban_slack: u64, - min_stake: U256, - min_unstake_delay: U256, - ); - fn get(&mut self, address: &Address) -> ReputationEntry; - fn increment_seen(&mut self, address: &Address); - fn increment_included(&mut self, address: &Address); - fn update_hourly(&mut self); - fn add_whitelist(&mut self, address: &Address) -> bool; - fn remove_whitelist(&mut self, address: &Address) -> bool; - fn is_whitelist(&self, address: &Address) -> bool; - fn add_blacklist(&mut self, address: &Address) -> bool; - fn remove_blacklist(&mut self, address: &Address) -> bool; - fn is_blacklist(&self, address: &Address) -> bool; - fn get_status(&self, address: &Address) -> ReputationStatus; - fn update_handle_ops_reverted(&mut self, address: &Address); - fn verify_stake( - &self, - title: &str, - stake_info: Option, - ) -> Result<(), BadReputationError>; - - // Try to get the reputation status from a sequence of bytes which the first 20 bytes should be the address - // This is useful in getting the reputation directly from paymaster_and_data field and init_code field in user operation. - // If the address is not found in the first 20 bytes, it would return ReputationStatus::OK directly. - fn get_status_from_bytes(&self, bytes: &Bytes) -> ReputationStatus { - let address_opt = utils::get_addr(bytes.deref()); - if let Some(address) = address_opt { - self.get_status(&address) - } else { - ReputationStatus::OK - } - } - - fn set(&mut self, reputation_entries: Self::ReputationEntries); - fn get_all(&self) -> Self::ReputationEntries; - fn clear(&mut self); -} - -#[derive(Debug)] -pub struct VerificationResult { - pub sanity_check_result: SanityCheckResult, - pub simulation_result: SimulationResult, -} - -pub struct UoPool { - pub entry_point: EntryPoint, - pub mempool: MempoolBox, - pub reputation: ReputationBox>, - pub eth_provider: Arc, - pub max_verification_gas: U256, - pub min_priority_fee_per_gas: U256, - pub chain_id: U256, -} - -impl UoPool { - pub fn new( - entry_point: EntryPoint, - mempool: MempoolBox, - reputation: ReputationBox>, - eth_provider: Arc, - max_verification_gas: U256, - min_priority_fee_per_gas: U256, - chain_id: U256, - ) -> Self { - Self { - entry_point, - mempool, - reputation, - eth_provider, - max_verification_gas, - min_priority_fee_per_gas, - chain_id, - } - } - - async fn verify_user_operation( - &self, - user_operation: &UserOperation, - ) -> Result> { - // sanity check - let sanity_check_result = self.validate_user_operation(user_operation).await?; - - // simulation - let simulation_result = self.simulate_user_operation(user_operation).await?; - - Ok(VerificationResult { - sanity_check_result, - simulation_result, - }) - } - - async fn get_user_operation_event_meta( - &self, - user_operation_hash: H256, - ) -> anyhow::Result> { - let mut event: Option<(UserOperationEventFilter, LogMeta)> = None; - let filter = self - .entry_point - .entry_point_api() - .event::() - .topic1(user_operation_hash); - let res: Vec<(UserOperationEventFilter, LogMeta)> = filter.query_with_meta().await?; - if res.len() >= 2 { - warn!( - "There are duplicate user operations with the same hash: {user_operation_hash:x?}" - ); - } - // It is possible have two same user operatation in same bundle - // see https://twitter.com/leekt216/status/1636414866662785024 - for log_meta in res.iter() { - event = Some(log_meta.clone()); - } - Ok(event) - } -} - -#[derive(Clone, Copy, Debug, Parser, PartialEq)] -pub struct UoPoolOpts { - #[clap(long, default_value = "127.0.0.1:3001")] - pub uopool_grpc_listen_address: SocketAddr, - - #[clap(long, value_parser=parse_u256, default_value = "1")] - pub min_stake: U256, - - #[clap(long, value_parser=parse_u256, default_value = "0")] - pub min_unstake_delay: U256, - - #[clap(long, value_parser=parse_u256, default_value = "0")] - pub min_priority_fee_per_gas: U256, -} - -pub async fn run( - opts: UoPoolOpts, - entry_points: Vec
, - eth_provider: Arc>, - max_verification_gas: U256, -) -> Result<()> { - let chain_id = eth_provider.get_chainid().await?; - - tokio::spawn(async move { - let mut builder = tonic::transport::Server::builder(); - - let mempools_map = Arc::new(DashMap::>>::new()); - - for entry_point in entry_points { - let id = mempool_id(&entry_point, &chain_id); - - let mut reputation = Box::::default(); - reputation.init( - MIN_INCLUSION_RATE_DENOMINATOR, - THROTTLING_SLACK, - BAN_SLACK, - opts.min_stake, - opts.min_unstake_delay, - ); - - mempools_map.insert( - id, - UoPool::>::new( - EntryPoint::>::new(eth_provider.clone(), entry_point), - Box::::default(), - reputation, - eth_provider.clone(), - max_verification_gas, - opts.min_priority_fee_per_gas, - chain_id, - ), - ); - } - - let svc = UoPoolServer::new(UoPoolService::new( - mempools_map.clone(), - eth_provider.clone(), - chain_id, - )); - - tokio::spawn(async move { - loop { - mempools_map - .iter_mut() - .for_each(|mut mempool| mempool.value_mut().reputation.update_hourly()); - tokio::time::sleep(Duration::from_secs(60 * 60)).await; - } - }); - - info!( - "UoPool gRPC server starting on {}", - opts.uopool_grpc_listen_address - ); - - builder - .add_service(svc) - .serve(opts.uopool_grpc_listen_address) - .await - }); - - tokio::time::sleep(Duration::from_secs(1)).await; - - Ok(()) -} diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 60bee535..00000000 --- a/src/utils.rs +++ /dev/null @@ -1,9 +0,0 @@ -use ethers::types::{Address, U256}; -use std::str::FromStr; - -pub fn parse_address(s: &str) -> Result { - Address::from_str(s).map_err(|_| format!("Adress {s} is not a valid address")) -} -pub fn parse_u256(s: &str) -> Result { - U256::from_str_radix(s, 10).map_err(|_| format!("{s} is not a valid U256")) -} diff --git a/tests/Cargo.toml b/tests/Cargo.toml new file mode 100644 index 00000000..0b98baac --- /dev/null +++ b/tests/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "aa-bundler-tests" +version = "0.1.0" +authors = ["Vid Kersic "] +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/Vid201/aa-bundler" +readme = "README.md" +description = """ +AA (ERC-4337) Bundler integration tests +""" +rust-version = "1.67.1" + +[dev-dependencies] +aa-bundler-contracts = { path = "../crates/contracts" } +aa-bundler-primitives = { path = "../crates/primitives" } + +anyhow = "1" +ethers = { version = "2.0.1", features = ["solc-full"] } +tempdir = "0.3.7" +tokio = { version = "1.18", features = ["full"] } \ No newline at end of file diff --git a/tests/common/gen.rs b/tests/common/gen.rs deleted file mode 100644 index f4054eba..00000000 --- a/tests/common/gen.rs +++ /dev/null @@ -1,44 +0,0 @@ -use ethers::prelude::abigen; - -abigen!(SimpleAccountFactory, - "$CARGO_MANIFEST_DIR/thirdparty/account-abstraction/artifacts/contracts/samples/SimpleAccountFactory.sol/SimpleAccountFactory.json"); - -abigen!(SimpleAccount, - "$CARGO_MANIFEST_DIR/thirdparty/account-abstraction/artifacts/contracts/samples/SimpleAccount.sol/SimpleAccount.json"); - -abigen!( - EntryPointContract, - "$CARGO_MANIFEST_DIR/thirdparty/account-abstraction/artifacts/contracts/core/EntryPoint.sol/EntryPoint.json" -); -abigen!( - TestOpcodesAccountFactory, - "$CARGO_MANIFEST_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestOpcodesAccount.sol/TestOpcodesAccountFactory.json" -); -abigen!( - TestOpcodesAccount, - "$CARGO_MANIFEST_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestOpcodesAccount.sol/TestOpcodesAccount.json" -); -abigen!( - TestStorageAccount, - "$CARGO_MANIFEST_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestStorageAccount.sol/TestStorageAccount.json" -); -abigen!( - TestRecursionAccount, - "$CARGO_MANIFEST_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestRecursionAccount.sol/TestRecursionAccount.json" -); -abigen!( - TestStorageAccountFactory, - "$CARGO_MANIFEST_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestStorageAccount.sol/TestStorageAccountFactory.json" -); -abigen!( - TestRulesAccount, - "$CARGO_MANIFEST_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestRulesAccount.sol/TestRulesAccount.json" -); -abigen!( - TestRulesAccountFactory, - "$CARGO_MANIFEST_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestRulesAccount.sol/TestRulesAccountFactory.json" -); -abigen!( - TracerTest, - "$CARGO_MANIFEST_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TracerTest.sol/TracerTest.json" -); diff --git a/tests/src/common/gen.rs b/tests/src/common/gen.rs new file mode 100644 index 00000000..07dca22c --- /dev/null +++ b/tests/src/common/gen.rs @@ -0,0 +1,44 @@ +use ethers::prelude::abigen; + +abigen!(SimpleAccountFactory, + "$CARGO_WORKSPACE_DIR/thirdparty/account-abstraction/artifacts/contracts/samples/SimpleAccountFactory.sol/SimpleAccountFactory.json"); + +abigen!(SimpleAccount, + "$CARGO_WORKSPACE_DIR/thirdparty/account-abstraction/artifacts/contracts/samples/SimpleAccount.sol/SimpleAccount.json"); + +abigen!( + EntryPointContract, + "$CARGO_WORKSPACE_DIR/thirdparty/account-abstraction/artifacts/contracts/core/EntryPoint.sol/EntryPoint.json" +); +abigen!( + TestOpcodesAccountFactory, + "$CARGO_WORKSPACE_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestOpcodesAccount.sol/TestOpcodesAccountFactory.json" +); +abigen!( + TestOpcodesAccount, + "$CARGO_WORKSPACE_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestOpcodesAccount.sol/TestOpcodesAccount.json" +); +abigen!( + TestStorageAccount, + "$CARGO_WORKSPACE_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestStorageAccount.sol/TestStorageAccount.json" +); +abigen!( + TestRecursionAccount, + "$CARGO_WORKSPACE_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestRecursionAccount.sol/TestRecursionAccount.json" +); +abigen!( + TestStorageAccountFactory, + "$CARGO_WORKSPACE_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestStorageAccount.sol/TestStorageAccountFactory.json" +); +abigen!( + TestRulesAccount, + "$CARGO_WORKSPACE_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestRulesAccount.sol/TestRulesAccount.json" +); +abigen!( + TestRulesAccountFactory, + "$CARGO_WORKSPACE_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TestRulesAccount.sol/TestRulesAccountFactory.json" +); +abigen!( + TracerTest, + "$CARGO_WORKSPACE_DIR/thirdparty/bundler/packages/bundler/artifacts/contracts/tests/TracerTest.sol/TracerTest.json" +); diff --git a/tests/common/mod.rs b/tests/src/common/mod.rs similarity index 99% rename from tests/common/mod.rs rename to tests/src/common/mod.rs index 53ea273c..2f4e471a 100644 --- a/tests/common/mod.rs +++ b/tests/src/common/mod.rs @@ -1,6 +1,6 @@ use std::{ops::Mul, sync::Arc, time::Duration}; -use aa_bundler::types::user_operation::UserOperation; +use aa_bundler_primitives::UserOperation; use ethers::{ prelude::{ k256::ecdsa::SigningKey, MiddlewareBuilder, NonceManagerMiddleware, SignerMiddleware, diff --git a/tests/src/lib.rs b/tests/src/lib.rs new file mode 100644 index 00000000..db28b67c --- /dev/null +++ b/tests/src/lib.rs @@ -0,0 +1,8 @@ +#![allow(dead_code)] + +#[cfg(test)] +mod common; +#[cfg(test)] +mod tracer_tests; +#[cfg(test)] +mod validate_tests; diff --git a/tests/tracer_tests.rs b/tests/src/tracer_tests.rs similarity index 96% rename from tests/tracer_tests.rs rename to tests/src/tracer_tests.rs index edf3c798..b31c6cce 100644 --- a/tests/tracer_tests.rs +++ b/tests/src/tracer_tests.rs @@ -1,7 +1,6 @@ use std::{ops::Deref, str::FromStr, sync::Arc}; -use aa_bundler::contracts::tracer::{JsTracerFrame, JS_TRACER}; -use common::{deploy_tracer_test, gen::TracerTest, setup_geth, ClientType, DeployedContract}; +use aa_bundler_contracts::{JsTracerFrame, JS_TRACER}; use ethers::{ abi::{RawLog, Token}, contract::EthLogDecode, @@ -14,9 +13,11 @@ use ethers::{ utils::GethInstance, }; -use crate::common::gen::ExecSelfResultFilter; - -pub mod common; +use crate::common::{ + deploy_tracer_test, + gen::{ExecSelfResultFilter, TracerTest}, + setup_geth, ClientType, DeployedContract, +}; struct Context { _geth: GethInstance, diff --git a/tests/validate_tests.rs b/tests/src/validate_tests.rs similarity index 98% rename from tests/validate_tests.rs rename to tests/src/validate_tests.rs index 02d64cf0..e713a4f8 100644 --- a/tests/validate_tests.rs +++ b/tests/src/validate_tests.rs @@ -1,11 +1,4 @@ -pub mod common; - -use aa_bundler::types::user_operation::UserOperation; -use common::gen::{ - EntryPointContract, TestOpcodesAccount, TestOpcodesAccountFactory, TestRulesAccount, - TestRulesAccountFactory, TestStorageAccountFactory, -}; -use common::{setup_geth, ClientType, DeployedContract}; +use aa_bundler_primitives::UserOperation; use ethers::abi::Token; use ethers::prelude::BaseContract; use ethers::types::transaction::eip2718::TypedTransaction; @@ -22,6 +15,11 @@ use crate::common::{ deploy_entry_point, deploy_test_opcode_account, deploy_test_opcode_account_factory, deploy_test_recursion_account, deploy_test_rules_account_factory, deploy_test_storage_account, deploy_test_storage_account_factory, + gen::{ + EntryPointContract, TestOpcodesAccount, TestOpcodesAccountFactory, TestRulesAccount, + TestRulesAccountFactory, TestStorageAccountFactory, + }, + setup_geth, ClientType, DeployedContract, }; struct TestContext {