diff --git a/Cargo.lock b/Cargo.lock index 532cd24cfc5a..770e28567edd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,22 +79,41 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-eips", - "alloy-network", "alloy-primitives", "alloy-rlp", + "alloy-serde", "c-kzg", - "sha2 0.10.8", + "serde", + "sha2", + "thiserror", +] + +[[package]] +name = "alloy-contract" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types", + "alloy-sol-types", + "alloy-transport", + "futures", + "futures-util", "thiserror", ] [[package]] name = "alloy-dyn-abi" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2919acdad13336bc5dc26b636cdd6892c2f27fb0d4a58320a00c2713cf6a4e9a" +checksum = "872f239c15befa27cc4f0d3d82a70b3365c2d0202562bf906eb93b299fa31882" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -114,29 +133,32 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-primitives", "alloy-rlp", + "alloy-serde", + "c-kzg", + "derive_more", + "once_cell", "serde", - "thiserror", ] [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-primitives", - "alloy-rpc-types", + "alloy-serde", "serde", ] [[package]] name = "alloy-json-abi" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ed0f2a6c3a1c947b4508522a53a190dba8f94dcd4e3e1a5af945a498e78f2f" +checksum = "83a35ddfd27576474322a5869e4c123e5f3e7b2177297c18e4e82ea501cb125b" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -147,7 +169,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-primitives", "serde", @@ -158,20 +180,24 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ + "alloy-consensus", "alloy-eips", "alloy-json-rpc", "alloy-primitives", - "alloy-rlp", - "serde", + "alloy-rpc-types", + "alloy-signer", + "async-trait", + "futures-utils-wasm", + "thiserror", ] [[package]] name = "alloy-primitives" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" +checksum = "99bbad0a6b588ef4aec1b5ddbbfdacd9ef04e00b979617765b03174318ee1f3a" dependencies = [ "alloy-rlp", "arbitrary", @@ -195,25 +221,27 @@ dependencies = [ ] [[package]] -name = "alloy-providers" +name = "alloy-provider" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ + "alloy-eips", + "alloy-json-rpc", "alloy-network", "alloy-primitives", + "alloy-pubsub", "alloy-rpc-client", - "alloy-rpc-trace-types", "alloy-rpc-types", + "alloy-rpc-types-trace", "alloy-transport", - "alloy-transport-http", "async-stream", "async-trait", "auto_impl", + "dashmap", "futures", + "futures-utils-wasm", "lru", - "reqwest 0.11.27", - "serde", - "thiserror", + "serde_json", "tokio", "tracing", ] @@ -221,7 +249,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -255,63 +283,144 @@ checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] name = "alloy-rpc-client" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-json-rpc", + "alloy-primitives", + "alloy-pubsub", "alloy-transport", "alloy-transport-http", "futures", "pin-project", - "reqwest 0.11.27", "serde", "serde_json", "tokio", "tokio-stream", "tower", "tracing", - "url", ] [[package]] -name = "alloy-rpc-trace-types" +name = "alloy-rpc-types" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "itertools 0.12.1", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-primitives", "alloy-rpc-types", + "alloy-serde", "serde", "serde_json", ] [[package]] -name = "alloy-rpc-types" +name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-primitives", - "alloy-rlp", - "itertools 0.12.1", "serde", "serde_json", - "thiserror", ] [[package]] name = "alloy-signer" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ - "alloy-network", + "alloy-dyn-abi", "alloy-primitives", "alloy-sol-types", "async-trait", "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + +[[package]] +name = "alloy-signer-aws" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "aws-sdk-kms", + "k256", + "spki", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-signer-ledger" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "coins-ledger", + "futures-util", + "semver 1.0.22", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-signer-trezor" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "semver 1.0.22", + "thiserror", + "tracing", + "trezor-client", +] + +[[package]] +name = "alloy-signer-wallet" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", "coins-bip32", "coins-bip39", "elliptic-curve", @@ -323,38 +432,54 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86ec0a47740b20bc5613b8712d0d321d031c4efc58e9645af96085d5cccfc27" +checksum = "452d929748ac948a10481fff4123affead32c553cf362841c5103dd508bdfc16" dependencies = [ "alloy-json-abi", + "alloy-sol-macro-input", "const-hex", - "dunce", "heck 0.4.1", "indexmap", "proc-macro-error", "proc-macro2", "quote", - "serde_json", - "syn 2.0.57", + "syn 2.0.58", "syn-solidity", "tiny-keccak", ] +[[package]] +name = "alloy-sol-macro-input" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df64e094f6d2099339f9e82b5b38440b159757b6920878f28316243f8166c8d1" +dependencies = [ + "alloy-json-abi", + "const-hex", + "dunce", + "heck 0.5.0", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.58", + "syn-solidity", +] + [[package]] name = "alloy-sol-type-parser" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0045cc89524e1451ccf33e8581355b6027ac7c6e494bb02959d4213ad0d8e91d" +checksum = "715f4d09a330cc181fc7c361b5c5c2766408fa59a0bac60349dcb7baabd404cc" dependencies = [ "winnow 0.6.5", ] [[package]] name = "alloy-sol-types" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad09ec5853fa700d12d778ad224dcdec636af424d29fad84fb9a2f16a5b0ef09" +checksum = "43bc2d6dfc2a19fd56644494479510f98b1ee929e04cf0d4aa45e98baa3e545b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -366,11 +491,12 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-json-rpc", "base64 0.22.0", "futures-util", + "futures-utils-wasm", "serde", "serde_json", "thiserror", @@ -383,11 +509,11 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest 0.11.27", + "reqwest 0.12.2", "serde_json", "tower", "url", @@ -396,7 +522,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -414,7 +540,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=9ac2c90#9ac2c90d58a9994d4b61c879e33c6af2739a2b4f" +source = "git+https://github.com/alloy-rs/alloy?rev=5f74d4e#5f74d4e7a417662489e4165236c1251fee8e6078" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -429,9 +555,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9e1498416f7e7f09af8061970e14936846b6271e153aa5ba539a22a7eb414d" +checksum = "beb28aa4ecd32fdfa1b1bdd111ff7357dd562c6b2372694cf9e613434fcba659" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -536,11 +662,12 @@ dependencies = [ "alloy-json-abi", "alloy-network", "alloy-primitives", - "alloy-providers", + "alloy-provider", "alloy-rlp", - "alloy-rpc-trace-types", "alloy-rpc-types", + "alloy-rpc-types-trace", "alloy-signer", + "alloy-signer-wallet", "alloy-sol-types", "alloy-transport", "alloy-trie", @@ -599,8 +726,8 @@ dependencies = [ "alloy-network", "alloy-primitives", "alloy-rlp", - "alloy-rpc-trace-types", "alloy-rpc-types", + "alloy-rpc-types-trace", "alloy-trie", "anvil-core", "bytes", @@ -846,7 +973,7 @@ checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -868,7 +995,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -885,7 +1012,7 @@ checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -938,7 +1065,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -947,6 +1074,324 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "aws-config" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297b64446175a73987cedc3c438d79b2a654d0fff96f65ff530fbe039347644c" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 0.2.12", + "hyper 0.14.28", + "ring 0.17.8", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8587ae17c8e967e4b05a62d495be2fb7701bec52a97f7acfe8a29f938384c8" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-runtime" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13dc54b4b49f8288532334bba8f87386a40571c47c37b1304979b556dc613c8" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid 1.8.0", +] + +[[package]] +name = "aws-sdk-kms" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a4e610d67363f6846c903ebca4ce65439033d5ec2a5d8effc96d5eaa53355" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "019a07902c43b03167ea5df0182f0cb63fae89f9a9682c44d18cf2e4a042cb34" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c46ee08a48a7f4eaa4ad201dcc1dd537b49c50859d14d4510e00ad9d3f9af2" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f752ac730125ca6017f72f9db5ec1772c9ecc664f87aa7507a7d81b023c23713" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d6f29688a4be9895c0ba8bef861ad0c0dac5c15e9618b9b7a6c233990fc263" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.1.0", + "once_cell", + "percent-encoding", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f10fa66956f01540051b0aa7ad54574640f748f9839e843442d99b970d3aff9" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c53572b4cd934ee5e8461ad53caa36e9d246aaef42166e3ac539e206a925d330" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "http-body 1.0.0", + "hyper 0.14.28", + "hyper-rustls 0.24.2", + "once_cell", + "pin-project-lite", + "pin-utils", + "rustls 0.21.10", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb2b3a7030dc9a3c9a08ce0b25decea5130e9db19619d4dffbbff34f75fe850" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.1.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe14dceea1e70101d38fbf2a99e6a34159477c0fb95e68e05c66bd7ae4c3729" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "http 0.2.12", + "http 1.1.0", + "http-body 0.4.6", + "http-body 1.0.0", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872c68cf019c0e4afc5de7753c4f7288ce4b71663212771bf5e4542eb9346ca9" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dbf2f3da841a8930f159163175cf6a3d16ddde517c1b0fba7aa776822800f40" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "http 0.2.12", + "rustc_version 0.4.0", + "tracing", +] + [[package]] name = "axum" version = "0.6.20" @@ -1038,6 +1483,16 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + [[package]] name = "base64ct" version = "1.6.0" @@ -1094,19 +1549,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", - "radium", - "serde", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", + "radium", + "serde", + "tap", + "wyz", ] [[package]] @@ -1152,7 +1598,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2 0.10.8", + "sha2", "tinyvec", ] @@ -1215,6 +1661,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "bzip2" version = "0.4.4" @@ -1238,9 +1694,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "0.4.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a4bc5367b6284358d2a6a6a1dc2d92ec4b86034561c3b9d3341909752fd848" +checksum = "3130f3d8717cc02e668a896af24984d5d5d4e8bf12e278e982e0f1bd88a0f9af" dependencies = [ "blst", "cc", @@ -1292,14 +1748,23 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" name = "cast" version = "0.2.0" dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", + "alloy-json-rpc", + "alloy-network", "alloy-primitives", - "alloy-providers", + "alloy-provider", "alloy-rlp", "alloy-rpc-types", "alloy-signer", + "alloy-signer-wallet", + "alloy-sol-types", + "alloy-transport", "async-trait", + "aws-sdk-kms", "chrono", "clap", "clap_complete", @@ -1308,12 +1773,8 @@ dependencies = [ "const-hex", "criterion", "dunce", - "eth-keystore", "ethers-contract", "ethers-core", - "ethers-middleware", - "ethers-providers", - "ethers-signers", "evm-disassembler", "evmole", "eyre", @@ -1332,8 +1793,6 @@ dependencies = [ "rayon", "regex", "rpassword", - "rusoto_core", - "rusoto_kms", "semver 1.0.22", "serde", "serde_json", @@ -1421,7 +1880,6 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "serde", "windows-targets 0.52.4", ] @@ -1481,7 +1939,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.0", + "strsim 0.11.1", "terminal_size", "unicase", "unicode-width", @@ -1515,7 +1973,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1557,10 +2015,10 @@ dependencies = [ "bs58", "coins-core", "digest 0.10.7", - "hmac 0.12.1", + "hmac", "k256", "serde", - "sha2 0.10.8", + "sha2", "thiserror", ] @@ -1572,11 +2030,11 @@ checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" dependencies = [ "bitvec", "coins-bip32", - "hmac 0.12.1", + "hmac", "once_cell", "pbkdf2 0.12.2", "rand 0.8.5", - "sha2 0.10.8", + "sha2", "thiserror", ] @@ -1595,7 +2053,7 @@ dependencies = [ "ripemd", "serde", "serde_derive", - "sha2 0.10.8", + "sha2", "sha3", "thiserror", ] @@ -1916,16 +2374,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "ctr" version = "0.9.2" @@ -1966,7 +2414,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1977,7 +2425,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2038,7 +2486,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2059,7 +2507,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2069,7 +2517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2119,7 +2567,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "const-oid", "crypto-common", "subtle", @@ -2313,7 +2761,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2375,16 +2823,16 @@ dependencies = [ "ctr", "digest 0.10.7", "hex", - "hmac 0.12.1", + "hmac", "pbkdf2 0.11.0", "rand 0.8.5", "scrypt", "serde", "serde_json", - "sha2 0.10.8", + "sha2", "sha3", "thiserror", - "uuid", + "uuid 0.8.2", ] [[package]] @@ -2525,7 +2973,7 @@ dependencies = [ "reqwest 0.11.27", "serde", "serde_json", - "syn 2.0.57", + "syn 2.0.58", "toml 0.8.12", "walkdir", ] @@ -2543,7 +2991,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2569,7 +3017,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.2", - "syn 2.0.57", + "syn 2.0.58", "tempfile", "thiserror", "tiny-keccak", @@ -2666,23 +3114,14 @@ dependencies = [ "async-trait", "coins-bip32", "coins-bip39", - "coins-ledger", "const-hex", "elliptic-curve", "eth-keystore", "ethers-core", - "futures-executor", - "futures-util", - "home", "rand 0.8.5", - "rusoto_core", - "rusoto_kms", - "semver 1.0.22", - "sha2 0.10.8", - "spki", + "sha2", "thiserror", "tracing", - "trezor-client", ] [[package]] @@ -2698,7 +3137,7 @@ dependencies = [ "ethers-core", "glob", "home", - "md-5 0.10.6", + "md-5", "num_cpus", "once_cell", "path-slash", @@ -2821,7 +3260,7 @@ dependencies = [ "bytes", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2948,10 +3387,17 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" name = "forge" version = "0.2.0" dependencies = [ + "alloy-chains", + "alloy-consensus", "alloy-dyn-abi", "alloy-json-abi", + "alloy-network", "alloy-primitives", + "alloy-provider", "alloy-rpc-types", + "alloy-signer", + "alloy-signer-wallet", + "alloy-transport", "anvil", "async-trait", "axum", @@ -2965,9 +3411,6 @@ dependencies = [ "dunce", "ethers-contract", "ethers-core", - "ethers-middleware", - "ethers-providers", - "ethers-signers", "evm-disassembler", "eyre", "forge-doc", @@ -3065,18 +3508,21 @@ dependencies = [ name = "forge-script" version = "0.2.0" dependencies = [ + "alloy-chains", "alloy-dyn-abi", + "alloy-eips", "alloy-json-abi", + "alloy-network", "alloy-primitives", + "alloy-provider", "alloy-rpc-types", + "alloy-signer", + "alloy-transport", "async-recursion", "clap", "const-hex", "dialoguer", "dunce", - "ethers-core", - "ethers-providers", - "ethers-signers", "eyre", "forge-verify", "foundry-cheatcodes", @@ -3107,10 +3553,10 @@ version = "0.2.0" dependencies = [ "alloy-json-abi", "alloy-primitives", + "alloy-provider", "async-trait", "clap", "const-hex", - "ethers-providers", "eyre", "foundry-block-explorers", "foundry-cli", @@ -3142,9 +3588,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e88929768c22f9912694c634db053cfd480c3f7bd7eb39223e29f4fb40b64b7" +checksum = "ee75d972291181ae98bd1b48647ca8d8832159012b240ca1b7225085d4a63f00" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3166,9 +3612,10 @@ dependencies = [ "alloy-genesis", "alloy-json-abi", "alloy-primitives", - "alloy-providers", + "alloy-provider", "alloy-rpc-types", "alloy-signer", + "alloy-signer-wallet", "alloy-sol-types", "base64 0.22.0", "const-hex", @@ -3210,14 +3657,16 @@ dependencies = [ name = "foundry-cli" version = "0.2.0" dependencies = [ + "alloy-chains", "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", + "alloy-provider", + "alloy-transport", "clap", "color-eyre", + "const-hex", "dotenvy", - "ethers-core", - "ethers-providers", "eyre", "forge-fmt", "foundry-common", @@ -3226,6 +3675,7 @@ dependencies = [ "foundry-debugger", "foundry-evm", "foundry-wallets", + "futures", "indicatif", "once_cell", "regex", @@ -3244,15 +3694,17 @@ dependencies = [ name = "foundry-common" version = "0.2.0" dependencies = [ + "alloy-consensus", + "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", "alloy-json-rpc", "alloy-primitives", - "alloy-providers", + "alloy-provider", "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-signer", + "alloy-signer-wallet", "alloy-sol-types", "alloy-transport", "alloy-transport-http", @@ -3280,6 +3732,7 @@ dependencies = [ "pretty_assertions", "rand 0.8.5", "reqwest 0.11.27", + "reqwest 0.12.2", "rustc-hash", "semver 1.0.22", "serde", @@ -3296,9 +3749,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "079ada1a2093e0fec67caa15ccf018a2d1b5747c16ba1c11a28df53530eb1a5f" +checksum = "dd3323f90e9f256a2c359dbb1cc7e1b7e4fef9e04e5a82693895e959a6efe010" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3309,7 +3762,7 @@ dependencies = [ "futures-util", "home", "itertools 0.12.1", - "md-5 0.10.6", + "md-5", "memmap2 0.9.4", "once_cell", "path-slash", @@ -3319,7 +3772,7 @@ dependencies = [ "semver 1.0.22", "serde", "serde_json", - "sha2 0.10.8", + "sha2", "solang-parser", "svm-rs 0.4.1", "svm-rs-builds", @@ -3360,7 +3813,7 @@ dependencies = [ "tempfile", "thiserror", "toml 0.8.12", - "toml_edit 0.21.1", + "toml_edit 0.22.9", "tracing", "walkdir", ] @@ -3420,9 +3873,10 @@ dependencies = [ "alloy-genesis", "alloy-json-abi", "alloy-primitives", - "alloy-providers", + "alloy-provider", "alloy-rpc-types", "alloy-sol-types", + "alloy-transport", "arrayvec", "auto_impl", "const-hex", @@ -3531,7 +3985,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -3539,8 +3993,7 @@ name = "foundry-test-utils" version = "0.2.0" dependencies = [ "alloy-primitives", - "ethers-core", - "ethers-providers", + "alloy-provider", "eyre", "fd-lock 4.0.2", "foundry-common", @@ -3560,21 +4013,27 @@ dependencies = [ name = "foundry-wallets" version = "0.2.0" dependencies = [ + "alloy-consensus", + "alloy-dyn-abi", + "alloy-network", "alloy-primitives", + "alloy-signer", + "alloy-signer-aws", + "alloy-signer-ledger", + "alloy-signer-trezor", + "alloy-signer-wallet", + "alloy-sol-types", "async-trait", + "aws-config", + "aws-sdk-kms", "clap", "const-hex", "derive_builder", - "ethers-core", - "ethers-providers", - "ethers-signers", "eyre", "foundry-common", "foundry-config", "itertools 0.12.1", "rpassword", - "rusoto_core", - "rusoto_kms", "serde", "thiserror", "tokio", @@ -3708,7 +4167,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -3751,6 +4210,12 @@ dependencies = [ "slab", ] +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + [[package]] name = "fxhash" version = "0.2.1" @@ -4169,16 +4634,6 @@ dependencies = [ "rusb", ] -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - [[package]] name = "hmac" version = "0.12.1" @@ -4334,21 +4789,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" -dependencies = [ - "http 0.2.12", - "hyper 0.14.28", - "log", - "rustls 0.20.9", - "rustls-native-certs 0.6.3", - "tokio", - "tokio-rustls 0.23.4", -] - [[package]] name = "hyper-rustls" version = "0.24.2" @@ -4358,7 +4798,9 @@ dependencies = [ "futures-util", "http 0.2.12", "hyper 0.14.28", + "log", "rustls 0.21.10", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", ] @@ -4733,7 +5175,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.8", + "sha2", "signature", ] @@ -4921,17 +5363,6 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" -[[package]] -name = "md-5" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "md-5" version = "0.10.6" @@ -5023,7 +5454,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5300,7 +5731,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5352,12 +5783,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "open-fastrlp" version = "0.1.4" @@ -5417,7 +5842,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5444,6 +5869,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + [[package]] name = "overload" version = "0.1.1" @@ -5465,7 +5896,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -5567,9 +5998,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest 0.10.7", - "hmac 0.12.1", + "hmac", "password-hash", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -5579,7 +6010,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", - "hmac 0.12.1", + "hmac", ] [[package]] @@ -5602,7 +6033,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5660,7 +6091,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5671,7 +6102,7 @@ checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" dependencies = [ "once_cell", "pest", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -5763,7 +6194,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5801,7 +6232,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5912,7 +6343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -6007,7 +6438,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", "version_check", "yansi 1.0.1", ] @@ -6076,9 +6507,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7" +checksum = "5f0530d13d87d1f549b66a3e8d0c688952abe5994e204ed62615baaf25dc029c" dependencies = [ "bitflags 2.5.0", "memchr", @@ -6293,6 +6724,12 @@ dependencies = [ "regex-syntax 0.8.3", ] +[[package]] +name = "regex-lite" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -6346,7 +6783,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.25.4", "winreg", ] @@ -6388,14 +6825,15 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 0.26.1", "winreg", ] [[package]] name = "revm" -version = "7.1.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "217d21144d329f21d5245b8e6a46e0d6d0a527d9917d7a087f225b161e529169" +checksum = "72a454c1c650b2b2e23f0c461af09e6c31e1d15e1cbebe905a701c46b8a50afc" dependencies = [ "auto_impl", "cfg-if", @@ -6409,11 +6847,11 @@ dependencies = [ [[package]] name = "revm-inspectors" version = "0.1.0" -source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=ba0b6ab#ba0b6ab695802c752601f17f5c941b62a067ad64" +source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=510d4d0#510d4d0d06130d52ee996fa8aebd32cdc8267905" dependencies = [ "alloy-primitives", - "alloy-rpc-trace-types", "alloy-rpc-types", + "alloy-rpc-types-trace", "alloy-sol-types", "anstyle", "colorchoice", @@ -6425,9 +6863,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "3.3.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "776848391ed76d5103ca1aa1632cd21b521e2870afb30b63723da862d69efd0f" +checksum = "d322f2730cd300e99d271a1704a2dfb8973d832428f5aa282aaa40e2473b5eec" dependencies = [ "revm-primitives", "serde", @@ -6435,9 +6873,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "5.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3fd1856a7cb09197a02669d779e1afb5a627b0888a24814ba2b6a1ad4c3ff8d" +checksum = "931f692f3f4fc72ec39d5d270f8e9d208c4a6008de7590ee96cf948e3b6d3f8d" dependencies = [ "aurora-engine-modexp", "c-kzg", @@ -6446,15 +6884,15 @@ dependencies = [ "revm-primitives", "ripemd", "secp256k1", - "sha2 0.10.8", + "sha2", "substrate-bn", ] [[package]] name = "revm-primitives" -version = "3.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4d7d3e793e907dc0797a9d3b43abfdf5226d133855214db9bd27d4cee33ebd" +checksum = "cbbc9640790cebcb731289afb7a7d96d16ad94afeb64b5d0b66443bd151e79d6" dependencies = [ "alloy-primitives", "auto_impl", @@ -6475,7 +6913,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac 0.12.1", + "hmac", "subtle", ] @@ -6602,89 +7040,6 @@ dependencies = [ "libusb1-sys", ] -[[package]] -name = "rusoto_core" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db30db44ea73551326269adcf7a2169428a054f14faf9e1768f2163494f2fa2" -dependencies = [ - "async-trait", - "base64 0.13.1", - "bytes", - "crc32fast", - "futures", - "http 0.2.12", - "hyper 0.14.28", - "hyper-rustls 0.23.2", - "lazy_static", - "log", - "rusoto_credential", - "rusoto_signature", - "rustc_version 0.4.0", - "serde", - "serde_json", - "tokio", - "xml-rs", -] - -[[package]] -name = "rusoto_credential" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee0a6c13db5aad6047b6a44ef023dbbc21a056b6dab5be3b79ce4283d5c02d05" -dependencies = [ - "async-trait", - "chrono", - "dirs-next", - "futures", - "hyper 0.14.28", - "serde", - "serde_json", - "shlex", - "tokio", - "zeroize", -] - -[[package]] -name = "rusoto_kms" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1fc19cfcfd9f6b2f96e36d5b0dddda9004d2cbfc2d17543e3b9f10cc38fce8" -dependencies = [ - "async-trait", - "bytes", - "futures", - "rusoto_core", - "serde", - "serde_json", -] - -[[package]] -name = "rusoto_signature" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ae95491c8b4847931e291b151127eccd6ff8ca13f33603eb3d0035ecb05272" -dependencies = [ - "base64 0.13.1", - "bytes", - "chrono", - "digest 0.9.0", - "futures", - "hex", - "hmac 0.11.0", - "http 0.2.12", - "hyper 0.14.28", - "log", - "md-5 0.9.1", - "percent-encoding", - "pin-project-lite", - "rusoto_credential", - "rustc_version 0.4.0", - "serde", - "sha2 0.9.9", - "tokio", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -6734,18 +7089,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustls" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" -dependencies = [ - "log", - "ring 0.16.20", - "sct", - "webpki", -] - [[package]] name = "rustls" version = "0.21.10" @@ -6977,10 +7320,10 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" dependencies = [ - "hmac 0.12.1", + "hmac", "pbkdf2 0.11.0", "salsa20", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -7013,6 +7356,7 @@ version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" dependencies = [ + "rand 0.8.5", "secp256k1-sys", ] @@ -7104,7 +7448,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7158,7 +7502,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7204,7 +7548,7 @@ checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7224,19 +7568,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.8" @@ -7469,9 +7800,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" @@ -7501,7 +7832,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7514,7 +7845,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7550,7 +7881,7 @@ dependencies = [ "semver 1.0.22", "serde", "serde_json", - "sha2 0.10.8", + "sha2", "thiserror", "url", "zip", @@ -7570,7 +7901,7 @@ dependencies = [ "semver 1.0.22", "serde", "serde_json", - "sha2 0.10.8", + "sha2", "thiserror", "url", "zip", @@ -7602,9 +7933,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.57" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -7613,14 +7944,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3d0961cd53c23ea94eeec56ba940f636f6394788976e9f16ca5ee0aca7464a" +checksum = "4497156948bd342b52038035a6fa514a89626e37af9d2c52a5e8d8ebcc7ee479" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7730,7 +8061,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7872,7 +8203,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7885,17 +8216,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.9", - "tokio", - "webpki", -] - [[package]] name = "tokio-rustls" version = "0.24.1" @@ -7937,13 +8257,11 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", - "native-tls", "rustls 0.21.10", "tokio", - "tokio-native-tls", "tokio-rustls 0.24.1", "tungstenite", - "webpki-roots", + "webpki-roots 0.25.4", ] [[package]] @@ -8117,7 +8435,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -8211,7 +8529,6 @@ dependencies = [ "http 0.2.12", "httparse", "log", - "native-tls", "rand 0.8.5", "rustls 0.21.10", "sha1", @@ -8337,6 +8654,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" @@ -8359,6 +8682,12 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" + [[package]] name = "valuable" version = "0.1.0" @@ -8389,6 +8718,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -8450,7 +8785,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", "wasm-bindgen-shared", ] @@ -8484,7 +8819,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8554,20 +8889,19 @@ dependencies = [ ] [[package]] -name = "webpki" -version = "0.22.4" +name = "webpki-roots" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" -dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", -] +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "which" @@ -8885,10 +9219,10 @@ dependencies = [ ] [[package]] -name = "xml-rs" -version = "0.8.20" +name = "xmlparser" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "yansi" @@ -8919,7 +9253,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -8939,7 +9273,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -8955,7 +9289,7 @@ dependencies = [ "crc32fast", "crossbeam-utils", "flate2", - "hmac 0.12.1", + "hmac", "pbkdf2 0.11.0", "sha1", "time", diff --git a/Cargo.toml b/Cargo.toml index ad300a085c8f..1bc2329a5093 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,9 +107,6 @@ codegen-units = 1 [profile.release.package] mdbook.opt-level = 1 protobuf.opt-level = 1 -rusoto_core.opt-level = 1 -rusoto_credential.opt-level = 1 -rusoto_kms.opt-level = 1 toml_edit.opt-level = 1 trezor-client.opt-level = 1 @@ -140,16 +137,16 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.2.4", default-features = false } -foundry-compilers = { version = "0.3.13", default-features = false } +foundry-block-explorers = { version = "0.2.5", default-features = false } +foundry-compilers = { version = "0.3.14", default-features = false } ## revm # no default features to avoid c-kzg -revm = { version = "7.1", default-features = false, features = ["std"] } -revm-primitives = { version = "3", default-features = false, features = ["std"] } -revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "ba0b6ab", features = [ +revm = { version = "8", default-features = false } +revm-primitives = { version = "3", default-features = false } +revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "510d4d0", features = [ "serde", -] } +] } ## ethers ethers = { version = "2.0.14", default-features = false } @@ -161,30 +158,34 @@ ethers-signers = { version = "2.0.14", default-features = false } ethers-middleware = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-providers = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "9ac2c90", default-features = false } -alloy-primitives = { version = "0.6.3", features = ["getrandom"] } -alloy-dyn-abi = "0.6.3" -alloy-json-abi = "0.6.3" -alloy-sol-types = "0.6.3" -syn-solidity = "0.6.3" +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-signer-wallet = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-signer-aws = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-signer-ledger = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-signer-trezor = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "5f74d4e", default-features = false } +alloy-primitives = { version = "0.7.0", features = ["getrandom"] } +alloy-dyn-abi = "0.7.0" +alloy-json-abi = "0.7.0" +alloy-sol-types = "0.7.0" +syn-solidity = "0.7.0" alloy-chains = "0.1" -alloy-trie = "0.3" - +alloy-trie = "0.3.1" alloy-rlp = "0.3.3" solang-parser = "=0.3.3" diff --git a/clippy.toml b/clippy.toml index 472818efed9b..09acb653d14a 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1,4 @@ msrv = "1.76" +# bytes::Bytes is included by default and alloy_primitives::Bytes is a wrapper around it, +# so it is safe to ignore it as well +ignore-interior-mutability = ["bytes::Bytes", "alloy_primitives::Bytes"] diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index 4ccbfadddafb..99bf00081b2c 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -16,7 +16,11 @@ path = "src/anvil.rs" required-features = ["cli"] [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # foundry internal @@ -36,12 +40,13 @@ alloy-primitives = { workspace = true, features = ["serde"] } alloy-consensus = { workspace = true, features = ["k256", "kzg"] } alloy-network.workspace = true alloy-rlp.workspace = true -alloy-signer = { workspace = true, features = ["eip712", "mnemonic"] } +alloy-signer = { workspace = true, features = ["eip712"] } +alloy-signer-wallet = { workspace = true, features = ["mnemonic"] } alloy-sol-types = { workspace = true, features = ["std"] } alloy-dyn-abi = { workspace = true, features = ["std", "eip712"] } alloy-rpc-types.workspace = true -alloy-rpc-trace-types.workspace = true -alloy-providers.workspace = true +alloy-rpc-types-trace.workspace = true +alloy-provider = { workspace = true, features = ["pubsub"] } alloy-transport.workspace = true alloy-chains.workspace = true alloy-genesis.workspace = true @@ -75,7 +80,11 @@ rand = "0.8" eyre.workspace = true # cli -clap = { version = "4", features = ["derive", "env", "wrap_help"], optional = true } +clap = { version = "4", features = [ + "derive", + "env", + "wrap_help", +], optional = true } clap_complete = { version = "4", optional = true } chrono.workspace = true auto_impl = "1" @@ -89,7 +98,7 @@ tikv-jemallocator = { workspace = true, optional = true } [dev-dependencies] alloy-json-abi.workspace = true -ethers = { workspace = true, features = ["abigen"] } +ethers = { workspace = true, features = ["abigen", "optimism"] } ethers-core = { workspace = true, features = ["optimism"] } foundry-compilers = { workspace = true, features = ["project-util", "full"] } diff --git a/crates/anvil/core/Cargo.toml b/crates/anvil/core/Cargo.toml index 999c60689983..feddead95212 100644 --- a/crates/anvil/core/Cargo.toml +++ b/crates/anvil/core/Cargo.toml @@ -12,11 +12,15 @@ repository.workspace = true [dependencies] foundry-common.workspace = true foundry-evm.workspace = true -revm = { workspace = true, default-features = false, features = ["std", "serde", "memory_limit"] } +revm = { workspace = true, default-features = false, features = [ + "std", + "serde", + "memory_limit", +] } alloy-primitives = { workspace = true, features = ["serde"] } alloy-rpc-types = { workspace = true } -alloy-rpc-trace-types.workspace = true +alloy-rpc-types-trace.workspace = true alloy-rlp.workspace = true alloy-eips.workspace = true alloy-network = { workspace = true, features = ["k256"] } @@ -27,7 +31,7 @@ alloy-trie.workspace = true serde = { workspace = true, optional = true } serde_json.workspace = true bytes = "1.4" -c-kzg = { version = "0.4.2", features = ["serde"] } +c-kzg = { version = "1", features = ["serde"] } # misc rand = "0.8" diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index c304eefa4a10..462d16c8209c 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -3,7 +3,7 @@ use super::{ trie, }; use alloy_consensus::Header; -use alloy_primitives::{Address, Bloom, Bytes, B256, U256}; +use alloy_primitives::{Address, Bloom, Bytes, B256, B64, U256}; use alloy_rlp::{RlpDecodable, RlpEncodable}; // Type alias to optionally support impersonated transactions @@ -88,13 +88,13 @@ pub struct PartialHeader { pub logs_bloom: Bloom, pub difficulty: U256, pub number: u64, - pub gas_limit: u64, - pub gas_used: u64, + pub gas_limit: u128, + pub gas_used: u128, pub timestamp: u64, pub extra_data: Bytes, pub mix_hash: B256, - pub nonce: u64, - pub base_fee: Option, + pub nonce: B64, + pub base_fee: Option, } impl From
for PartialHeader { @@ -120,7 +120,6 @@ impl From
for PartialHeader { #[cfg(test)] mod tests { - use alloy_network::Sealable; use alloy_primitives::{ b256, hex::{self, FromHex}, @@ -143,11 +142,11 @@ mod tests { difficulty: Default::default(), number: 124u64, gas_limit: Default::default(), - gas_used: 1337u64, + gas_used: 1337u128, timestamp: 0, extra_data: Default::default(), mix_hash: Default::default(), - nonce: 99u64, + nonce: B64::with_last_byte(99), withdrawals_root: Default::default(), blob_gas_used: Default::default(), excess_blob_gas: Default::default(), @@ -159,7 +158,7 @@ mod tests { let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap(); assert_eq!(header, decoded); - header.base_fee_per_gas = Some(12345u64); + header.base_fee_per_gas = Some(12345u128); let encoded = alloy_rlp::encode(&header); let decoded: Header = Header::decode(&mut encoded.as_ref()).unwrap(); @@ -182,8 +181,8 @@ mod tests { logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), difficulty: U256::from(2222), number: 0xd05u64, - gas_limit: 0x115cu64, - gas_used: 0x15b3u64, + gas_limit: 0x115cu128, + gas_used: 0x15b3u128, timestamp: 0x1a0au64, extra_data: hex::decode("7788").unwrap().into(), mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), @@ -191,7 +190,7 @@ mod tests { blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, - nonce: 0, + nonce: B64::ZERO, base_fee_per_gas: None, }; @@ -214,12 +213,12 @@ mod tests { logs_bloom: <[u8; 256]>::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().into(), difficulty: U256::from(2222), number: 0xd05u64, - gas_limit: 0x115cu64, - gas_used: 0x15b3u64, + gas_limit: 0x115cu128, + gas_used: 0x15b3u128, timestamp: 0x1a0au64, extra_data: hex::decode("7788").unwrap().into(), mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - nonce: 0, + nonce: B64::ZERO, withdrawals_root: None, blob_gas_used: None, excess_blob_gas: None, @@ -245,19 +244,19 @@ mod tests { logs_bloom: Bloom::from_hex("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), difficulty: U256::from(0x020000), number: 1u64, - gas_limit: U256::from(0x016345785d8a0000u128).to::(), - gas_used: U256::from(0x015534).to::(), + gas_limit: U256::from(0x016345785d8a0000u128).to::(), + gas_used: U256::from(0x015534).to::(), timestamp: 0x079e, extra_data: hex::decode("42").unwrap().into(), mix_hash: B256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(), - nonce: 0, + nonce: B64::ZERO, base_fee_per_gas: Some(875), withdrawals_root: None, blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, }; - assert_eq!(header.hash(), expected_hash); + assert_eq!(header.hash_slow(), expected_hash); } #[test] diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index e5a394a8fa4c..7a2a153e8ed8 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -3,13 +3,13 @@ use crate::{ types::{EvmMineOptions, Forking, Index}, }; use alloy_primitives::{Address, Bytes, TxHash, B256, B64, U256}; -use alloy_rpc_trace_types::geth::{GethDebugTracingOptions, GethDefaultTracingOptions}; use alloy_rpc_types::{ pubsub::{Params as SubscriptionParams, SubscriptionKind}, request::TransactionRequest, state::StateOverride, - BlockId, BlockNumberOrTag as BlockNumber, Filter, + BlockId, BlockNumberOrTag as BlockNumber, Filter, WithOtherFields, }; +use alloy_rpc_types_trace::geth::{GethDebugTracingOptions, GethDefaultTracingOptions}; pub mod block; pub mod proof; @@ -143,7 +143,7 @@ pub enum EthRequest { EthSign(Address, Bytes), #[cfg_attr(feature = "serde", serde(rename = "eth_signTransaction"))] - EthSignTransaction(Box), + EthSignTransaction(Box>), /// Signs data via [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md). #[cfg_attr(feature = "serde", serde(rename = "eth_signTypedData"))] @@ -158,27 +158,27 @@ pub enum EthRequest { EthSignTypedDataV4(Address, alloy_dyn_abi::TypedData), #[cfg_attr(feature = "serde", serde(rename = "eth_sendTransaction", with = "sequence"))] - EthSendTransaction(Box), + EthSendTransaction(Box>), #[cfg_attr(feature = "serde", serde(rename = "eth_sendRawTransaction", with = "sequence"))] EthSendRawTransaction(Bytes), #[cfg_attr(feature = "serde", serde(rename = "eth_call"))] EthCall( - TransactionRequest, + WithOtherFields, #[cfg_attr(feature = "serde", serde(default))] Option, #[cfg_attr(feature = "serde", serde(default))] Option, ), #[cfg_attr(feature = "serde", serde(rename = "eth_createAccessList"))] EthCreateAccessList( - TransactionRequest, + WithOtherFields, #[cfg_attr(feature = "serde", serde(default))] Option, ), #[cfg_attr(feature = "serde", serde(rename = "eth_estimateGas"))] EthEstimateGas( - TransactionRequest, + WithOtherFields, #[cfg_attr(feature = "serde", serde(default))] Option, #[cfg_attr(feature = "serde", serde(default))] Option, ), @@ -272,7 +272,7 @@ pub enum EthRequest { /// geth's `debug_traceCall` endpoint #[cfg_attr(feature = "serde", serde(rename = "debug_traceCall"))] DebugTraceCall( - TransactionRequest, + WithOtherFields, #[cfg_attr(feature = "serde", serde(default))] Option, #[cfg_attr(feature = "serde", serde(default))] GethDefaultTracingOptions, ), @@ -597,7 +597,7 @@ pub enum EthRequest { feature = "serde", serde(rename = "eth_sendUnsignedTransaction", with = "sequence") )] - EthSendUnsignedTransaction(Box), + EthSendUnsignedTransaction(Box>), /// Turn on call traces for transactions that are returned to the user when they execute a /// transaction (instead of just txhash/receipt) diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 10de5f80e7f0..84e236f92f8b 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -5,21 +5,24 @@ use crate::eth::{ utils::eip_to_revm_access_list, }; use alloy_consensus::{ - BlobTransactionSidecar, ReceiptWithBloom, TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, - TxEip4844WithSidecar, TxLegacy, + AnyReceiptEnvelope, BlobTransactionSidecar, Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, + TxEip1559, TxEip2930, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope, TxLegacy, + TxReceipt, }; -use alloy_network::{Signed, Transaction, TxKind}; -use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, B256, U128, U256, U64}; -use alloy_rlp::{Decodable, Encodable}; +use alloy_eips::eip2718::Decodable2718; +use alloy_primitives::{Address, Bloom, Bytes, Log, Signature, TxHash, TxKind, B256, U256}; +use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use alloy_rpc_types::{ - request::TransactionRequest, AccessList, Signature as RpcSignature, - Transaction as RpcTransaction, + request::TransactionRequest, AccessList, AnyTransactionReceipt, Signature as RpcSignature, + Transaction as RpcTransaction, TransactionReceipt, WithOtherFields, }; +use bytes::BufMut; use foundry_evm::traces::CallTraceNode; use revm::{ interpreter::InstructionResult, primitives::{CreateScheme, OptimismFields, TransactTo, TxEnv}, }; +use serde::{Deserialize, Serialize}; use std::ops::Deref; use super::utils::from_eip_to_alloy_access_list; @@ -35,26 +38,30 @@ pub fn impersonated_signature() -> Signature { /// Converts a [TransactionRequest] into a [TypedTransactionRequest]. /// Should be removed once the call builder abstraction for providers is in place. -pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option { - let TransactionRequest { - from, - to, - gas_price, - max_fee_per_gas, - max_priority_fee_per_gas, - max_fee_per_blob_gas, - mut blob_versioned_hashes, - gas, - value, - input, - nonce, - mut access_list, - sidecar, - transaction_type, +pub fn transaction_request_to_typed( + tx: WithOtherFields, +) -> Option { + let WithOtherFields:: { + inner: + TransactionRequest { + from, + to, + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + max_fee_per_blob_gas, + mut blob_versioned_hashes, + gas, + value, + input, + nonce, + mut access_list, + sidecar, + transaction_type, + .. + }, other, - .. } = tx; - let transaction_type = transaction_type.map(|id| id.to::()); // Special case: OP-stack deposit tx if transaction_type == Some(126) { @@ -79,14 +86,15 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option { + (Some(0), _, None, None, None, None, None, None, _) | + (None, Some(_), None, None, None, None, None, None, _) => { Some(TypedTransactionRequest::Legacy(TxLegacy { - nonce: nonce.unwrap_or_default().to::(), - gas_price: gas_price.unwrap_or_default().to::(), - gas_limit: gas.unwrap_or_default().to::(), + nonce: nonce.unwrap_or_default(), + gas_price: gas_price.unwrap_or_default(), + gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), to: match to { @@ -97,12 +105,12 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option { + (Some(1), _, None, None, _, None, None, None, _) | + (None, _, None, None, Some(_), None, None, None, _) => { Some(TypedTransactionRequest::EIP2930(TxEip2930 { - nonce: nonce.unwrap_or_default().to::(), - gas_price: gas_price.unwrap_or_default().to(), - gas_limit: gas.unwrap_or_default().to::(), + nonce: nonce.unwrap_or_default(), + gas_price: gas_price.unwrap_or_default(), + gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), to: match to { @@ -110,20 +118,20 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option TxKind::Create, }, chain_id: 0, - access_list: to_eip_access_list(access_list.unwrap_or_default()), + access_list: access_list.unwrap_or_default(), })) } // EIP1559 - (Some(2), None, _, _, _, _, None, None) | - (None, None, Some(_), _, _, _, None, None) | - (None, None, _, Some(_), _, _, None, None) | - (None, None, None, None, None, _, None, None) => { + (Some(2), None, _, _, _, _, None, None, _) | + (None, None, Some(_), _, _, _, None, None, _) | + (None, None, _, Some(_), _, _, None, None, _) | + (None, None, None, None, None, _, None, None, _) => { // Empty fields fall back to the canonical transaction schema. Some(TypedTransactionRequest::EIP1559(TxEip1559 { - nonce: nonce.unwrap_or_default().to::(), - max_fee_per_gas: max_fee_per_gas.unwrap_or_default().to::(), - max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default().to::(), - gas_limit: gas.unwrap_or_default().to::(), + nonce: nonce.unwrap_or_default(), + max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), + max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default(), + gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), to: match to { @@ -131,25 +139,22 @@ pub fn transaction_request_to_typed(tx: TransactionRequest) -> Option TxKind::Create, }, chain_id: 0, - access_list: to_eip_access_list(access_list.unwrap_or_default()), + access_list: access_list.unwrap_or_default(), })) } // EIP4844 - (Some(3), None, _, _, _, Some(_), Some(_), Some(sidecar)) => { + (Some(3), None, _, _, _, Some(_), Some(_), Some(sidecar), Some(to)) => { let tx = TxEip4844 { - nonce: nonce.unwrap_or_default().to::(), - max_fee_per_gas: max_fee_per_gas.unwrap_or_default().to::(), - max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default().to::(), - max_fee_per_blob_gas: max_fee_per_blob_gas.unwrap_or_default().to::(), - gas_limit: gas.unwrap_or_default().to::(), + nonce: nonce.unwrap_or_default(), + max_fee_per_gas: max_fee_per_gas.unwrap_or_default(), + max_priority_fee_per_gas: max_priority_fee_per_gas.unwrap_or_default(), + max_fee_per_blob_gas: max_fee_per_blob_gas.unwrap_or_default(), + gas_limit: gas.unwrap_or_default(), value: value.unwrap_or(U256::ZERO), input: input.into_input().unwrap_or_default(), - to: match to { - Some(to) => TxKind::Call(to), - None => TxKind::Create, - }, + to, chain_id: 0, - access_list: to_eip_access_list(access_list.unwrap_or_default()), + access_list: access_list.unwrap_or_default(), blob_versioned_hashes: blob_versioned_hashes.unwrap_or_default(), }; let blob_sidecar = BlobTransactionSidecar { @@ -288,19 +293,19 @@ pub fn to_alloy_transaction_with_hash_and_sender( match transaction { TypedTransaction::Legacy(t) => RpcTransaction { hash, - nonce: U64::from(t.nonce), + nonce: t.tx().nonce, block_hash: None, block_number: None, transaction_index: None, from, to: None, - value: t.value, - gas_price: Some(U128::from(t.gas_price)), - max_fee_per_gas: Some(U128::from(t.gas_price)), - max_priority_fee_per_gas: Some(U128::from(t.gas_price)), - gas: U256::from(t.gas_limit), - input: t.input.clone(), - chain_id: t.chain_id.map(U64::from), + value: t.tx().value, + gas_price: Some(t.tx().gas_price), + max_fee_per_gas: Some(t.tx().gas_price), + max_priority_fee_per_gas: Some(t.tx().gas_price), + gas: t.tx().gas_limit, + input: t.tx().input.clone(), + chain_id: t.tx().chain_id, signature: Some(RpcSignature { r: t.signature().r(), s: t.signature().s(), @@ -310,93 +315,93 @@ pub fn to_alloy_transaction_with_hash_and_sender( access_list: None, transaction_type: None, max_fee_per_blob_gas: None, - blob_versioned_hashes: vec![], + blob_versioned_hashes: None, other: Default::default(), }, TypedTransaction::EIP2930(t) => RpcTransaction { hash, - nonce: U64::from(t.nonce), + nonce: t.tx().nonce, block_hash: None, block_number: None, transaction_index: None, from, to: None, - value: t.value, - gas_price: Some(U128::from(t.gas_price)), - max_fee_per_gas: Some(U128::from(t.gas_price)), - max_priority_fee_per_gas: Some(U128::from(t.gas_price)), - gas: U256::from(t.gas_limit), - input: t.input.clone(), - chain_id: Some(U64::from(t.chain_id)), + value: t.tx().value, + gas_price: Some(t.tx().gas_price), + max_fee_per_gas: Some(t.tx().gas_price), + max_priority_fee_per_gas: Some(t.tx().gas_price), + gas: t.tx().gas_limit, + input: t.tx().input.clone(), + chain_id: Some(t.tx().chain_id), signature: Some(RpcSignature { r: t.signature().r(), s: t.signature().s(), v: U256::from(t.signature().v().y_parity_byte()), y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), }), - access_list: Some(from_eip_to_alloy_access_list(t.access_list.clone()).0), - transaction_type: Some(U64::from(1)), + access_list: Some(t.tx().access_list.clone()), + transaction_type: Some(1), max_fee_per_blob_gas: None, - blob_versioned_hashes: vec![], + blob_versioned_hashes: None, other: Default::default(), }, TypedTransaction::EIP1559(t) => RpcTransaction { hash, - nonce: U64::from(t.nonce), + nonce: t.tx().nonce, block_hash: None, block_number: None, transaction_index: None, from, to: None, - value: t.value, + value: t.tx().value, gas_price: None, - max_fee_per_gas: Some(U128::from(t.max_fee_per_gas)), - max_priority_fee_per_gas: Some(U128::from(t.max_priority_fee_per_gas)), - gas: U256::from(t.gas_limit), - input: t.input.clone(), - chain_id: Some(U64::from(t.chain_id)), + max_fee_per_gas: Some(t.tx().max_fee_per_gas), + max_priority_fee_per_gas: Some(t.tx().max_priority_fee_per_gas), + gas: t.tx().gas_limit, + input: t.tx().input.clone(), + chain_id: Some(t.tx().chain_id), signature: Some(RpcSignature { r: t.signature().r(), s: t.signature().s(), v: U256::from(t.signature().v().y_parity_byte()), y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), }), - access_list: Some(from_eip_to_alloy_access_list(t.access_list.clone()).0), - transaction_type: Some(U64::from(2)), + access_list: Some(t.tx().access_list.clone()), + transaction_type: Some(2), max_fee_per_blob_gas: None, - blob_versioned_hashes: vec![], + blob_versioned_hashes: None, other: Default::default(), }, TypedTransaction::EIP4844(t) => RpcTransaction { hash, - nonce: U64::from(t.tx().tx().nonce), + nonce: t.tx().tx().nonce, block_hash: None, block_number: None, transaction_index: None, from, to: None, value: t.tx().tx().value, - gas_price: Some(U128::from(t.tx().tx().max_fee_per_gas)), - max_fee_per_gas: Some(U128::from(t.tx().tx().max_fee_per_gas)), - max_priority_fee_per_gas: Some(U128::from(t.tx().tx().max_priority_fee_per_gas)), - gas: U256::from(t.tx().tx().gas_limit), + gas_price: Some(t.tx().tx().max_fee_per_gas), + max_fee_per_gas: Some(t.tx().tx().max_fee_per_gas), + max_priority_fee_per_gas: Some(t.tx().tx().max_priority_fee_per_gas), + gas: t.tx().tx().gas_limit, input: t.tx().tx().input.clone(), - chain_id: Some(U64::from(t.tx().tx().chain_id)), + chain_id: Some(t.tx().tx().chain_id), signature: Some(RpcSignature { r: t.signature().r(), s: t.signature().s(), v: U256::from(t.signature().v().y_parity_byte()), y_parity: Some(alloy_rpc_types::Parity::from(t.signature().v().y_parity())), }), - access_list: Some(from_eip_to_alloy_access_list(t.tx().tx().access_list.clone()).0), - transaction_type: Some(U64::from(3)), - max_fee_per_blob_gas: Some(U128::from(t.tx().tx().max_fee_per_blob_gas)), - blob_versioned_hashes: t.tx().tx().blob_versioned_hashes.clone(), + access_list: Some(from_eip_to_alloy_access_list(t.tx().tx().access_list.clone())), + transaction_type: Some(3), + max_fee_per_blob_gas: Some(t.tx().tx().max_fee_per_blob_gas), + blob_versioned_hashes: Some(t.tx().tx().blob_versioned_hashes.clone()), other: Default::default(), }, TypedTransaction::Deposit(t) => RpcTransaction { hash, - nonce: U64::from(t.nonce), + nonce: t.nonce, block_hash: None, block_number: None, transaction_index: None, @@ -406,14 +411,14 @@ pub fn to_alloy_transaction_with_hash_and_sender( gas_price: None, max_fee_per_gas: None, max_priority_fee_per_gas: None, - gas: U256::from(t.gas_limit), + gas: t.gas_limit, input: t.input.clone().0.into(), - chain_id: t.chain_id().map(U64::from), + chain_id: t.chain_id().map(u64::from), signature: None, access_list: None, transaction_type: None, max_fee_per_blob_gas: None, - blob_versioned_hashes: vec![], + blob_versioned_hashes: None, other: Default::default(), }, } @@ -447,7 +452,7 @@ impl PendingTransaction { } } - pub fn nonce(&self) -> U256 { + pub fn nonce(&self) -> u64 { self.transaction.nonce() } @@ -472,7 +477,7 @@ impl PendingTransaction { let caller = *self.sender(); match &self.transaction.transaction { TypedTransaction::Legacy(tx) => { - let chain_id = tx.chain_id(); + let chain_id = tx.tx().chain_id; let TxLegacy { nonce, gas_price, gas_limit, value, to, input, .. } = tx.tx(); TxEnv { caller, @@ -483,7 +488,7 @@ impl PendingTransaction { value: (*value), gas_price: U256::from(*gas_price), gas_priority_fee: None, - gas_limit: *gas_limit, + gas_limit: *gas_limit as u64, access_list: vec![], ..Default::default() } @@ -509,7 +514,7 @@ impl PendingTransaction { value: *value, gas_price: U256::from(*gas_price), gas_priority_fee: None, - gas_limit: *gas_limit, + gas_limit: *gas_limit as u64, access_list: eip_to_revm_access_list(access_list.0.clone()), ..Default::default() } @@ -536,7 +541,7 @@ impl PendingTransaction { value: *value, gas_price: U256::from(*max_fee_per_gas), gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)), - gas_limit: *gas_limit, + gas_limit: *gas_limit as u64, access_list: eip_to_revm_access_list(access_list.0.clone()), ..Default::default() } @@ -558,7 +563,7 @@ impl PendingTransaction { } = tx.tx().tx(); TxEnv { caller, - transact_to: transact_to(to), + transact_to: TransactTo::call(*to), data: alloy_primitives::Bytes(input.0.clone()), chain_id: Some(*chain_id), nonce: Some(*nonce), @@ -567,7 +572,7 @@ impl PendingTransaction { gas_priority_fee: Some(U256::from(*max_priority_fee_per_gas)), max_fee_per_blob_gas: Some(U256::from(*max_fee_per_blob_gas)), blob_hashes: blob_versioned_hashes.clone(), - gas_limit: *gas_limit, + gas_limit: *gas_limit as u64, access_list: eip_to_revm_access_list(access_list.0.clone()), ..Default::default() } @@ -590,11 +595,11 @@ impl PendingTransaction { transact_to: transact_to(kind), data: alloy_primitives::Bytes(input.0.clone()), chain_id, - nonce: Some(nonce.to::()), + nonce: Some(*nonce), value: *value, gas_price: U256::ZERO, gas_priority_fee: None, - gas_limit: gas_limit.to::(), + gas_limit: *gas_limit as u64, access_list: vec![], optimism: OptimismFields { source_hash: Some(*source_hash), @@ -630,31 +635,31 @@ impl TypedTransaction { matches!(self, TypedTransaction::EIP1559(_)) } - pub fn gas_price(&self) -> U256 { - U256::from(match self { - TypedTransaction::Legacy(tx) => tx.gas_price, - TypedTransaction::EIP2930(tx) => tx.gas_price, - TypedTransaction::EIP1559(tx) => tx.max_fee_per_gas, + pub fn gas_price(&self) -> u128 { + match self { + TypedTransaction::Legacy(tx) => tx.tx().gas_price, + TypedTransaction::EIP2930(tx) => tx.tx().gas_price, + TypedTransaction::EIP1559(tx) => tx.tx().max_fee_per_gas, TypedTransaction::EIP4844(tx) => tx.tx().tx().max_fee_per_blob_gas, TypedTransaction::Deposit(_) => 0, - }) + } } - pub fn gas_limit(&self) -> U256 { - U256::from(match self { - TypedTransaction::Legacy(tx) => tx.gas_limit, - TypedTransaction::EIP2930(tx) => tx.gas_limit, - TypedTransaction::EIP1559(tx) => tx.gas_limit, + pub fn gas_limit(&self) -> u128 { + match self { + TypedTransaction::Legacy(tx) => tx.tx().gas_limit, + TypedTransaction::EIP2930(tx) => tx.tx().gas_limit, + TypedTransaction::EIP1559(tx) => tx.tx().gas_limit, TypedTransaction::EIP4844(tx) => tx.tx().tx().gas_limit, - TypedTransaction::Deposit(tx) => tx.gas_limit.to::(), - }) + TypedTransaction::Deposit(tx) => tx.gas_limit, + } } pub fn value(&self) -> U256 { U256::from(match self { - TypedTransaction::Legacy(tx) => tx.value, - TypedTransaction::EIP2930(tx) => tx.value, - TypedTransaction::EIP1559(tx) => tx.value, + TypedTransaction::Legacy(tx) => tx.tx().value, + TypedTransaction::EIP2930(tx) => tx.tx().value, + TypedTransaction::EIP1559(tx) => tx.tx().value, TypedTransaction::EIP4844(tx) => tx.tx().tx().value, TypedTransaction::Deposit(tx) => tx.value, }) @@ -662,9 +667,9 @@ impl TypedTransaction { pub fn data(&self) -> &Bytes { match self { - TypedTransaction::Legacy(tx) => &tx.input, - TypedTransaction::EIP2930(tx) => &tx.input, - TypedTransaction::EIP1559(tx) => &tx.input, + TypedTransaction::Legacy(tx) => &tx.tx().input, + TypedTransaction::EIP2930(tx) => &tx.tx().input, + TypedTransaction::EIP1559(tx) => &tx.tx().input, TypedTransaction::EIP4844(tx) => &tx.tx().tx().input, TypedTransaction::Deposit(tx) => &tx.input, } @@ -682,7 +687,7 @@ impl TypedTransaction { } /// Max cost of the transaction - pub fn max_cost(&self) -> U256 { + pub fn max_cost(&self) -> u128 { self.gas_limit().saturating_mul(self.gas_price()) } @@ -691,51 +696,51 @@ impl TypedTransaction { match self { TypedTransaction::Legacy(t) => TransactionEssentials { kind: t.tx().to, - input: t.input.clone(), - nonce: U256::from(t.tx().nonce), - gas_limit: U256::from(t.tx().gas_limit), + input: t.tx().input.clone(), + nonce: t.tx().nonce, + gas_limit: t.tx().gas_limit, gas_price: Some(U256::from(t.tx().gas_price)), max_fee_per_gas: None, max_priority_fee_per_gas: None, max_fee_per_blob_gas: None, blob_versioned_hashes: None, - value: t.value, + value: t.tx().value, chain_id: t.tx().chain_id, access_list: Default::default(), }, TypedTransaction::EIP2930(t) => TransactionEssentials { kind: t.tx().to, - input: t.input.clone(), - nonce: U256::from(t.tx().nonce), - gas_limit: U256::from(t.tx().gas_limit), + input: t.tx().input.clone(), + nonce: t.tx().nonce, + gas_limit: t.tx().gas_limit, gas_price: Some(U256::from(t.tx().gas_price)), max_fee_per_gas: None, max_priority_fee_per_gas: None, max_fee_per_blob_gas: None, blob_versioned_hashes: None, - value: t.value, - chain_id: Some(t.chain_id), - access_list: to_alloy_access_list(t.access_list.clone()), + value: t.tx().value, + chain_id: Some(t.tx().chain_id), + access_list: t.tx().access_list.clone(), }, TypedTransaction::EIP1559(t) => TransactionEssentials { - kind: t.to, - input: t.input.clone(), - nonce: U256::from(t.nonce), - gas_limit: U256::from(t.gas_limit), + kind: t.tx().to, + input: t.tx().input.clone(), + nonce: t.tx().nonce, + gas_limit: t.tx().gas_limit, gas_price: None, - max_fee_per_gas: Some(U256::from(t.max_fee_per_gas)), - max_priority_fee_per_gas: Some(U256::from(t.max_priority_fee_per_gas)), + max_fee_per_gas: Some(U256::from(t.tx().max_fee_per_gas)), + max_priority_fee_per_gas: Some(U256::from(t.tx().max_priority_fee_per_gas)), max_fee_per_blob_gas: None, blob_versioned_hashes: None, - value: t.value, - chain_id: Some(t.chain_id), - access_list: to_alloy_access_list(t.access_list.clone()), + value: t.tx().value, + chain_id: Some(t.tx().chain_id), + access_list: t.tx().access_list.clone(), }, TypedTransaction::EIP4844(t) => TransactionEssentials { - kind: t.tx().tx().to, + kind: TxKind::Call(t.tx().tx().to), input: t.tx().tx().input.clone(), - nonce: U256::from(t.tx().tx().nonce), - gas_limit: U256::from(t.tx().tx().gas_limit), + nonce: t.tx().tx().nonce, + gas_limit: t.tx().tx().gas_limit, gas_price: Some(U256::from(t.tx().tx().max_fee_per_blob_gas)), max_fee_per_gas: Some(U256::from(t.tx().tx().max_fee_per_gas)), max_priority_fee_per_gas: Some(U256::from(t.tx().tx().max_priority_fee_per_gas)), @@ -743,7 +748,7 @@ impl TypedTransaction { blob_versioned_hashes: Some(t.tx().tx().blob_versioned_hashes.clone()), value: t.tx().tx().value, chain_id: Some(t.tx().tx().chain_id), - access_list: to_alloy_access_list(t.tx().tx().access_list.clone()), + access_list: t.tx().tx().access_list.clone(), }, TypedTransaction::Deposit(t) => TransactionEssentials { kind: t.kind, @@ -762,21 +767,21 @@ impl TypedTransaction { } } - pub fn nonce(&self) -> U256 { + pub fn nonce(&self) -> u64 { match self { - TypedTransaction::Legacy(t) => U256::from(t.nonce), - TypedTransaction::EIP2930(t) => U256::from(t.nonce), - TypedTransaction::EIP1559(t) => U256::from(t.nonce), - TypedTransaction::EIP4844(t) => U256::from(t.tx().tx().nonce), - TypedTransaction::Deposit(t) => U256::from(t.nonce), + TypedTransaction::Legacy(t) => t.tx().nonce, + TypedTransaction::EIP2930(t) => t.tx().nonce, + TypedTransaction::EIP1559(t) => t.tx().nonce, + TypedTransaction::EIP4844(t) => t.tx().tx().nonce, + TypedTransaction::Deposit(t) => t.nonce, } } pub fn chain_id(&self) -> Option { match self { - TypedTransaction::Legacy(t) => t.chain_id, - TypedTransaction::EIP2930(t) => Some(t.chain_id), - TypedTransaction::EIP1559(t) => Some(t.chain_id), + TypedTransaction::Legacy(t) => t.tx().chain_id, + TypedTransaction::EIP2930(t) => Some(t.tx().chain_id), + TypedTransaction::EIP1559(t) => Some(t.tx().chain_id), TypedTransaction::EIP4844(t) => Some(t.tx().tx().chain_id), TypedTransaction::Deposit(t) => t.chain_id(), } @@ -852,19 +857,19 @@ impl TypedTransaction { } /// Returns what kind of transaction this is - pub fn kind(&self) -> &TxKind { + pub fn kind(&self) -> TxKind { match self { - TypedTransaction::Legacy(tx) => &tx.to, - TypedTransaction::EIP2930(tx) => &tx.to, - TypedTransaction::EIP1559(tx) => &tx.to, - TypedTransaction::EIP4844(tx) => &tx.tx().tx().to, - TypedTransaction::Deposit(tx) => &tx.kind, + TypedTransaction::Legacy(tx) => tx.tx().to, + TypedTransaction::EIP2930(tx) => tx.tx().to, + TypedTransaction::EIP1559(tx) => tx.tx().to, + TypedTransaction::EIP4844(tx) => TxKind::Call(tx.tx().tx().to), + TypedTransaction::Deposit(tx) => tx.kind, } } /// Returns the callee if this transaction is a call pub fn to(&self) -> Option
{ - self.kind().to() + self.kind().to().copied() } /// Returns the Signature of the transaction @@ -887,65 +892,72 @@ impl TypedTransaction { impl Encodable for TypedTransaction { fn encode(&self, out: &mut dyn bytes::BufMut) { match self { - TypedTransaction::Legacy(tx) => tx.encode(out), - TypedTransaction::EIP2930(tx) => tx.encode(out), - TypedTransaction::EIP1559(tx) => tx.encode(out), - TypedTransaction::EIP4844(tx) => tx.encode(out), - TypedTransaction::Deposit(tx) => tx.encode(out), + TypedTransaction::Legacy(tx) => TxEnvelope::from(tx.clone()).encode(out), + TypedTransaction::EIP2930(tx) => TxEnvelope::from(tx.clone()).encode(out), + TypedTransaction::EIP1559(tx) => TxEnvelope::from(tx.clone()).encode(out), + TypedTransaction::EIP4844(tx) => TxEnvelope::from(tx.clone()).encode(out), + TypedTransaction::Deposit(tx) => { + let tx_payload_len = tx.fields_len(); + let tx_header_len = Header { list: false, payload_length: tx_payload_len }.length(); + Header { list: false, payload_length: 1 + tx_payload_len + tx_header_len } + .encode(out); + out.put_u8(0x7E); + tx.encode(out); + } } } } impl Decodable for TypedTransaction { fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - use bytes::Buf; - use std::cmp::Ordering; + let mut h_decode_copy = *buf; + let header = alloy_rlp::Header::decode(&mut h_decode_copy)?; - let first = *buf.first().ok_or(alloy_rlp::Error::Custom("empty slice"))?; + // Legacy TX + if header.list { + return Ok(TxEnvelope::decode(buf)?.into()) + } - // a signed transaction is either encoded as a string (non legacy) or a list (legacy). - // We should not consume the buffer if we are decoding a legacy transaction, so let's - // check if the first byte is between 0x80 and 0xbf. - match first.cmp(&alloy_rlp::EMPTY_LIST_CODE) { - Ordering::Less => { - // strip out the string header - // NOTE: typed transaction encodings either contain a "rlp header" which contains - // the type of the payload and its length, or they do not contain a header and - // start with the tx type byte. - // - // This line works for both types of encodings because byte slices starting with - // 0x01 and 0x02 return a Header { list: false, payload_length: 1 } when input to - // Header::decode. - // If the encoding includes a header, the header will be properly decoded and - // consumed. - // Otherwise, header decoding will succeed but nothing is consumed. - let _header = alloy_rlp::Header::decode(buf)?; - let tx_type = *buf.first().ok_or(alloy_rlp::Error::Custom( - "typed tx cannot be decoded from an empty slice", - ))?; - if tx_type == 0x01 { - buf.advance(1); - as Decodable>::decode(buf).map(TypedTransaction::EIP2930) - } else if tx_type == 0x02 { - buf.advance(1); - as Decodable>::decode(buf).map(TypedTransaction::EIP1559) - } else if tx_type == 0x03 { - buf.advance(1); - as Decodable>::decode(buf) - .map(TypedTransaction::EIP4844) - } else if tx_type == 0x7E { - buf.advance(1); - ::decode(buf).map(TypedTransaction::Deposit) - } else { - Err(alloy_rlp::Error::Custom("invalid tx type")) - } - } - Ordering::Equal => { - Err(alloy_rlp::Error::Custom("an empty list is not a valid transaction encoding")) - } - Ordering::Greater => { - as Decodable>::decode(buf).map(TypedTransaction::Legacy) - } + // Check byte after header + let ty = *h_decode_copy.first().ok_or(alloy_rlp::Error::Custom("empty slice"))?; + + if ty != 0x7E { + Ok(TxEnvelope::decode(buf)?.into()) + } else { + Ok(Self::Deposit(DepositTransaction::decode(&mut h_decode_copy)?)) + } + } +} + +impl Decodable2718 for TypedTransaction { + fn typed_decode(ty: u8, buf: &mut &[u8]) -> alloy_rlp::Result { + if ty == 0x7E { + return Ok(Self::Deposit(DepositTransaction::decode(buf)?)) + } + match TxEnvelope::typed_decode(ty, buf)? { + TxEnvelope::Eip2930(tx) => Ok(Self::EIP2930(tx)), + TxEnvelope::Eip1559(tx) => Ok(Self::EIP1559(tx)), + TxEnvelope::Eip4844(tx) => Ok(Self::EIP4844(tx)), + _ => unreachable!(), + } + } + + fn fallback_decode(buf: &mut &[u8]) -> alloy_rlp::Result { + match TxEnvelope::fallback_decode(buf)? { + TxEnvelope::Legacy(tx) => Ok(Self::Legacy(tx)), + _ => unreachable!(), + } + } +} + +impl From for TypedTransaction { + fn from(value: TxEnvelope) -> Self { + match value { + TxEnvelope::Legacy(tx) => TypedTransaction::Legacy(tx), + TxEnvelope::Eip2930(tx) => TypedTransaction::EIP2930(tx), + TxEnvelope::Eip1559(tx) => TypedTransaction::EIP1559(tx), + TxEnvelope::Eip4844(tx) => TypedTransaction::EIP4844(tx), + _ => unreachable!(), } } } @@ -954,8 +966,8 @@ impl Decodable for TypedTransaction { pub struct TransactionEssentials { pub kind: TxKind, pub input: Bytes, - pub nonce: U256, - pub gas_limit: U256, + pub nonce: u64, + pub gas_limit: u128, pub gas_price: Option, pub max_fee_per_gas: Option, pub max_priority_fee_per_gas: Option, @@ -970,75 +982,168 @@ pub struct TransactionEssentials { #[derive(Clone, Debug, PartialEq, Eq)] pub struct TransactionInfo { pub transaction_hash: B256, - pub transaction_index: u32, + pub transaction_index: u64, pub from: Address, pub to: Option
, pub contract_address: Option
, - pub logs: Vec, - pub logs_bloom: Bloom, pub traces: Vec, pub exit: InstructionResult, pub out: Option, pub nonce: u64, + pub gas_used: u128, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum TypedReceipt { - Legacy(ReceiptWithBloom), - EIP2930(ReceiptWithBloom), - EIP1559(ReceiptWithBloom), - EIP4844(ReceiptWithBloom), - Deposit(ReceiptWithBloom), +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct DepositReceipt { + #[serde(flatten)] + pub inner: ReceiptWithBloom, + pub deposit_nonce: Option, + pub deposit_nonce_version: Option, } -impl TypedReceipt { - pub fn gas_used(&self) -> U256 { - match self { - TypedReceipt::Legacy(r) | - TypedReceipt::EIP1559(r) | - TypedReceipt::EIP2930(r) | - TypedReceipt::EIP4844(r) | - TypedReceipt::Deposit(r) => U256::from(r.receipt.cumulative_gas_used), +impl DepositReceipt { + fn payload_len(&self) -> usize { + self.inner.receipt.status.length() + + self.inner.receipt.cumulative_gas_used.length() + + self.inner.logs_bloom.length() + + self.inner.receipt.logs.length() + + self.deposit_nonce.map_or(0, |n| n.length()) + + self.deposit_nonce_version.map_or(0, |n| n.length()) + } + + /// Returns the rlp header for the receipt payload. + fn receipt_rlp_header(&self) -> alloy_rlp::Header { + alloy_rlp::Header { list: true, payload_length: self.payload_len() } + } + + /// Encodes the receipt data. + fn encode_fields(&self, out: &mut dyn BufMut) { + self.receipt_rlp_header().encode(out); + self.inner.receipt.status.encode(out); + self.inner.receipt.cumulative_gas_used.encode(out); + self.inner.logs_bloom.encode(out); + self.inner.receipt.logs.encode(out); + if let Some(n) = self.deposit_nonce { + n.encode(out); + } + if let Some(n) = self.deposit_nonce_version { + n.encode(out); } } - pub fn logs_bloom(&self) -> &Bloom { - match self { - TypedReceipt::Legacy(r) | - TypedReceipt::EIP1559(r) | - TypedReceipt::EIP2930(r) | - TypedReceipt::EIP4844(r) | - TypedReceipt::Deposit(r) => &r.bloom, + /// Decodes the receipt payload + fn decode_receipt(buf: &mut &[u8]) -> alloy_rlp::Result { + let b: &mut &[u8] = &mut &**buf; + let rlp_head = alloy_rlp::Header::decode(b)?; + if !rlp_head.list { + return Err(alloy_rlp::Error::UnexpectedString); + } + let started_len = b.len(); + let remaining = |b: &[u8]| rlp_head.payload_length - (started_len - b.len()) > 0; + + let status = Decodable::decode(b)?; + let cumulative_gas_used = Decodable::decode(b)?; + let logs_bloom = Decodable::decode(b)?; + let logs = Decodable::decode(b)?; + let deposit_nonce = remaining(b).then(|| alloy_rlp::Decodable::decode(b)).transpose()?; + let deposit_nonce_version = + remaining(b).then(|| alloy_rlp::Decodable::decode(b)).transpose()?; + + let this = Self { + inner: ReceiptWithBloom { + receipt: Receipt { status, cumulative_gas_used, logs }, + logs_bloom, + }, + deposit_nonce, + deposit_nonce_version, + }; + + let consumed = started_len - b.len(); + if consumed != rlp_head.payload_length { + return Err(alloy_rlp::Error::ListLengthMismatch { + expected: rlp_head.payload_length, + got: consumed, + }); } + + *buf = *b; + Ok(this) + } +} + +impl alloy_rlp::Encodable for DepositReceipt { + fn encode(&self, out: &mut dyn BufMut) { + self.encode_fields(out); + } + + fn length(&self) -> usize { + let payload_length = self.payload_len(); + payload_length + length_of_length(payload_length) } +} - pub fn logs(&self) -> &Vec { +impl alloy_rlp::Decodable for DepositReceipt { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + Self::decode_receipt(buf) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum TypedReceipt { + #[serde(rename = "0x0", alias = "0x00")] + Legacy(ReceiptWithBloom), + #[serde(rename = "0x1", alias = "0x01")] + EIP2930(ReceiptWithBloom), + #[serde(rename = "0x2", alias = "0x02")] + EIP1559(ReceiptWithBloom), + #[serde(rename = "0x3", alias = "0x03")] + EIP4844(ReceiptWithBloom), + #[serde(rename = "0x7E", alias = "0x7e")] + Deposit(DepositReceipt), +} + +impl TypedReceipt { + pub fn as_receipt_with_bloom(&self) -> &ReceiptWithBloom { match self { TypedReceipt::Legacy(r) | TypedReceipt::EIP1559(r) | TypedReceipt::EIP2930(r) | - TypedReceipt::EIP4844(r) | - TypedReceipt::Deposit(r) => &r.receipt.logs, + TypedReceipt::EIP4844(r) => r, + TypedReceipt::Deposit(r) => &r.inner, } } } -impl From for ReceiptWithBloom { - fn from(val: TypedReceipt) -> Self { - match val { - TypedReceipt::Legacy(r) | - TypedReceipt::EIP1559(r) | - TypedReceipt::EIP2930(r) | - TypedReceipt::EIP4844(r) | - TypedReceipt::Deposit(r) => r, +impl TypedReceipt { + pub fn cumulative_gas_used(&self) -> u128 { + self.as_receipt_with_bloom().cumulative_gas_used() + } + + pub fn logs_bloom(&self) -> &Bloom { + &self.as_receipt_with_bloom().logs_bloom + } + + pub fn logs(&self) -> &[Log] { + self.as_receipt_with_bloom().logs() + } +} + +impl From> for TypedReceipt { + fn from(value: ReceiptEnvelope) -> Self { + match value { + ReceiptEnvelope::Legacy(r) => TypedReceipt::Legacy(r), + ReceiptEnvelope::Eip2930(r) => TypedReceipt::EIP2930(r), + ReceiptEnvelope::Eip1559(r) => TypedReceipt::EIP1559(r), + ReceiptEnvelope::Eip4844(r) => TypedReceipt::EIP4844(r), + _ => unreachable!(), } } } impl Encodable for TypedReceipt { fn encode(&self, out: &mut dyn bytes::BufMut) { - use alloy_rlp::Header; - match self { TypedReceipt::Legacy(r) => r.encode(out), receipt => { @@ -1080,7 +1185,6 @@ impl Encodable for TypedReceipt { impl Decodable for TypedReceipt { fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - use alloy_rlp::Header; use bytes::Buf; use std::cmp::Ordering; @@ -1109,7 +1213,7 @@ impl Decodable for TypedReceipt { ::decode(buf).map(TypedReceipt::EIP4844) } else if receipt_type == 0x7E { buf.advance(1); - ::decode(buf).map(TypedReceipt::Deposit) + ::decode(buf).map(TypedReceipt::Deposit) } else { Err(alloy_rlp::Error::Custom("invalid receipt type")) } @@ -1124,41 +1228,59 @@ impl Decodable for TypedReceipt { } } -/// Translates an EIP-2930 access list to an alloy-rpc-types access list. -pub fn to_alloy_access_list( - access_list: alloy_eips::eip2930::AccessList, -) -> alloy_rpc_types::AccessList { - alloy_rpc_types::AccessList( - access_list - .0 - .into_iter() - .map(|item| alloy_rpc_types::AccessListItem { - address: item.address, - storage_keys: item.storage_keys, - }) - .collect(), - ) -} - -/// Translates an alloy-rpc-types access list to an EIP-2930 access list. -pub fn to_eip_access_list( - access_list: alloy_rpc_types::AccessList, -) -> alloy_eips::eip2930::AccessList { - alloy_eips::eip2930::AccessList( - access_list - .0 - .into_iter() - .map(|item| alloy_eips::eip2930::AccessListItem { - address: item.address, - storage_keys: item.storage_keys, - }) - .collect(), - ) +pub type ReceiptResponse = TransactionReceipt>; + +pub fn convert_to_anvil_receipt(receipt: AnyTransactionReceipt) -> Option { + let WithOtherFields { + inner: + TransactionReceipt { + transaction_hash, + transaction_index, + block_hash, + block_number, + gas_used, + contract_address, + effective_gas_price, + from, + to, + blob_gas_price, + blob_gas_used, + state_root, + inner: AnyReceiptEnvelope { inner: receipt_with_bloom, r#type }, + }, + other, + } = receipt; + + Some(TransactionReceipt { + transaction_hash, + transaction_index, + block_hash, + block_number, + gas_used, + contract_address, + effective_gas_price, + from, + to, + blob_gas_price, + blob_gas_used, + state_root, + inner: match r#type { + 0x00 => TypedReceipt::Legacy(receipt_with_bloom), + 0x01 => TypedReceipt::EIP2930(receipt_with_bloom), + 0x02 => TypedReceipt::EIP1559(receipt_with_bloom), + 0x03 => TypedReceipt::EIP4844(receipt_with_bloom), + 0x7E => TypedReceipt::Deposit(DepositReceipt { + inner: receipt_with_bloom, + deposit_nonce: other.get("depositNonce").and_then(|v| v.as_u64()), + deposit_nonce_version: other.get("depositNonceVersion").and_then(|v| v.as_u64()), + }), + _ => return None, + }, + }) } #[cfg(test)] mod tests { - use alloy_consensus::Receipt; use alloy_primitives::{b256, hex, LogData}; use std::str::FromStr; @@ -1171,8 +1293,8 @@ mod tests { let tx = TxLegacy { nonce: 2u64, - gas_price: 1000000000u64.into(), - gas_limit: 100000u64, + gas_price: 1000000000u128, + gas_limit: 100000u128, to: TxKind::Call(Address::from_slice( &hex::decode("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap()[..], )), @@ -1238,10 +1360,7 @@ mod tests { _ => unreachable!(), }; - assert_eq!( - tx.tx().tx().to, - TxKind::Call(address!("11E9CA82A3a762b4B5bd264d4173a242e7a77064")) - ); + assert_eq!(tx.tx().tx().to, address!("11E9CA82A3a762b4B5bd264d4173a242e7a77064")); assert_eq!( tx.tx().tx().blob_versioned_hashes, @@ -1266,11 +1385,11 @@ mod tests { panic!("decoding TypedTransaction failed"); }; - assert_eq!(tx.input, Bytes::from(b"")); - assert_eq!(tx.gas_price, 1); - assert_eq!(tx.gas_limit, 21000); - assert_eq!(tx.nonce, 0); - if let TxKind::Call(to) = tx.to { + assert_eq!(tx.tx().input, Bytes::from(b"")); + assert_eq!(tx.tx().gas_price, 1); + assert_eq!(tx.tx().gas_limit, 21000); + assert_eq!(tx.tx().nonce, 0); + if let TxKind::Call(to) = tx.tx().to { assert_eq!( to, "0x095e7baea6a6c7c4c2dfeb977efac326af552d87".parse::
().unwrap() @@ -1278,7 +1397,7 @@ mod tests { } else { panic!("expected a call transaction"); } - assert_eq!(tx.value, U256::from(0x0au64)); + assert_eq!(tx.tx().value, U256::from(0x0au64)); assert_eq!( tx.recover_signer().unwrap(), "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".parse::
().unwrap() @@ -1292,8 +1411,8 @@ mod tests { let mut data = vec![]; let receipt = TypedReceipt::Legacy(ReceiptWithBloom { receipt: Receipt { - success: false, - cumulative_gas_used: 0x1u64, + status: false, + cumulative_gas_used: 0x1u128, logs: vec![Log { address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), data: LogData::new_unchecked( @@ -1311,7 +1430,7 @@ mod tests { ), }], }, - bloom: [0; 256].into(), + logs_bloom: [0; 256].into(), }); receipt.encode(&mut data); @@ -1327,8 +1446,8 @@ mod tests { let expected = TypedReceipt::Legacy(ReceiptWithBloom { receipt: Receipt { - success: false, - cumulative_gas_used: 0x1u64, + status: false, + cumulative_gas_used: 0x1u128, logs: vec![Log { address: Address::from_str("0000000000000000000000000000000000000011").unwrap(), data: LogData::new_unchecked( @@ -1346,7 +1465,7 @@ mod tests { ), }], }, - bloom: [0; 256].into(), + logs_bloom: [0; 256].into(), }); let receipt = TypedReceipt::decode(&mut &data[..]).unwrap(); diff --git a/crates/anvil/core/src/eth/transaction/optimism.rs b/crates/anvil/core/src/eth/transaction/optimism.rs index 86edc810a537..dedaffaf3458 100644 --- a/crates/anvil/core/src/eth/transaction/optimism.rs +++ b/crates/anvil/core/src/eth/transaction/optimism.rs @@ -1,6 +1,5 @@ -use alloy_consensus::TxType; -use alloy_network::{Transaction, TxKind}; -use alloy_primitives::{Address, Bytes, ChainId, Signature, B256, U256}; +use alloy_consensus::{SignableTransaction, Signed, Transaction, TxType}; +use alloy_primitives::{keccak256, Address, Bytes, ChainId, Signature, TxKind, B256, U256}; use alloy_rlp::{ length_of_length, Decodable, Encodable, Error as DecodeError, Header as RlpHeader, }; @@ -13,7 +12,7 @@ pub struct DepositTransactionRequest { pub kind: TxKind, pub mint: U256, pub value: U256, - pub gas_limit: U256, + pub gas_limit: u128, pub is_system_tx: bool, pub input: Bytes, } @@ -134,108 +133,74 @@ impl DepositTransactionRequest { 1 + length_of_length(payload_length) + payload_length } - /// Outputs the signature hash of the transaction by first encoding without a signature, then - /// hashing. - pub(crate) fn signature_hash(&self) -> B256 { - let mut buf = Vec::with_capacity(self.payload_len_for_signature()); - self.encode_for_signing(&mut buf); - alloy_primitives::utils::keccak256(&buf) + fn encoded_len_with_signature(&self, signature: &Signature) -> usize { + // this counts the tx fields and signature fields + let payload_length = self.fields_len() + signature.rlp_vrs_len(); + + // this counts: + // * tx type byte + // * inner header length + // * inner payload length + 1 + alloy_rlp::Header { list: true, payload_length }.length() + payload_length } } impl Transaction for DepositTransactionRequest { - type Signature = Signature; - - fn chain_id(&self) -> Option { - None - } - - fn gas_limit(&self) -> u64 { - self.gas_limit.to::() - } - - fn nonce(&self) -> u64 { - u64::MAX + fn input(&self) -> &[u8] { + &self.input } - fn decode_signed(buf: &mut &[u8]) -> alloy_rlp::Result> - where - Self: Sized, - { - let header = alloy_rlp::Header::decode(buf)?; - if !header.list { - return Err(alloy_rlp::Error::UnexpectedString); - } - - let tx = Self::decode_inner(buf)?; - let signature = Signature::decode_rlp_vrs(buf)?; - - Ok(tx.into_signed(signature)) + /// Get `to`. + fn to(&self) -> TxKind { + self.kind } - fn encode_signed(&self, signature: &Signature, out: &mut dyn bytes::BufMut) { - self.encode_with_signature(signature, out) + /// Get `value`. + fn value(&self) -> U256 { + self.value } - fn gas_price(&self) -> Option { + /// Get `chain_id`. + fn chain_id(&self) -> Option { None } - fn input(&self) -> &[u8] { - &self.input + /// Get `nonce`. + fn nonce(&self) -> u64 { + u64::MAX } - fn input_mut(&mut self) -> &mut Bytes { - &mut self.input + /// Get `gas_limit`. + fn gas_limit(&self) -> u128 { + self.gas_limit } - fn into_signed(self, signature: Signature) -> alloy_network::Signed - where - Self: Sized, - { - alloy_network::Signed::new_unchecked(self.clone(), signature, self.signature_hash()) + /// Get `gas_price`. + fn gas_price(&self) -> Option { + None } +} +impl SignableTransaction for DepositTransactionRequest { fn set_chain_id(&mut self, _chain_id: ChainId) {} - fn set_gas_limit(&mut self, limit: u64) { - self.gas_limit = U256::from(limit); - } - - fn set_gas_price(&mut self, _price: U256) {} - - fn set_input(&mut self, data: Bytes) { - self.input = data; - } - - fn set_nonce(&mut self, _nonce: u64) {} - - fn set_to(&mut self, to: TxKind) { - self.kind = to; - } - - fn set_value(&mut self, value: U256) { - self.value = value; - } - - fn signature_hash(&self) -> B256 { - self.signature_hash() + fn payload_len_for_signature(&self) -> usize { + self.payload_len_for_signature() } - fn to(&self) -> TxKind { - self.kind - } + fn into_signed(self, signature: Signature) -> Signed { + let mut buf = Vec::with_capacity(self.encoded_len_with_signature(&signature)); + self.encode_with_signature(&signature, &mut buf); + let hash = keccak256(&buf); - fn value(&self) -> U256 { - self.value + // Drop any v chain id value to ensure the signature format is correct at the time of + // combination for an EIP-4844 transaction. V should indicate the y-parity of the + // signature. + Signed::new_unchecked(self, signature.with_parity_bool(), hash) } fn encode_for_signing(&self, out: &mut dyn alloy_rlp::BufMut) { - self.encode_for_signing(out) - } - - fn payload_len_for_signature(&self) -> usize { - self.payload_len_for_signature() + self.encode_for_signing(out); } } @@ -265,19 +230,19 @@ impl Encodable for DepositTransactionRequest { /// See #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct DepositTransaction { - pub nonce: U256, + pub nonce: u64, pub source_hash: B256, pub from: Address, pub kind: TxKind, pub mint: U256, pub value: U256, - pub gas_limit: U256, + pub gas_limit: u128, pub is_system_tx: bool, pub input: Bytes, } impl DepositTransaction { - pub fn nonce(&self) -> &U256 { + pub fn nonce(&self) -> &u64 { &self.nonce } @@ -336,7 +301,7 @@ impl DepositTransaction { /// - `input` pub fn decode_inner(buf: &mut &[u8]) -> Result { Ok(Self { - nonce: U256::ZERO, + nonce: 0, source_hash: Decodable::decode(buf)?, from: Decodable::decode(buf)?, kind: Decodable::decode(buf)?, diff --git a/crates/anvil/core/src/types.rs b/crates/anvil/core/src/types.rs index db966d956d20..2e0c63b13485 100644 --- a/crates/anvil/core/src/types.rs +++ b/crates/anvil/core/src/types.rs @@ -190,10 +190,10 @@ pub struct NodeInfo { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct NodeEnvironment { - pub base_fee: U256, + pub base_fee: u128, pub chain_id: u64, - pub gas_limit: U256, - pub gas_price: U256, + pub gas_limit: u128, + pub gas_price: u128, } #[derive(Clone, Debug, Default, PartialEq, Eq)] diff --git a/crates/anvil/src/cmd.rs b/crates/anvil/src/cmd.rs index 9cde54553120..8e29152410ee 100644 --- a/crates/anvil/src/cmd.rs +++ b/crates/anvil/src/cmd.rs @@ -5,7 +5,7 @@ use crate::{ }; use alloy_genesis::Genesis; use alloy_primitives::{utils::Unit, U256}; -use alloy_signer::coins_bip39::{English, Mnemonic}; +use alloy_signer_wallet::coins_bip39::{English, Mnemonic}; use anvil_server::ServerConfig; use clap::Parser; use core::fmt; @@ -194,9 +194,9 @@ impl NodeArgs { }; NodeConfig::default() - .with_gas_limit(self.evm_opts.gas_limit.map(U256::from)) + .with_gas_limit(self.evm_opts.gas_limit) .disable_block_gas_limit(self.evm_opts.disable_block_gas_limit) - .with_gas_price(self.evm_opts.gas_price.map(U256::from)) + .with_gas_price(self.evm_opts.gas_price) .with_hardfork(self.hardfork) .with_blocktime(self.block_time) .with_no_mining(self.no_mining) @@ -216,7 +216,7 @@ impl NodeArgs { .fork_retry_backoff(self.evm_opts.fork_retry_backoff.map(Duration::from_millis)) .fork_compute_units_per_second(compute_units_per_second) .with_eth_rpc_url(self.evm_opts.fork_url.map(|fork| fork.url)) - .with_base_fee(self.evm_opts.block_base_fee_per_gas.map(U256::from)) + .with_base_fee(self.evm_opts.block_base_fee_per_gas) .with_storage_caching(self.evm_opts.no_storage_caching) .with_server_config(self.server_config) .with_host(self.host) @@ -455,7 +455,7 @@ pub struct AnvilEvmArgs { /// The block gas limit. #[arg(long, alias = "block-gas-limit", help_heading = "Environment config")] - pub gas_limit: Option, + pub gas_limit: Option, /// Disable the `call.gas_limit <= block.gas_limit` constraint. #[arg( @@ -474,7 +474,7 @@ pub struct AnvilEvmArgs { /// The gas price. #[arg(long, help_heading = "Environment config")] - pub gas_price: Option, + pub gas_price: Option, /// The base fee in a block. #[arg( @@ -483,7 +483,7 @@ pub struct AnvilEvmArgs { value_name = "FEE", help_heading = "Environment config" )] - pub block_base_fee_per_gas: Option, + pub block_base_fee_per_gas: Option, /// The chain ID. #[arg(long, alias = "chain", help_heading = "Environment config")] diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index a1e736619250..58de505ed011 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -16,14 +16,16 @@ use crate::{ FeeManager, Hardfork, }; use alloy_genesis::Genesis; +use alloy_network::AnyNetwork; use alloy_primitives::{hex, utils::Unit, U256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use alloy_rpc_types::BlockNumberOrTag; -use alloy_signer::{ +use alloy_signer::Signer; +use alloy_signer_wallet::{ coins_bip39::{English, Mnemonic}, - LocalWallet, MnemonicBuilder, Signer as AlloySigner, + LocalWallet, MnemonicBuilder, }; -use alloy_transport::TransportError; +use alloy_transport::{Transport, TransportError}; use anvil_server::ServerConfig; use foundry_common::{ provider::alloy::ProviderBuilder, ALCHEMY_FREE_TIER_CUPS, NON_ARCHIVE_NODE_WARNING, @@ -91,13 +93,13 @@ pub struct NodeConfig { /// Chain ID of the EVM chain pub chain_id: Option, /// Default gas limit for all txs - pub gas_limit: U256, + pub gas_limit: u128, /// If set to `true`, disables the block gas limit pub disable_block_gas_limit: bool, /// Default gas price for all txs - pub gas_price: Option, + pub gas_price: Option, /// Default base fee - pub base_fee: Option, + pub base_fee: Option, /// The hardfork to use pub hardfork: Option, /// Signer accounts that will be initialised with `genesis_balance` in the genesis block @@ -376,7 +378,7 @@ impl Default for NodeConfig { let genesis_accounts = AccountGenerator::new(10).phrase(DEFAULT_MNEMONIC).gen(); Self { chain_id: None, - gas_limit: U256::from(30_000_000), + gas_limit: 30_000_000, disable_block_gas_limit: false, gas_price: None, hardfork: None, @@ -432,15 +434,15 @@ impl NodeConfig { self } /// Returns the base fee to use - pub fn get_base_fee(&self) -> U256 { + pub fn get_base_fee(&self) -> u128 { self.base_fee - .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas.map(U256::from))) - .unwrap_or_else(|| U256::from(INITIAL_BASE_FEE)) + .or_else(|| self.genesis.as_ref().and_then(|g| g.base_fee_per_gas)) + .unwrap_or(INITIAL_BASE_FEE) } /// Returns the base fee to use - pub fn get_gas_price(&self) -> U256 { - self.gas_price.unwrap_or_else(|| U256::from(INITIAL_GAS_PRICE)) + pub fn get_gas_price(&self) -> u128 { + self.gas_price.unwrap_or(INITIAL_GAS_PRICE) } /// Returns the base fee to use @@ -497,7 +499,7 @@ impl NodeConfig { /// Sets the gas limit #[must_use] - pub fn with_gas_limit(mut self, gas_limit: Option) -> Self { + pub fn with_gas_limit(mut self, gas_limit: Option) -> Self { if let Some(gas_limit) = gas_limit { self.gas_limit = gas_limit; } @@ -515,8 +517,8 @@ impl NodeConfig { /// Sets the gas price #[must_use] - pub fn with_gas_price(mut self, gas_price: Option) -> Self { - self.gas_price = gas_price.map(Into::into); + pub fn with_gas_price(mut self, gas_price: Option) -> Self { + self.gas_price = gas_price; self } @@ -539,8 +541,8 @@ impl NodeConfig { /// Sets the base fee #[must_use] - pub fn with_base_fee(mut self, base_fee: Option) -> Self { - self.base_fee = base_fee.map(Into::into); + pub fn with_base_fee(mut self, base_fee: Option) -> Self { + self.base_fee = base_fee; self } @@ -857,8 +859,8 @@ impl NodeConfig { let env = revm::primitives::Env { cfg: cfg.cfg_env, block: BlockEnv { - gas_limit: self.gas_limit, - basefee: self.get_base_fee(), + gas_limit: U256::from(self.gas_limit), + basefee: U256::from(self.get_base_fee()), ..Default::default() }, tx: TxEnv { chain_id: self.get_chain_id().into(), ..Default::default() }, @@ -985,7 +987,7 @@ impl NodeConfig { // but only if we're forking mainnet let chain_id = provider.get_chain_id().await.expect("Failed to fetch network chain ID"); - if alloy_chains::NamedChain::Mainnet == chain_id.to::() { + if alloy_chains::NamedChain::Mainnet == chain_id { let hardfork: Hardfork = fork_block_number.into(); env.handler_cfg.spec_id = hardfork.into(); self.hardfork = Some(hardfork); @@ -1030,19 +1032,19 @@ latest block number: {latest_block}" // we only use the gas limit value of the block if it is non-zero and the block gas // limit is enabled, since there are networks where this is not used and is always // `0x0` which would inevitably result in `OutOfGas` errors as soon as the evm is about to record gas, See also - let gas_limit = if self.disable_block_gas_limit || block.header.gas_limit.is_zero() { - U256::from(u64::MAX) + let gas_limit = if self.disable_block_gas_limit || block.header.gas_limit == 0 { + u128::MAX } else { block.header.gas_limit }; env.block = BlockEnv { number: U256::from(fork_block_number), - timestamp: block.header.timestamp, + timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, // ensures prevrandao is set prevrandao: Some(block.header.mix_hash.unwrap_or_default()), - gas_limit, + gas_limit: U256::from(gas_limit), // Keep previous `coinbase` and `basefee` value coinbase: env.block.coinbase, basefee: env.block.basefee, @@ -1053,7 +1055,7 @@ latest block number: {latest_block}" if self.base_fee.is_none() { if let Some(base_fee) = block.header.base_fee_per_gas { self.base_fee = Some(base_fee); - env.block.basefee = base_fee; + env.block.basefee = U256::from(base_fee); // this is the base fee of the current block, but we need the base fee of // the next block let next_block_base_fee = fees.get_next_block_base_fee_per_gas( @@ -1062,7 +1064,7 @@ latest block number: {latest_block}" block.header.base_fee_per_gas.unwrap_or_default(), ); // update next base fee - fees.set_base_fee(U256::from(next_block_base_fee)); + fees.set_base_fee(next_block_base_fee); } } @@ -1080,9 +1082,9 @@ latest block number: {latest_block}" chain_id } else { let chain_id = if let Some(fork_chain_id) = fork_chain_id { - fork_chain_id.to::() + fork_chain_id.to() } else { - provider.get_chain_id().await.unwrap().to::() + provider.get_chain_id().await.unwrap() }; // need to update the dev signers and env with the chain id @@ -1117,7 +1119,7 @@ latest block number: {latest_block}" provider, chain_id, override_chain_id, - timestamp: block.header.timestamp.to::(), + timestamp: block.header.timestamp, base_fee: block.header.base_fee_per_gas, timeout: self.fork_request_timeout, retries: self.fork_request_retries, @@ -1241,7 +1243,9 @@ pub fn anvil_tmp_dir() -> Option { /// /// This fetches the "latest" block and checks whether the `Block` is fully populated (`hash` field /// is present). This prevents edge cases where anvil forks the "latest" block but `eth_getBlockByNumber` still returns a pending block, -async fn find_latest_fork_block(provider: P) -> Result { +async fn find_latest_fork_block, T: Transport + Clone>( + provider: P, +) -> Result { let mut num = provider.get_block_number().await?; // walk back from the head of the chain, but at most 2 blocks, which should be more than enough diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index aee9670849df..d21ed852feee 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -31,29 +31,27 @@ use crate::{ revm::primitives::Output, ClientFork, LoggingManager, Miner, MiningMode, StorageInfo, }; -use alloy_consensus::TxLegacy; use alloy_dyn_abi::TypedData; -use alloy_network::{Signed, TxKind}; -use alloy_primitives::{Address, Bytes, TxHash, B256, B64, U256, U64}; -use alloy_rlp::Decodable; -use alloy_rpc_trace_types::{ - geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, - parity::LocalizedTransactionTrace, -}; +use alloy_network::eip2718::Decodable2718; +use alloy_primitives::{Address, Bytes, TxHash, TxKind, B256, B64, U256, U64}; use alloy_rpc_types::{ request::TransactionRequest, state::StateOverride, txpool::{TxpoolContent, TxpoolInspect, TxpoolInspectSummary, TxpoolStatus}, AccessList, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, Filter, FilteredParams, Log, - Transaction, TransactionReceipt, + Transaction, WithOtherFields, +}; +use alloy_rpc_types_trace::{ + geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, + parity::LocalizedTransactionTrace, }; use alloy_transport::TransportErrorKind; use anvil_core::{ eth::{ block::BlockInfo, transaction::{ - transaction_request_to_typed, PendingTransaction, TypedTransaction, + transaction_request_to_typed, PendingTransaction, ReceiptResponse, TypedTransaction, TypedTransactionRequest, }, EthRequest, @@ -447,11 +445,6 @@ impl EthApi { Err(BlockchainError::NoSignerAvailable) } - /// Queries the current gas limit - fn current_gas_limit(&self) -> Result { - Ok(self.backend.gas_limit()) - } - async fn block_request(&self, block_number: Option) -> Result { let block_request = match block_number { Some(BlockId::Number(BlockNumber::Pending)) => { @@ -550,7 +543,7 @@ impl EthApi { /// Returns the current gas price pub fn gas_price(&self) -> Result { - Ok(self.backend.gas_price()) + Ok(U256::from(self.backend.gas_price())) } /// Returns a fee per gas that is an estimate of how much you can pay as a priority fee, or @@ -558,12 +551,12 @@ impl EthApi { /// /// Handler for ETH RPC call: `eth_maxPriorityFeePerGas` pub fn gas_max_priority_fee_per_gas(&self) -> Result { - Ok(self.backend.max_priority_fee_per_gas()) + Ok(U256::from(self.backend.max_priority_fee_per_gas())) } /// Returns the block gas limit pub fn gas_limit(&self) -> U256 { - self.backend.gas_limit() + U256::from(self.backend.gas_limit()) } /// Returns the accounts list @@ -690,7 +683,7 @@ impl EthApi { block_number: Option, ) -> Result { node_info!("eth_getTransactionCount"); - self.get_transaction_count(address, block_number).await + self.get_transaction_count(address, block_number).await.map(U256::from) } /// Returns the number of transactions in a block with given hash. @@ -845,7 +838,10 @@ impl EthApi { /// Signs a transaction /// /// Handler for ETH RPC call: `eth_signTransaction` - pub async fn sign_transaction(&self, mut request: TransactionRequest) -> Result { + pub async fn sign_transaction( + &self, + mut request: WithOtherFields, + ) -> Result { node_info!("eth_signTransaction"); let from = request.from.map(Ok).unwrap_or_else(|| { @@ -857,7 +853,7 @@ impl EthApi { if request.gas.is_none() { // estimate if not provided if let Ok(gas) = self.estimate_gas(request.clone(), None, None).await { - request.gas = Some(gas); + request.gas = Some(gas.to()); } } @@ -872,7 +868,10 @@ impl EthApi { /// Sends a transaction /// /// Handler for ETH RPC call: `eth_sendTransaction` - pub async fn send_transaction(&self, mut request: TransactionRequest) -> Result { + pub async fn send_transaction( + &self, + mut request: WithOtherFields, + ) -> Result { node_info!("eth_sendTransaction"); let from = request.from.map(Ok).unwrap_or_else(|| { @@ -883,7 +882,7 @@ impl EthApi { if request.gas.is_none() { // estimate if not provided if let Ok(gas) = self.estimate_gas(request.clone(), None, None).await { - request.gas = Some(gas); + request.gas = Some(gas.to()); } } @@ -904,7 +903,7 @@ impl EthApi { self.backend.validate_pool_transaction(&pending_transaction).await?; let requires = required_marker(nonce, on_chain_nonce, from); - let provides = vec![to_marker(nonce.to::(), from)]; + let provides = vec![to_marker(nonce, from)]; debug_assert!(requires != provides); self.add_pending_transaction(pending_transaction, requires, provides) @@ -919,26 +918,11 @@ impl EthApi { if data.is_empty() { return Err(BlockchainError::EmptyRawTransactionData); } - let transaction = if data[0] > 0x7f { - // legacy transaction - match Signed::::decode(&mut data) { - Ok(transaction) => TypedTransaction::Legacy(transaction), - Err(_) => return Err(BlockchainError::FailedToDecodeSignedTransaction), - } - } else { - // the [TypedTransaction] requires a valid rlp input, - // but EIP-1559 prepends a version byte, so we need to encode the data first to get a - // valid rlp and then rlp decode impl of `TypedTransaction` will remove and check the - // version byte - let extend = alloy_rlp::encode(data); - let tx = match TypedTransaction::decode(&mut &extend[..]) { - Ok(transaction) => transaction, - Err(_) => return Err(BlockchainError::FailedToDecodeSignedTransaction), - }; + let transaction = TypedTransaction::decode_2718(&mut data) + .map_err(|_| BlockchainError::FailedToDecodeSignedTransaction)?; + + self.ensure_typed_transaction_supported(&transaction)?; - self.ensure_typed_transaction_supported(&tx)?; - tx - }; let pending_transaction = PendingTransaction::new(transaction)?; // pre-validate @@ -952,7 +936,7 @@ impl EthApi { let priority = self.transaction_priority(&pending_transaction.transaction); let pool_transaction = PoolTransaction { requires, - provides: vec![to_marker(nonce.to::(), *pending_transaction.sender())], + provides: vec![to_marker(nonce, *pending_transaction.sender())], pending_transaction, priority, }; @@ -967,7 +951,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_call` pub async fn call( &self, - request: TransactionRequest, + request: WithOtherFields, block_number: Option, overrides: Option, ) -> Result { @@ -1020,7 +1004,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_createAccessList` pub async fn create_access_list( &self, - mut request: TransactionRequest, + mut request: WithOtherFields, block_number: Option, ) -> Result { node_info!("eth_createAccessList"); @@ -1069,7 +1053,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_estimateGas` pub async fn estimate_gas( &self, - request: TransactionRequest, + request: WithOtherFields, block_number: Option, overrides: Option, ) -> Result { @@ -1080,6 +1064,7 @@ impl EthApi { overrides, ) .await + .map(U256::from) } /// Get transaction by its hash. @@ -1088,7 +1073,10 @@ impl EthApi { /// this will also scan the mempool for a matching pending transaction /// /// Handler for ETH RPC call: `eth_getTransactionByHash` - pub async fn transaction_by_hash(&self, hash: B256) -> Result> { + pub async fn transaction_by_hash( + &self, + hash: B256, + ) -> Result>> { node_info!("eth_getTransactionByHash"); let mut tx = self.pool.get_transaction(hash).map(|pending| { let from = *pending.sender(); @@ -1118,7 +1106,7 @@ impl EthApi { &self, hash: B256, index: Index, - ) -> Result> { + ) -> Result>> { node_info!("eth_getTransactionByBlockHashAndIndex"); self.backend.transaction_by_block_hash_and_index(hash, index).await } @@ -1130,7 +1118,7 @@ impl EthApi { &self, block: BlockNumber, idx: Index, - ) -> Result> { + ) -> Result>> { node_info!("eth_getTransactionByBlockNumberAndIndex"); self.backend.transaction_by_block_number_and_index(block, idx).await } @@ -1138,7 +1126,7 @@ impl EthApi { /// Returns transaction receipt by transaction hash. /// /// Handler for ETH RPC call: `eth_getTransactionReceipt` - pub async fn transaction_receipt(&self, hash: B256) -> Result> { + pub async fn transaction_receipt(&self, hash: B256) -> Result> { node_info!("eth_getTransactionReceipt"); let tx = self.pool.get_transaction(hash); if tx.is_some() { @@ -1153,7 +1141,7 @@ impl EthApi { pub async fn block_receipts( &self, number: BlockNumber, - ) -> Result>> { + ) -> Result>> { node_info!("eth_getBlockReceipts"); self.backend.block_receipts(number).await } @@ -1266,7 +1254,7 @@ impl EthApi { // efficiently, instead we fetch it from the fork if fork.predates_fork_inclusive(number) { return fork - .fee_history(block_count, BlockNumber::Number(number), &reward_percentiles) + .fee_history(block_count.to(), BlockNumber::Number(number), &reward_percentiles) .await .map_err(BlockchainError::AlloyForkProvider); } @@ -1285,7 +1273,7 @@ impl EthApi { } let mut response = FeeHistory { - oldest_block: U256::from(lowest), + oldest_block: lowest, base_fee_per_gas: Vec::new(), gas_used_ratio: Vec::new(), reward: Some(Default::default()), @@ -1301,7 +1289,7 @@ impl EthApi { for n in lowest..=highest { // if let Some(block) = fee_history.get(&n) { - response.base_fee_per_gas.push(U256::from(block.base_fee)); + response.base_fee_per_gas.push(block.base_fee); response.gas_used_ratio.push(block.gas_used_ratio); // requested percentiles @@ -1311,11 +1299,7 @@ impl EthApi { for p in &reward_percentiles { let p = p.clamp(0.0, 100.0); let index = ((p.round() / 2f64) * 2f64) * resolution_per_percentile; - let reward = if let Some(r) = block.rewards.get(index as usize) { - U256::from(*r) - } else { - U256::ZERO - }; + let reward = block.rewards.get(index as usize).map_or(0, |r| *r); block_rewards.push(reward); } rewards.push(block_rewards); @@ -1334,20 +1318,20 @@ impl EthApi { (response.gas_used_ratio.last(), response.base_fee_per_gas.last()) { let elasticity = self.backend.elasticity(); - let last_fee_per_gas = last_fee_per_gas.to::() as f64; + let last_fee_per_gas = *last_fee_per_gas as f64; if last_gas_used > &0.5 { // increase base gas let increase = ((last_gas_used - 0.5) * 2f64) * elasticity; - let new_base_fee = (last_fee_per_gas + (last_fee_per_gas * increase)) as u64; - response.base_fee_per_gas.push(U256::from(new_base_fee)); + let new_base_fee = (last_fee_per_gas + (last_fee_per_gas * increase)) as u128; + response.base_fee_per_gas.push(new_base_fee); } else if last_gas_used < &0.5 { // decrease gas let increase = ((0.5 - last_gas_used) * 2f64) * elasticity; - let new_base_fee = (last_fee_per_gas - (last_fee_per_gas * increase)) as u64; - response.base_fee_per_gas.push(U256::from(new_base_fee)); + let new_base_fee = (last_fee_per_gas - (last_fee_per_gas * increase)) as u128; + response.base_fee_per_gas.push(new_base_fee); } else { // same base gas - response.base_fee_per_gas.push(U256::from(last_fee_per_gas as u64)); + response.base_fee_per_gas.push(last_fee_per_gas as u128); } } @@ -1362,7 +1346,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_maxPriorityFeePerGas` pub fn max_priority_fee_per_gas(&self) -> Result { node_info!("eth_maxPriorityFeePerGas"); - Ok(self.backend.max_priority_fee_per_gas()) + Ok(U256::from(self.backend.max_priority_fee_per_gas())) } /// Creates a filter object, based on filter options, to notify when the state changes (logs). @@ -1447,7 +1431,7 @@ impl EthApi { /// Handler for RPC call: `debug_traceCall` pub async fn debug_trace_call( &self, - request: TransactionRequest, + request: WithOtherFields, block_number: Option, opts: GethDefaultTracingOptions, ) -> Result { @@ -1671,7 +1655,7 @@ impl EthApi { ) .into()); } - self.backend.set_gas_price(gas); + self.backend.set_gas_price(gas.to()); Ok(()) } @@ -1686,7 +1670,7 @@ impl EthApi { ) .into()); } - self.backend.set_base_fee(basefee); + self.backend.set_base_fee(basefee.to()); Ok(()) } @@ -1842,7 +1826,7 @@ impl EthApi { /// Handler for RPC call: `evm_setBlockGasLimit` pub fn evm_set_block_gas_limit(&self, gas_limit: U256) -> Result { node_info!("evm_setBlockGasLimit"); - self.backend.set_gas_limit(gas_limit); + self.backend.set_gas_limit(gas_limit.to()); Ok(true) } @@ -1907,7 +1891,7 @@ impl EthApi { if let Some(receipt) = self.backend.mined_transaction_receipt(tx.hash) { if let Some(output) = receipt.out { // insert revert reason if failure - if receipt.inner.status_code.unwrap_or_default().to::() == 0 { + if !receipt.inner.inner.as_receipt_with_bloom().receipt.status { if let Some(reason) = RevertDecoder::new().maybe_decode(&output, None) { @@ -1980,7 +1964,7 @@ impl EthApi { /// Handler for ETH RPC call: `eth_sendUnsignedTransaction` pub async fn eth_send_unsigned_transaction( &self, - request: TransactionRequest, + request: WithOtherFields, ) -> Result { node_info!("eth_sendUnsignedTransaction"); // either use the impersonated account of the request's `from` field @@ -2001,7 +1985,7 @@ impl EthApi { self.backend.validate_pool_transaction(&pending_transaction).await?; let requires = required_marker(nonce, on_chain_nonce, from); - let provides = vec![to_marker(nonce.to::(), from)]; + let provides = vec![to_marker(nonce, from)]; self.add_pending_transaction(pending_transaction, requires, provides) } @@ -2029,9 +2013,9 @@ impl EthApi { fn convert(tx: Arc) -> TxpoolInspectSummary { let tx = &tx.pending_transaction.transaction; let to = tx.to(); - let gas_price = tx.gas_price(); + let gas_price = U256::from(tx.gas_price()); let value = tx.value(); - let gas = tx.gas_limit(); + let gas = U256::from(tx.gas_limit()); TxpoolInspectSummary { to, value, gas, gas_price } } @@ -2076,7 +2060,7 @@ impl EthApi { // we set the from field here explicitly to the set sender of the pending transaction, // in case the transaction is impersonated. tx.from = from; - tx + tx.inner } for pending in self.pool.ready_transactions() { @@ -2146,10 +2130,10 @@ impl EthApi { async fn do_estimate_gas( &self, - request: TransactionRequest, + request: WithOtherFields, block_number: Option, overrides: Option, - ) -> Result { + ) -> Result { let block_request = self.block_request(block_number).await?; // check if the number predates the fork, if in fork mode if let BlockRequest::Number(number) = block_request { @@ -2183,10 +2167,10 @@ impl EthApi { /// This will execute the [CallRequest] and find the best gas limit via binary search fn do_estimate_gas_with_state( &self, - mut request: TransactionRequest, + mut request: WithOtherFields, state: D, block_env: BlockEnv, - ) -> Result + ) -> Result where D: DatabaseRef, { @@ -2212,11 +2196,11 @@ impl EthApi { // get the highest possible gas limit, either the request's set value or the currently // configured gas limit - let mut highest_gas_limit = request.gas.unwrap_or(block_env.gas_limit); + let mut highest_gas_limit = request.gas.unwrap_or(block_env.gas_limit.to()); let gas_price = fees.gas_price.unwrap_or_default(); // If we have non-zero gas price, cap gas limit by sender balance - if !gas_price.is_zero() { + if gas_price > 0 { if let Some(from) = request.from { let mut available_funds = self.backend.get_balance_with_state(&state, from)?; if let Some(value) = request.value { @@ -2227,7 +2211,8 @@ impl EthApi { available_funds -= value; } // amount of gas the sender can afford with the `gas_price` - let allowance = available_funds.checked_div(gas_price).unwrap_or_default(); + let allowance = + available_funds.to::().checked_div(gas_price).unwrap_or_default(); highest_gas_limit = std::cmp::min(highest_gas_limit, allowance); } } @@ -2240,7 +2225,7 @@ impl EthApi { self.backend.call_with_state(&state, call_to_estimate, fees.clone(), block_env.clone()); let gas_used = match ethres.try_into()? { - GasEstimationCallResult::Success(gas) => Ok(U256::from(gas)), + GasEstimationCallResult::Success(gas) => Ok(gas), GasEstimationCallResult::OutOfGas => { Err(InvalidTransactionError::BasicOutOfGas(highest_gas_limit).into()) } @@ -2262,13 +2247,11 @@ impl EthApi { let mut lowest_gas_limit = determine_base_gas_by_kind(&request); // pick a point that's close to the estimated gas - let mut mid_gas_limit = std::cmp::min( - gas_used * U256::from(3), - (highest_gas_limit + lowest_gas_limit) / U256::from(2), - ); + let mut mid_gas_limit = + std::cmp::min(gas_used * 3, (highest_gas_limit + lowest_gas_limit) / 2); // Binary search for the ideal gas limit - while (highest_gas_limit - lowest_gas_limit) > U256::from(1) { + while (highest_gas_limit - lowest_gas_limit) > 1 { request.gas = Some(mid_gas_limit); let ethres = self.backend.call_with_state( &state, @@ -2297,7 +2280,7 @@ impl EthApi { } }; // new midpoint - mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / U256::from(2); + mid_gas_limit = (highest_gas_limit + lowest_gas_limit) / 2; } trace!(target : "node", "Estimated Gas for call {:?}", highest_gas_limit); @@ -2398,7 +2381,7 @@ impl EthApi { Some(info), Some(base_fee), ); - block_transactions.push(tx); + block_transactions.push(tx.inner); } Some(partial_block.into_full_block(block_transactions)) @@ -2406,40 +2389,40 @@ impl EthApi { fn build_typed_tx_request( &self, - request: TransactionRequest, - nonce: U256, + request: WithOtherFields, + nonce: u64, ) -> Result { - let chain_id = request.chain_id.map(|c| c.to::()).unwrap_or_else(|| self.chain_id()); + let chain_id = request.chain_id.unwrap_or_else(|| self.chain_id()); let max_fee_per_gas = request.max_fee_per_gas; let gas_price = request.gas_price; - let gas_limit = request.gas.map(Ok).unwrap_or_else(|| self.current_gas_limit())?; + let gas_limit = request.gas.unwrap_or(self.backend.gas_limit()); let request = match transaction_request_to_typed(request) { Some(TypedTransactionRequest::Legacy(mut m)) => { - m.nonce = nonce.to::(); + m.nonce = nonce; m.chain_id = Some(chain_id); - m.gas_limit = gas_limit.to::(); + m.gas_limit = gas_limit; if gas_price.is_none() { - m.gas_price = self.gas_price().unwrap_or_default().to::(); + m.gas_price = self.backend.gas_price() } TypedTransactionRequest::Legacy(m) } Some(TypedTransactionRequest::EIP2930(mut m)) => { - m.nonce = nonce.to::(); + m.nonce = nonce; m.chain_id = chain_id; - m.gas_limit = gas_limit.to::(); + m.gas_limit = gas_limit; if gas_price.is_none() { - m.gas_price = self.gas_price().unwrap_or_default().to::(); + m.gas_price = self.backend.gas_price(); } TypedTransactionRequest::EIP2930(m) } Some(TypedTransactionRequest::EIP1559(mut m)) => { - m.nonce = nonce.to::(); + m.nonce = nonce; m.chain_id = chain_id; - m.gas_limit = gas_limit.to::(); + m.gas_limit = gas_limit; if max_fee_per_gas.is_none() { - m.max_fee_per_gas = self.gas_price().unwrap_or_default().to::(); + m.max_fee_per_gas = self.backend.gas_price(); } TypedTransactionRequest::EIP1559(m) } @@ -2462,7 +2445,7 @@ impl EthApi { &self, address: Address, block_number: Option, - ) -> Result { + ) -> Result { let block_request = self.block_request(block_number).await?; if let BlockRequest::Number(number) = block_request { @@ -2473,9 +2456,7 @@ impl EthApi { } } - let nonce = self.backend.get_nonce(address, block_request).await?; - - Ok(nonce) + self.backend.get_nonce(address, block_request).await } /// Returns the nonce for this request @@ -2489,10 +2470,10 @@ impl EthApi { &self, request: &TransactionRequest, from: Address, - ) -> Result<(U256, U256)> { + ) -> Result<(u64, u64)> { let highest_nonce = self.get_transaction_count(from, Some(BlockId::Number(BlockNumber::Pending))).await?; - let nonce = request.nonce.map(|n| n.to::()).unwrap_or(highest_nonce); + let nonce = request.nonce.unwrap_or(highest_nonce); Ok((nonce, highest_nonce)) } @@ -2530,13 +2511,13 @@ impl EthApi { } } -fn required_marker(provided_nonce: U256, on_chain_nonce: U256, from: Address) -> Vec { +fn required_marker(provided_nonce: u64, on_chain_nonce: u64, from: Address) -> Vec { if provided_nonce == on_chain_nonce { return Vec::new(); } - let prev_nonce = provided_nonce.saturating_sub(U256::from(1)); + let prev_nonce = provided_nonce.saturating_sub(1); if on_chain_nonce <= prev_nonce { - vec![to_marker(prev_nonce.to::(), from)] + vec![to_marker(prev_nonce, from)] } else { Vec::new() } @@ -2562,7 +2543,7 @@ fn ensure_return_ok(exit: InstructionResult, out: &Option) -> Result U256 { +fn determine_base_gas_by_kind(request: &WithOtherFields) -> u128 { match transaction_request_to_typed(request.clone()) { Some(request) => match request { TypedTransactionRequest::Legacy(req) => match req.to { @@ -2577,10 +2558,7 @@ fn determine_base_gas_by_kind(request: &TransactionRequest) -> U256 { TxKind::Call(_) => MIN_TRANSACTION_GAS, TxKind::Create => MIN_CREATE_GAS, }, - TypedTransactionRequest::EIP4844(req) => match req.tx().to { - TxKind::Call(_) => MIN_TRANSACTION_GAS, - TxKind::Create => MIN_CREATE_GAS, - }, + TypedTransactionRequest::EIP4844(_) => MIN_TRANSACTION_GAS, TypedTransactionRequest::Deposit(req) => match req.kind { TxKind::Call(_) => MIN_TRANSACTION_GAS, TxKind::Create => MIN_CREATE_GAS, @@ -2594,17 +2572,17 @@ fn determine_base_gas_by_kind(request: &TransactionRequest) -> U256 { /// Keeps result of a call to revm EVM used for gas estimation enum GasEstimationCallResult { - Success(u64), + Success(u128), OutOfGas, Revert(Option), EvmError(InstructionResult), } /// Converts the result of a call to revm EVM into a [GasEstimationCallRes]. -impl TryFrom, u64, State)>> for GasEstimationCallResult { +impl TryFrom, u128, State)>> for GasEstimationCallResult { type Error = BlockchainError; - fn try_from(res: Result<(InstructionResult, Option, u64, State)>) -> Result { + fn try_from(res: Result<(InstructionResult, Option, u128, State)>) -> Result { match res { // Exceptional case: init used too much gas, treated as out of gas error Err(BlockchainError::InvalidTransaction(InvalidTransactionError::GasTooHigh(_))) => { diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 5e68924c95c3..80a72beedc64 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -7,10 +7,12 @@ use crate::{ mem::inspector::Inspector, }; use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; -use alloy_primitives::{Bloom, BloomInput, Log, B256, U256}; +use alloy_primitives::{Bloom, BloomInput, Log, B256}; use anvil_core::eth::{ block::{Block, BlockInfo, PartialHeader}, - transaction::{PendingTransaction, TransactionInfo, TypedReceipt, TypedTransaction}, + transaction::{ + DepositReceipt, PendingTransaction, TransactionInfo, TypedReceipt, TypedTransaction, + }, trie, }; use foundry_evm::{ @@ -33,7 +35,7 @@ pub struct ExecutedTransaction { transaction: Arc, exit_reason: InstructionResult, out: Option, - gas_used: u64, + gas_used: u128, logs: Vec, traces: Vec, nonce: u64, @@ -43,54 +45,25 @@ pub struct ExecutedTransaction { impl ExecutedTransaction { /// Creates the receipt for the transaction - fn create_receipt(&self) -> TypedReceipt { - let used_gas = U256::from(self.gas_used); - let mut bloom = Bloom::default(); - logs_bloom(self.logs.clone(), &mut bloom); + fn create_receipt(&self, cumulative_gas_used: &mut u128) -> TypedReceipt { let logs = self.logs.clone(); + *cumulative_gas_used = cumulative_gas_used.saturating_add(self.gas_used); // successful return see [Return] let status_code = u8::from(self.exit_reason as u8 <= InstructionResult::SelfDestruct as u8); + let receipt_with_bloom: ReceiptWithBloom = + Receipt { status: status_code == 1, cumulative_gas_used: *cumulative_gas_used, logs } + .into(); + match &self.transaction.pending_transaction.transaction.transaction { - TypedTransaction::Legacy(_) => TypedReceipt::Legacy(ReceiptWithBloom { - receipt: Receipt { - success: status_code == 1, - cumulative_gas_used: used_gas.to::(), - logs, - }, - bloom, - }), - TypedTransaction::EIP2930(_) => TypedReceipt::EIP2930(ReceiptWithBloom { - receipt: Receipt { - success: status_code == 1, - cumulative_gas_used: used_gas.to::(), - logs, - }, - bloom, - }), - TypedTransaction::EIP1559(_) => TypedReceipt::EIP1559(ReceiptWithBloom { - receipt: Receipt { - success: status_code == 1, - cumulative_gas_used: used_gas.to::(), - logs, - }, - bloom, - }), - TypedTransaction::EIP4844(_) => TypedReceipt::EIP4844(ReceiptWithBloom { - receipt: Receipt { - success: status_code == 1, - cumulative_gas_used: used_gas.to::(), - logs, - }, - bloom, - }), - TypedTransaction::Deposit(_) => TypedReceipt::Deposit(ReceiptWithBloom { - receipt: Receipt { - success: status_code == 1, - cumulative_gas_used: used_gas.to::(), - logs, - }, - bloom, + TypedTransaction::Legacy(_) => TypedReceipt::Legacy(receipt_with_bloom), + TypedTransaction::EIP2930(_) => TypedReceipt::EIP2930(receipt_with_bloom), + TypedTransaction::EIP1559(_) => TypedReceipt::EIP1559(receipt_with_bloom), + TypedTransaction::EIP4844(_) => TypedReceipt::EIP4844(receipt_with_bloom), + TypedTransaction::Deposit(tx) => TypedReceipt::Deposit(DepositReceipt { + inner: receipt_with_bloom, + deposit_nonce: Some(tx.nonce), + deposit_nonce_version: Some(1), }), } } @@ -121,7 +94,7 @@ pub struct TransactionExecutor<'a, Db: ?Sized, Validator: TransactionValidator> pub cfg_env: CfgEnvWithHandlerCfg, pub parent_hash: B256, /// Cumulative gas used by all executed transactions - pub gas_used: U256, + pub gas_used: u128, pub enable_steps_tracing: bool, } @@ -132,17 +105,17 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let mut transaction_infos = Vec::new(); let mut receipts = Vec::new(); let mut bloom = Bloom::default(); - let mut cumulative_gas_used = U256::ZERO; + let mut cumulative_gas_used: u128 = 0; let mut invalid = Vec::new(); let mut included = Vec::new(); - let gas_limit = self.block_env.gas_limit; + let gas_limit = self.block_env.gas_limit.to::(); let parent_hash = self.parent_hash; - let block_number = self.block_env.number; + let block_number = self.block_env.number.to::(); let difficulty = self.block_env.difficulty; let beneficiary = self.block_env.coinbase; let timestamp = self.block_env.timestamp.to::(); let base_fee = if (self.cfg_env.handler_cfg.spec_id as u8) >= (SpecId::LONDON as u8) { - Some(self.block_env.basefee) + Some(self.block_env.basefee.to::()) } else { None }; @@ -169,10 +142,9 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' continue } }; - let receipt = tx.create_receipt(); - cumulative_gas_used = cumulative_gas_used.saturating_add(receipt.gas_used()); + let receipt = tx.create_receipt(&mut cumulative_gas_used); let ExecutedTransaction { transaction, logs, out, traces, exit_reason: exit, .. } = tx; - logs_bloom(logs.clone(), &mut bloom); + build_logs_bloom(logs.clone(), &mut bloom); let contract_address = out.as_ref().and_then(|out| { if let Output::Create(_, contract_address) = out { @@ -183,15 +155,13 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' } }); - let transaction_index = transaction_infos.len() as u32; + let transaction_index = transaction_infos.len() as u64; let info = TransactionInfo { transaction_hash: transaction.hash(), transaction_index, from: *transaction.pending_transaction.sender(), to: transaction.pending_transaction.transaction.to(), contract_address, - logs, - logs_bloom: *receipt.logs_bloom(), traces, exit, out: match out { @@ -200,6 +170,7 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' _ => None, }, nonce: tx.nonce, + gas_used: tx.gas_used, }; transaction_infos.push(info); @@ -217,14 +188,14 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' receipts_root, logs_bloom: bloom, difficulty, - number: block_number.to::(), - gas_limit: gas_limit.to::(), - gas_used: cumulative_gas_used.to::(), + number: block_number, + gas_limit, + gas_used: cumulative_gas_used, timestamp, extra_data: Default::default(), mix_hash: Default::default(), nonce: Default::default(), - base_fee: base_fee.map(|b| b.to::()), + base_fee, }; let block = Block::new(partial_header, transactions.clone(), ommers); @@ -268,8 +239,8 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator }; let env = self.env_for(&transaction.pending_transaction); // check that we comply with the block's gas limit - let max_gas = self.gas_used.saturating_add(U256::from(env.tx.gas_limit)); - if max_gas > env.block.gas_limit { + let max_gas = self.gas_used.saturating_add(env.tx.gas_limit as u128); + if max_gas > env.block.gas_limit.to::() { return Some(TransactionExecutionOutcome::Exhausted(transaction)) } @@ -342,7 +313,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator trace!(target: "backend", ?exit_reason, ?gas_used, "[{:?}] executed with out={:?}", transaction.hash(), out); - self.gas_used = self.gas_used.saturating_add(U256::from(gas_used)); + self.gas_used = self.gas_used.saturating_add(gas_used as u128); trace!(target: "backend::executor", "transacted [{:?}], result: {:?} gas {}", transaction.hash(), exit_reason, gas_used); @@ -350,7 +321,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator transaction, exit_reason, out, - gas_used, + gas_used: gas_used as u128, logs: logs.unwrap_or_default(), traces: inspector .tracer @@ -366,7 +337,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator } /// Inserts all logs into the bloom -fn logs_bloom(logs: Vec, bloom: &mut Bloom) { +fn build_logs_bloom(logs: Vec, bloom: &mut Bloom) { for log in logs { bloom.accrue(BloomInput::Raw(&log.address[..])); for topic in log.topics() { diff --git a/crates/anvil/src/eth/backend/fork.rs b/crates/anvil/src/eth/backend/fork.rs index 04ea0c70f5f1..3727740cd51a 100644 --- a/crates/anvil/src/eth/backend/fork.rs +++ b/crates/anvil/src/eth/backend/fork.rs @@ -2,17 +2,18 @@ use crate::eth::{backend::db::Db, error::BlockchainError}; use alloy_primitives::{Address, Bytes, StorageValue, B256, U256, U64}; -use alloy_providers::tmp::TempProvider; -use alloy_rpc_trace_types::{ - geth::{GethDebugTracingOptions, GethTrace}, - parity::LocalizedTransactionTrace as Trace, -}; +use alloy_provider::Provider; use alloy_rpc_types::{ request::TransactionRequest, AccessListWithGasUsed, Block, BlockId, BlockNumberOrTag as BlockNumber, BlockTransactions, EIP1186AccountProofResponse, FeeHistory, - Filter, Log, Transaction, TransactionReceipt, + Filter, Log, Transaction, WithOtherFields, +}; +use alloy_rpc_types_trace::{ + geth::{GethDebugTracingOptions, GethTrace}, + parity::LocalizedTransactionTrace as Trace, }; use alloy_transport::TransportError; +use anvil_core::eth::transaction::{convert_to_anvil_receipt, ReceiptResponse}; use foundry_common::provider::alloy::{ProviderBuilder, RetryProvider}; use parking_lot::{ lock_api::{RwLockReadGuard, RwLockWriteGuard}, @@ -66,7 +67,7 @@ impl ClientFork { let chain_id = if let Some(chain_id) = override_chain_id { chain_id } else { - self.provider().get_chain_id().await?.to::() + self.provider().get_chain_id().await? }; self.config.write().chain_id = chain_id; } @@ -75,11 +76,11 @@ impl ClientFork { let block = provider.get_block(block_number, false).await?.ok_or(BlockchainError::BlockNotFound)?; let block_hash = block.header.hash.ok_or(BlockchainError::BlockNotFound)?; - let timestamp = block.header.timestamp.to::(); + let timestamp = block.header.timestamp; let base_fee = block.header.base_fee_per_gas; let total_difficulty = block.header.total_difficulty.unwrap_or_default(); - let number = block.header.number.ok_or(BlockchainError::BlockNotFound)?.to::(); + let number = block.header.number.ok_or(BlockchainError::BlockNotFound)?; self.config.write().update_block(number, block_hash, timestamp, base_fee, total_difficulty); self.clear_cached_storage(); @@ -116,7 +117,7 @@ impl ClientFork { self.config.read().total_difficulty } - pub fn base_fee(&self) -> Option { + pub fn base_fee(&self) -> Option { self.config.read().base_fee } @@ -147,7 +148,7 @@ impl ClientFork { /// Returns the fee history `eth_feeHistory` pub async fn fee_history( &self, - block_count: U256, + block_count: u64, newest_block: BlockNumber, reward_percentiles: &[f64], ) -> Result { @@ -167,11 +168,11 @@ impl ClientFork { /// Sends `eth_call` pub async fn call( &self, - request: &TransactionRequest, + request: &WithOtherFields, block: Option, ) -> Result { let block = block.unwrap_or(BlockNumber::Latest); - let res = self.provider().call((*request).clone(), Some(block.into())).await?; + let res = self.provider().call(request, Some(block.into())).await?; Ok(res) } @@ -179,11 +180,11 @@ impl ClientFork { /// Sends `eth_call` pub async fn estimate_gas( &self, - request: &TransactionRequest, + request: &WithOtherFields, block: Option, - ) -> Result { + ) -> Result { let block = block.unwrap_or(BlockNumber::Latest); - let res = self.provider().estimate_gas(request.clone(), Some(block.into())).await?; + let res = self.provider().estimate_gas(request, Some(block.into())).await?; Ok(res) } @@ -191,10 +192,10 @@ impl ClientFork { /// Sends `eth_createAccessList` pub async fn create_access_list( &self, - request: &TransactionRequest, + request: &WithOtherFields, block: Option, ) -> Result { - self.provider().create_access_list(request.clone(), block.map(|b| b.into())).await + self.provider().create_access_list(request, block.map(|b| b.into())).await } pub async fn storage_at( @@ -211,7 +212,7 @@ impl ClientFork { return Ok(logs); } - let logs = self.provider().get_logs(filter.clone()).await?; + let logs = self.provider().get_logs(filter).await?; let mut storage = self.storage_write(); storage.logs.insert(filter.clone(), logs.clone()); @@ -230,7 +231,7 @@ impl ClientFork { let block_id = BlockId::Number(blocknumber.into()); - let code = self.provider().get_code_at(address, Some(block_id)).await?; + let code = self.provider().get_code_at(address, block_id).await?; let mut storage = self.storage_write(); storage.code_at.insert((address, blocknumber), code.clone().0.into()); @@ -247,25 +248,21 @@ impl ClientFork { self.provider().get_balance(address, Some(blocknumber.into())).await } - pub async fn get_nonce( - &self, - address: Address, - blocknumber: u64, - ) -> Result { + pub async fn get_nonce(&self, address: Address, block: u64) -> Result { trace!(target: "backend::fork", "get_nonce={:?}", address); - self.provider().get_transaction_count(address, Some(blocknumber.into())).await + self.provider().get_transaction_count(address, Some(block.into())).await } pub async fn transaction_by_block_number_and_index( &self, number: u64, index: usize, - ) -> Result, TransportError> { + ) -> Result>, TransportError> { if let Some(block) = self.block_by_number(number).await? { match block.transactions { BlockTransactions::Full(txs) => { if let Some(tx) = txs.get(index) { - return Ok(Some(tx.clone())); + return Ok(Some(WithOtherFields::new(tx.clone()))); } } BlockTransactions::Hashes(hashes) => { @@ -284,12 +281,12 @@ impl ClientFork { &self, hash: B256, index: usize, - ) -> Result, TransportError> { + ) -> Result>, TransportError> { if let Some(block) = self.block_by_hash(hash).await? { match block.transactions { BlockTransactions::Full(txs) => { if let Some(tx) = txs.get(index) { - return Ok(Some(tx.clone())); + return Ok(Some(WithOtherFields::new(tx.clone()))); } } BlockTransactions::Hashes(hashes) => { @@ -307,7 +304,7 @@ impl ClientFork { pub async fn transaction_by_hash( &self, hash: B256, - ) -> Result, TransportError> { + ) -> Result>, TransportError> { trace!(target: "backend::fork", "transaction_by_hash={:?}", hash); if let tx @ Some(_) = self.storage_read().transactions.get(&hash).cloned() { return Ok(tx); @@ -367,12 +364,14 @@ impl ClientFork { pub async fn transaction_receipt( &self, hash: B256, - ) -> Result, TransportError> { + ) -> Result, BlockchainError> { if let Some(receipt) = self.storage_read().transaction_receipts.get(&hash).cloned() { return Ok(Some(receipt)); } if let Some(receipt) = self.provider().get_transaction_receipt(hash).await? { + let receipt = + convert_to_anvil_receipt(receipt).ok_or(BlockchainError::FailedToDecodeReceipt)?; let mut storage = self.storage_write(); storage.transaction_receipts.insert(hash, receipt.clone()); return Ok(Some(receipt)); @@ -384,7 +383,7 @@ impl ClientFork { pub async fn block_receipts( &self, number: u64, - ) -> Result>, TransportError> { + ) -> Result>, BlockchainError> { if let receipts @ Some(_) = self.storage_read().block_receipts.get(&number).cloned() { return Ok(receipts); } @@ -394,6 +393,16 @@ impl ClientFork { // this is being temporarily implemented in anvil. if self.predates_fork_inclusive(number) { let receipts = self.provider().get_block_receipts(BlockNumber::Number(number)).await?; + let receipts = receipts + .map(|r| { + r.into_iter() + .map(|r| { + convert_to_anvil_receipt(r) + .ok_or(BlockchainError::FailedToDecodeReceipt) + }) + .collect::, _>>() + }) + .transpose()?; if let Some(receipts) = receipts.clone() { let mut storage = self.storage_write(); @@ -469,14 +478,16 @@ impl ClientFork { ) -> Result, TransportError> { if let Some(block) = self.provider().get_block(block_id.into(), true).await? { let hash = block.header.hash.unwrap(); - let block_number = block.header.number.unwrap().to::(); + let block_number = block.header.number.unwrap(); let mut storage = self.storage_write(); // also insert all transactions let block_txs = match block.clone().transactions { BlockTransactions::Full(txs) => txs, _ => vec![], }; - storage.transactions.extend(block_txs.iter().map(|tx| (tx.hash, tx.clone()))); + storage + .transactions + .extend(block_txs.iter().map(|tx| (tx.hash, WithOtherFields::new(tx.clone())))); storage.hashes.insert(block_number, hash); storage.blocks.insert(hash, block.clone()); return Ok(Some(block)); @@ -520,14 +531,11 @@ impl ClientFork { let mut uncles = Vec::with_capacity(block.uncles.len()); for (uncle_idx, _) in block.uncles.iter().enumerate() { - let uncle = match self - .provider() - .get_uncle(block_number.to::().into(), U64::from(uncle_idx)) - .await? - { - Some(u) => u, - None => return Ok(None), - }; + let uncle = + match self.provider().get_uncle(block_number.into(), U64::from(uncle_idx)).await? { + Some(u) => u, + None => return Ok(None), + }; uncles.push(uncle); } self.storage_write().uncles.insert(block_hash, uncles.clone()); @@ -546,9 +554,10 @@ impl ClientFork { let mut transactions = Vec::with_capacity(block_txs_len); for tx in block.transactions.hashes() { if let Some(tx) = storage.transactions.get(tx).cloned() { - transactions.push(tx); + transactions.push(tx.inner); } } + // TODO: fix once blocks have generic transactions block.into_full_block(transactions) } } @@ -568,7 +577,7 @@ pub struct ClientForkConfig { /// The timestamp for the forked block pub timestamp: u64, /// The basefee of the forked block - pub base_fee: Option, + pub base_fee: Option, /// request timeout pub timeout: Duration, /// request retries for spurious networks @@ -611,7 +620,7 @@ impl ClientForkConfig { block_number: u64, block_hash: B256, timestamp: u64, - base_fee: Option, + base_fee: Option, total_difficulty: U256, ) { self.block_number = block_number; @@ -631,13 +640,13 @@ pub struct ForkedStorage { pub uncles: HashMap>, pub blocks: HashMap, pub hashes: HashMap, - pub transactions: HashMap, - pub transaction_receipts: HashMap, + pub transactions: HashMap>, + pub transaction_receipts: HashMap, pub transaction_traces: HashMap>, pub logs: HashMap>, pub geth_transaction_traces: HashMap, pub block_traces: HashMap>, - pub block_receipts: HashMap>, + pub block_receipts: HashMap>, pub code_at: HashMap<(Address, u64), Bytes>, } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index da49d8d35eec..939913b842a8 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -34,25 +34,24 @@ use crate::{ NodeConfig, }; use alloy_consensus::{Header, Receipt, ReceiptWithBloom}; -use alloy_network::Sealable; -use alloy_primitives::{keccak256, Address, Bytes, TxHash, B256, B64, U128, U256, U64, U8}; -use alloy_rpc_trace_types::{ +use alloy_primitives::{keccak256, Address, Bytes, TxHash, B256, U256, U64}; +use alloy_rpc_types::{ + request::TransactionRequest, serde_helpers::JsonStorageKey, state::StateOverride, AccessList, + Block as AlloyBlock, BlockId, BlockNumberOrTag as BlockNumber, + EIP1186AccountProofResponse as AccountProof, EIP1186StorageProof as StorageProof, Filter, + FilteredParams, Header as AlloyHeader, Log, Transaction, TransactionReceipt, WithOtherFields, +}; +use alloy_rpc_types_trace::{ geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace}, parity::LocalizedTransactionTrace, }; -use alloy_rpc_types::{ - request::TransactionRequest, state::StateOverride, AccessList, Block as AlloyBlock, BlockId, - BlockNumberOrTag as BlockNumber, EIP1186AccountProofResponse as AccountProof, - EIP1186StorageProof as StorageProof, Filter, FilteredParams, Header as AlloyHeader, - JsonStorageKey, Log, Transaction, TransactionReceipt, -}; use alloy_trie::{HashBuilder, Nibbles}; use anvil_core::{ eth::{ block::{Block, BlockInfo}, transaction::{ - MaybeImpersonatedTransaction, PendingTransaction, TransactionInfo, TypedReceipt, - TypedTransaction, + DepositReceipt, MaybeImpersonatedTransaction, PendingTransaction, ReceiptResponse, + TransactionInfo, TypedReceipt, TypedTransaction, }, utils::{alloy_to_revm_access_list, meets_eip155}, }, @@ -97,9 +96,9 @@ pub mod state; pub mod storage; // Gas per transaction not creating a contract. -pub const MIN_TRANSACTION_GAS: U256 = U256::from_limbs([21_000, 0, 0, 0]); +pub const MIN_TRANSACTION_GAS: u128 = 21000; // Gas per transaction creating a contract. -pub const MIN_CREATE_GAS: U256 = U256::from_limbs([53_000, 0, 0, 0]); +pub const MIN_CREATE_GAS: u128 = 53000; pub type State = foundry_evm::utils::StateChangeset; @@ -410,8 +409,8 @@ impl Backend { env.block = BlockEnv { number: rU256::from(fork_block_number), - timestamp: fork_block.header.timestamp, - gas_limit: fork_block.header.gas_limit, + timestamp: U256::from(fork_block.header.timestamp), + gas_limit: U256::from(fork_block.header.gas_limit), difficulty: fork_block.header.difficulty, prevrandao: Some(fork_block.header.mix_hash.unwrap_or_default()), // Keep previous `coinbase` and `basefee` value @@ -430,7 +429,7 @@ impl Backend { fork_block.header.base_fee_per_gas.unwrap_or_default(), ); - self.fees.set_base_fee(U256::from(next_block_base_fee)); + self.fees.set_base_fee(next_block_base_fee); // also reset the total difficulty self.blockchain.storage.write().total_difficulty = fork.total_difficulty(); @@ -523,8 +522,8 @@ impl Backend { } /// Returns balance of the given account. - pub async fn current_nonce(&self, address: Address) -> DatabaseResult { - Ok(U256::from(self.get_account(address).await?.nonce)) + pub async fn current_nonce(&self, address: Address) -> DatabaseResult { + Ok(self.get_account(address).await?.nonce) } /// Sets the coinbase address @@ -619,37 +618,37 @@ impl Backend { } /// Returns the block gas limit - pub fn gas_limit(&self) -> U256 { - self.env.read().block.gas_limit + pub fn gas_limit(&self) -> u128 { + self.env.read().block.gas_limit.to() } /// Sets the block gas limit - pub fn set_gas_limit(&self, gas_limit: U256) { - self.env.write().block.gas_limit = gas_limit; + pub fn set_gas_limit(&self, gas_limit: u128) { + self.env.write().block.gas_limit = U256::from(gas_limit); } /// Returns the current base fee - pub fn base_fee(&self) -> U256 { + pub fn base_fee(&self) -> u128 { self.fees.base_fee() } /// Sets the current basefee - pub fn set_base_fee(&self, basefee: U256) { + pub fn set_base_fee(&self, basefee: u128) { self.fees.set_base_fee(basefee) } /// Returns the current gas price - pub fn gas_price(&self) -> U256 { + pub fn gas_price(&self) -> u128 { self.fees.gas_price() } /// Returns the suggested fee cap - pub fn max_priority_fee_per_gas(&self) -> U256 { + pub fn max_priority_fee_per_gas(&self) -> u128 { self.fees.max_priority_fee_per_gas() } /// Sets the gas price - pub fn set_gas_price(&self, price: U256) { + pub fn set_gas_price(&self, price: u128) { self.fees.set_gas_price(price) } @@ -705,17 +704,17 @@ impl Backend { let block = self.block_by_hash(best_block_hash).await?.ok_or(BlockchainError::BlockNotFound)?; - let reset_time = block.header.timestamp.to::(); + let reset_time = block.header.timestamp; self.time.reset(reset_time); let mut env = self.env.write(); env.block = BlockEnv { number: rU256::from(num), - timestamp: block.header.timestamp, + timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, // ensures prevrandao is set prevrandao: Some(block.header.mix_hash.unwrap_or_default()), - gas_limit: block.header.gas_limit, + gas_limit: U256::from(block.header.gas_limit), // Keep previous `coinbase` and `basefee` value coinbase: env.block.coinbase, basefee: env.block.basefee, @@ -795,9 +794,9 @@ impl Backend { fn next_env(&self) -> EnvWithHandlerCfg { let mut env = self.env.read().clone(); // increase block number for this block - env.block.number = env.block.number.saturating_add(rU256::from(1)); - env.block.basefee = self.base_fee(); - env.block.timestamp = rU256::from(self.time.current_call_timestamp()); + env.block.number = env.block.number.saturating_add(U256::from(1)); + env.block.basefee = U256::from(self.base_fee()); + env.block.timestamp = U256::from(self.time.current_call_timestamp()); env } @@ -864,7 +863,7 @@ impl Backend { block_env: env.block.clone(), cfg_env, parent_hash: storage.best_hash, - gas_used: U256::ZERO, + gas_used: 0, enable_steps_tracing: self.enable_steps_tracing, }; @@ -903,7 +902,7 @@ impl Backend { // increase block number for this block env.block.number = env.block.number.saturating_add(rU256::from(1)); - env.block.basefee = current_base_fee; + env.block.basefee = U256::from(current_base_fee); env.block.timestamp = rU256::from(self.time.next_timestamp()); let best_hash = self.blockchain.storage.read().best_hash; @@ -923,13 +922,13 @@ impl Backend { block_env: env.block.clone(), cfg_env: CfgEnvWithHandlerCfg::new(env.cfg.clone(), env.handler_cfg), parent_hash: best_hash, - gas_used: U256::ZERO, + gas_used: 0, enable_steps_tracing: self.enable_steps_tracing, }; let executed_tx = executor.execute(); // we also need to update the new blockhash in the db itself - let block_hash = executed_tx.block.block.header.hash(); + let block_hash = executed_tx.block.block.header.hash_slow(); db.insert_block_hash(U256::from(executed_tx.block.block.header.number), block_hash); (executed_tx, block_hash) @@ -972,7 +971,7 @@ impl Backend { if let Some(contract) = &info.contract_address { node_info!(" Contract created: {contract:?}"); } - node_info!(" Gas used: {}", receipt.gas_used()); + node_info!(" Gas used: {}", receipt.cumulative_gas_used()); if !info.exit.is_ok() { let r = RevertDecoder::new().decode( info.out.as_ref().map(|b| &b[..]).unwrap_or_default(), @@ -1018,16 +1017,16 @@ impl Backend { (outcome, header, block_hash) }; let next_block_base_fee = self.fees.get_next_block_base_fee_per_gas( - U256::from(header.gas_used), - U256::from(header.gas_limit), - U256::from(header.base_fee_per_gas.unwrap_or_default()), + header.gas_used, + header.gas_limit, + header.base_fee_per_gas.unwrap_or_default(), ); // notify all listeners self.notify_on_new_block(header, block_hash); // update next base fee - self.fees.set_base_fee(U256::from(next_block_base_fee)); + self.fees.set_base_fee(next_block_base_fee); outcome } @@ -1039,11 +1038,11 @@ impl Backend { /// Returns an error if the `block_number` is greater than the current height pub async fn call( &self, - request: TransactionRequest, + request: WithOtherFields, fee_details: FeeDetails, block_request: Option, overrides: Option, - ) -> Result<(InstructionResult, Option, u64, State), BlockchainError> { + ) -> Result<(InstructionResult, Option, u128, State), BlockchainError> { self.with_database_at(block_request, |state, block| { let block_number = block.number.to::(); let (exit, out, gas, state) = match overrides { @@ -1060,15 +1059,18 @@ impl Backend { fn build_call_env( &self, - request: TransactionRequest, + request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, ) -> EnvWithHandlerCfg { - let TransactionRequest { from, to, gas, value, input, nonce, access_list, .. } = request; + let WithOtherFields:: { + inner: TransactionRequest { from, to, gas, value, input, nonce, access_list, .. }, + .. + } = request; let FeeDetails { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = fee_details; - let gas_limit = gas.unwrap_or(block_env.gas_limit); + let gas_limit = gas.unwrap_or(block_env.gas_limit.to()); let mut env = self.env.read().clone(); env.block = block_env; // we want to disable this in eth_call, since this is common practice used by other node @@ -1076,7 +1078,7 @@ impl Backend { env.cfg.disable_block_gas_limit = true; if let Some(base) = max_fee_per_gas { - env.block.basefee = base; + env.block.basefee = U256::from(base); } let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| self.gas_price()); @@ -1084,9 +1086,9 @@ impl Backend { env.tx = TxEnv { caller, - gas_limit: gas_limit.to::(), - gas_price, - gas_priority_fee: max_priority_fee_per_gas, + gas_limit: gas_limit as u64, + gas_price: U256::from(gas_price), + gas_priority_fee: max_priority_fee_per_gas.map(U256::from), transact_to: match to { Some(addr) => TransactTo::Call(addr), None => TransactTo::Create(CreateScheme::Create), @@ -1094,7 +1096,7 @@ impl Backend { value: value.unwrap_or_default(), data: input.into_input().unwrap_or_default(), chain_id: None, - nonce: nonce.map(|n| n.to::()), + nonce, access_list: alloy_to_revm_access_list(access_list.unwrap_or_default().0), ..Default::default() }; @@ -1111,10 +1113,10 @@ impl Backend { pub fn call_with_state( &self, state: D, - request: TransactionRequest, + request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, - ) -> Result<(InstructionResult, Option, u64, State), BlockchainError> + ) -> Result<(InstructionResult, Option, u128, State), BlockchainError> where D: DatabaseRef, { @@ -1133,12 +1135,12 @@ impl Backend { ExecutionResult::Halt { reason, gas_used } => (reason.into(), gas_used, None), }; inspector.print_logs(); - Ok((exit_reason, out, gas_used, state)) + Ok((exit_reason, out, gas_used as u128, state)) } pub async fn call_with_tracing( &self, - request: TransactionRequest, + request: WithOtherFields, fee_details: FeeDetails, block_request: Option, opts: GethDefaultTracingOptions, @@ -1171,7 +1173,7 @@ impl Backend { pub fn build_access_list_with_state( &self, state: D, - request: TransactionRequest, + request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, ) -> Result<(InstructionResult, Option, u64, AccessList), BlockchainError> @@ -1244,53 +1246,43 @@ impl Backend { fn mined_logs_for_block(&self, filter: Filter, block: Block) -> Vec { let params = FilteredParams::new(Some(filter.clone())); let mut all_logs = Vec::new(); - let block_hash = block.header.hash(); + let block_hash = block.header.hash_slow(); let mut block_log_index = 0u32; - let transactions: Vec<_> = { - let storage = self.blockchain.storage.read(); - block - .transactions - .iter() - .filter_map(|tx| storage.transactions.get(&tx.hash()).map(|tx| tx.info.clone())) - .collect() - }; + let storage = self.blockchain.storage.read(); - for transaction in transactions { - let logs = transaction.logs.clone(); - let transaction_hash = transaction.transaction_hash; - - for log in logs.into_iter() { - let mut log = Log { - address: log.address, - topics: log.topics().to_vec(), - data: log.data.data, - block_hash: None, - block_number: None, - transaction_hash: None, - transaction_index: None, - log_index: None, - removed: false, - }; + for tx in block.transactions { + let Some(tx) = storage.transactions.get(&tx.hash()) else { + continue; + }; + let logs = tx.receipt.logs(); + let transaction_hash = tx.info.transaction_hash; + + for log in logs { let mut is_match: bool = true; if !filter.address.is_empty() && filter.has_topics() { - if !params.filter_address(&log.address) || !params.filter_topics(&log.topics) { + if !params.filter_address(&log.address) || !params.filter_topics(log.topics()) { is_match = false; } } else if !filter.address.is_empty() { if !params.filter_address(&log.address) { is_match = false; } - } else if filter.has_topics() && !params.filter_topics(&log.topics) { + } else if filter.has_topics() && !params.filter_topics(log.topics()) { is_match = false; } if is_match { - log.block_hash = Some(block_hash); - log.block_number = Some(block.header.number.to_alloy()); - log.transaction_hash = Some(transaction_hash); - log.transaction_index = Some(U256::from(transaction.transaction_index)); - log.log_index = Some(U256::from(block_log_index)); + let log = Log { + inner: log.clone(), + block_hash: Some(block_hash), + block_number: Some(block.header.number), + block_timestamp: Some(block.header.timestamp), + transaction_hash: Some(transaction_hash), + transaction_index: Some(tx.info.transaction_index), + log_index: Some(block_log_index as u64), + removed: false, + }; all_logs.push(log); } block_log_index += 1; @@ -1394,7 +1386,7 @@ impl Backend { pub(crate) async fn mined_transactions_by_block_number( &self, number: BlockNumber, - ) -> Option> { + ) -> Option>> { if let Some(block) = self.get_block(number) { return self.mined_transactions_in_block(&block); } @@ -1402,7 +1394,10 @@ impl Backend { } /// Returns all transactions given a block - pub(crate) fn mined_transactions_in_block(&self, block: &Block) -> Option> { + pub(crate) fn mined_transactions_in_block( + &self, + block: &Block, + ) -> Option>> { let mut transactions = Vec::with_capacity(block.transactions.len()); let base_fee = block.header.base_fee_per_gas; let storage = self.blockchain.storage.read(); @@ -1410,13 +1405,7 @@ impl Backend { let info = storage.transactions.get(&hash)?.info.clone(); let tx = block.transactions.get(info.transaction_index as usize)?.clone(); - let tx = transaction_build( - Some(hash), - tx, - Some(block), - Some(info), - base_fee.map(|f| f.to_alloy()), - ); + let tx = transaction_build(Some(hash), tx, Some(block), Some(info), base_fee); transactions.push(tx); } Some(transactions) @@ -1508,7 +1497,7 @@ impl Backend { let block = self.get_block(id)?; let transactions = self.mined_transactions_in_block(&block)?; let block = self.convert_block(block); - Some(block.into_full_block(transactions)) + Some(block.into_full_block(transactions.into_iter().map(|t| t.inner).collect())) } /// Takes a block as it's stored internally and returns the eth api conform block format @@ -1517,7 +1506,7 @@ impl Backend { let Block { header, transactions, .. } = block; - let hash = header.hash(); + let hash = header.hash_slow(); let Header { parent_hash, ommers_hash, @@ -1550,17 +1539,17 @@ impl Backend { state_root, transactions_root, receipts_root, - number: Some(number.to_alloy()), - gas_used: gas_used.to_alloy(), - gas_limit: gas_limit.to_alloy(), + number: Some(number), + gas_used, + gas_limit, extra_data: extra_data.0.into(), logs_bloom, - timestamp: U256::from(timestamp), + timestamp, total_difficulty: Some(self.total_difficulty()), difficulty, mix_hash: Some(mix_hash), - nonce: Some(B64::from(nonce)), - base_fee_per_gas: base_fee_per_gas.map(|f| f.to_alloy()), + nonce: Some(nonce), + base_fee_per_gas, withdrawals_root: None, blob_gas_used: None, excess_blob_gas: None, @@ -1594,8 +1583,7 @@ impl Backend { .ok_or(BlockchainError::BlockNotFound)? .header .number - .ok_or(BlockchainError::BlockNotFound)? - .to::(), + .ok_or(BlockchainError::BlockNotFound)?, BlockId::Number(num) => match num { BlockNumber::Latest | BlockNumber::Pending => self.best_number(), BlockNumber::Earliest => U64::ZERO.to::(), @@ -1640,11 +1628,11 @@ impl Backend { let block = BlockEnv { number: block.header.number.to_alloy(), coinbase: block.header.beneficiary, - timestamp: rU256::from(block.header.timestamp), + timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, prevrandao: Some(block.header.mix_hash), - basefee: block.header.base_fee_per_gas.unwrap_or_default().to_alloy(), - gas_limit: block.header.gas_limit.to_alloy(), + basefee: U256::from(block.header.base_fee_per_gas.unwrap_or_default()), + gas_limit: U256::from(block.header.gas_limit), ..Default::default() }; f(state, block) @@ -1663,7 +1651,7 @@ impl Backend { if let Some((state, block)) = self .get_block(block_number.to::()) - .and_then(|block| Some((states.get(&block.header.hash())?, block))) + .and_then(|block| Some((states.get(&block.header.hash_slow())?, block))) { let block = BlockEnv { number: block.header.number.to_alloy(), @@ -1671,8 +1659,8 @@ impl Backend { timestamp: rU256::from(block.header.timestamp), difficulty: block.header.difficulty, prevrandao: Some(block.header.mix_hash), - basefee: block.header.base_fee_per_gas.unwrap_or_default().to_alloy(), - gas_limit: block.header.gas_limit.to_alloy(), + basefee: U256::from(block.header.base_fee_per_gas.unwrap_or_default()), + gas_limit: U256::from(block.header.gas_limit), ..Default::default() }; return Ok(f(Box::new(state), block)); @@ -1692,7 +1680,7 @@ impl Backend { block.number = block_number; block.timestamp = rU256::from(fork.timestamp()); - block.basefee = fork.base_fee().unwrap_or_default(); + block.basefee = rU256::from(fork.base_fee().unwrap_or_default()); return Ok(f(Box::new(&gen_db), block)); } @@ -1789,7 +1777,7 @@ impl Backend { &self, address: Address, block_request: BlockRequest, - ) -> Result { + ) -> Result { if let BlockRequest::Pending(pool_transactions) = &block_request { if let Some(value) = get_pool_transactions_nonce(pool_transactions, address) { return Ok(value); @@ -1802,7 +1790,7 @@ impl Backend { self.with_database_at(Some(final_block_request), |db, _| { trace!(target: "backend", "get nonce for {:?}", address); - Ok(U256::from(db.basic_ref(address)?.unwrap_or_default().nonce)) + Ok(db.basic_ref(address)?.unwrap_or_default().nonce) }) .await? } @@ -1897,7 +1885,7 @@ impl Backend { pub async fn transaction_receipt( &self, hash: B256, - ) -> Result, BlockchainError> { + ) -> Result, BlockchainError> { if let Some(receipt) = self.mined_transaction_receipt(hash) { return Ok(Some(receipt.inner)); } @@ -1905,10 +1893,7 @@ impl Backend { if let Some(fork) = self.get_fork() { let receipt = fork.transaction_receipt(hash).await?; let number = self.convert_block_number( - receipt - .clone() - .and_then(|r| r.block_number) - .map(|n| BlockNumber::from(n.to::())), + receipt.clone().and_then(|r| r.block_number).map(BlockNumber::from), ); if fork.predates_fork_inclusive(number) { @@ -1932,7 +1917,7 @@ impl Backend { } /// Returns all transaction receipts of the block - pub fn mined_block_receipts(&self, id: impl Into) -> Option> { + pub fn mined_block_receipts(&self, id: impl Into) -> Option> { let mut receipts = Vec::new(); let block = self.get_block(id)?; @@ -1946,104 +1931,83 @@ impl Backend { /// Returns the transaction receipt for the given hash pub(crate) fn mined_transaction_receipt(&self, hash: B256) -> Option { - let MinedTransaction { info, receipt, block_hash, .. } = + let MinedTransaction { info, receipt: tx_receipt, block_hash, .. } = self.blockchain.get_transaction_by_hash(&hash)?; - let ReceiptWithBloom { receipt, bloom } = receipt.into(); - let Receipt { success, cumulative_gas_used: _, logs } = receipt; - let logs_bloom = bloom; - let index = info.transaction_index as usize; - let block = self.blockchain.get_block_by_hash(&block_hash)?; - - // TODO store cumulative gas used in receipt instead - let receipts = self.get_receipts(block.transactions.iter().map(|tx| tx.hash())); - - let mut cumulative_gas_used = U256::ZERO; - for receipt in receipts.iter().take(index + 1) { - cumulative_gas_used = cumulative_gas_used.saturating_add(receipt.gas_used()); - } - - // cumulative_gas_used = cumulative_gas_used.saturating_sub(gas_used); - - let mut cumulative_receipts = receipts; - cumulative_receipts.truncate(index + 1); - let transaction = block.transactions[index].clone(); - let transaction_type = transaction.transaction.r#type(); - let effective_gas_price = match transaction.transaction { - TypedTransaction::Legacy(t) => t.gas_price, - TypedTransaction::EIP2930(t) => t.gas_price, + TypedTransaction::Legacy(t) => t.tx().gas_price, + TypedTransaction::EIP2930(t) => t.tx().gas_price, TypedTransaction::EIP1559(t) => block .header .base_fee_per_gas - .map_or(self.base_fee().to::(), |b| b as u128) - .checked_add(t.max_priority_fee_per_gas) - .unwrap_or(u128::MAX), + .unwrap_or_else(|| self.base_fee()) + .saturating_add(t.tx().max_priority_fee_per_gas), TypedTransaction::EIP4844(t) => block .header .base_fee_per_gas - .map_or(self.base_fee().to::(), |b| b as u128) - .checked_add(t.tx().tx().max_priority_fee_per_gas) - .unwrap_or(u128::MAX), + .unwrap_or_else(|| self.base_fee()) + .saturating_add(t.tx().tx().max_priority_fee_per_gas), TypedTransaction::Deposit(_) => 0_u128, }; - let deposit_nonce = transaction_type.and_then(|x| (x == 0x7E).then_some(info.nonce)); + let receipts = self.get_receipts(block.transactions.iter().map(|tx| tx.hash())); + let next_log_index = receipts[..index].iter().map(|r| r.logs().len()).sum::(); + + let receipt = tx_receipt.as_receipt_with_bloom().receipt.clone(); + let receipt = Receipt { + status: receipt.status, + cumulative_gas_used: receipt.cumulative_gas_used, + logs: receipt + .logs + .into_iter() + .enumerate() + .map(|(index, log)| alloy_rpc_types::Log { + inner: log, + block_hash: Some(block_hash), + block_number: Some(block.header.number), + block_timestamp: Some(block.header.timestamp), + transaction_hash: Some(info.transaction_hash), + transaction_index: Some(info.transaction_index), + log_index: Some((next_log_index + index) as u64), + removed: false, + }) + .collect(), + }; + let receipt_with_bloom = + ReceiptWithBloom { receipt, logs_bloom: tx_receipt.as_receipt_with_bloom().logs_bloom }; + + let inner = match tx_receipt { + TypedReceipt::EIP1559(_) => TypedReceipt::EIP1559(receipt_with_bloom), + TypedReceipt::Legacy(_) => TypedReceipt::Legacy(receipt_with_bloom), + TypedReceipt::EIP2930(_) => TypedReceipt::EIP2930(receipt_with_bloom), + TypedReceipt::EIP4844(_) => TypedReceipt::EIP4844(receipt_with_bloom), + TypedReceipt::Deposit(r) => TypedReceipt::Deposit(DepositReceipt { + inner: receipt_with_bloom, + deposit_nonce: r.deposit_nonce, + deposit_nonce_version: r.deposit_nonce_version, + }), + }; - let mut inner = TransactionReceipt { - transaction_hash: Some(info.transaction_hash), - transaction_index: U64::from(info.transaction_index), + let inner = TransactionReceipt { + inner, + transaction_hash: info.transaction_hash, + transaction_index: info.transaction_index, + block_number: Some(block.header.number), + gas_used: info.gas_used, + contract_address: info.contract_address, + effective_gas_price, block_hash: Some(block_hash), - block_number: Some(U256::from(block.header.number)), from: info.from, to: info.to, - cumulative_gas_used, - gas_used: Some(cumulative_gas_used), - contract_address: info.contract_address, - logs: { - let mut pre_receipts_log_index = None; - if !cumulative_receipts.is_empty() { - cumulative_receipts.truncate(cumulative_receipts.len() - 1); - pre_receipts_log_index = Some( - cumulative_receipts.iter().map(|r| r.logs().len() as u32).sum::(), - ); - } - logs.iter() - .enumerate() - .map(|(i, log)| Log { - address: log.address, - topics: log.topics().to_vec(), - data: log.data.data.clone(), - block_hash: Some(block_hash), - block_number: Some(U256::from(block.header.number)), - transaction_hash: Some(info.transaction_hash), - transaction_index: Some(U256::from(info.transaction_index)), - log_index: Some(U256::from( - (pre_receipts_log_index.unwrap_or(0)) + i as u32, - )), - removed: false, - }) - .collect() - }, - status_code: Some(U64::from(success)), - state_root: None, - logs_bloom, - transaction_type: transaction_type.map(U8::from).unwrap_or_default(), - effective_gas_price: U128::from(effective_gas_price), + state_root: Some(block.header.state_root), blob_gas_price: None, blob_gas_used: None, - other: Default::default(), }; - inner.other.insert( - "depositNonce".to_string(), - serde_json::to_value(deposit_nonce).expect("Infallible"), - ); - Some(MinedTransactionReceipt { inner, out: info.out.map(|o| o.0.into()) }) } @@ -2051,7 +2015,7 @@ impl Backend { pub async fn block_receipts( &self, number: BlockNumber, - ) -> Result>, BlockchainError> { + ) -> Result>, BlockchainError> { if let Some(receipts) = self.mined_block_receipts(number) { return Ok(Some(receipts)); } @@ -2060,10 +2024,7 @@ impl Backend { let number = self.convert_block_number(Some(number)); if fork.predates_fork_inclusive(number) { - let receipts = fork - .block_receipts(number) - .await - .map_err(BlockchainError::AlloyForkProvider)?; + let receipts = fork.block_receipts(number).await?; return Ok(receipts); } @@ -2076,7 +2037,7 @@ impl Backend { &self, number: BlockNumber, index: Index, - ) -> Result, BlockchainError> { + ) -> Result>, BlockchainError> { if let Some(hash) = self.mined_block_by_number(number).and_then(|b| b.header.hash) { return Ok(self.mined_transaction_by_block_hash_and_index(hash, index)); } @@ -2095,7 +2056,7 @@ impl Backend { &self, hash: B256, index: Index, - ) -> Result, BlockchainError> { + ) -> Result>, BlockchainError> { if let tx @ Some(_) = self.mined_transaction_by_block_hash_and_index(hash, index) { return Ok(tx); } @@ -2111,7 +2072,7 @@ impl Backend { &self, block_hash: B256, index: Index, - ) -> Option { + ) -> Option> { let (info, block, tx) = { let storage = self.blockchain.storage.read(); let block = storage.blocks.get(&block_hash).cloned()?; @@ -2126,14 +2087,14 @@ impl Backend { tx, Some(&block), Some(info), - block.header.base_fee_per_gas.map(|g| g.to_alloy()), + block.header.base_fee_per_gas, )) } pub async fn transaction_by_hash( &self, hash: B256, - ) -> Result, BlockchainError> { + ) -> Result>, BlockchainError> { trace!(target: "backend", "transaction_by_hash={:?}", hash); if let tx @ Some(_) = self.mined_transaction_by_hash(hash) { return Ok(tx); @@ -2146,7 +2107,7 @@ impl Backend { Ok(None) } - fn mined_transaction_by_hash(&self, hash: B256) -> Option { + fn mined_transaction_by_hash(&self, hash: B256) -> Option> { let (info, block) = { let storage = self.blockchain.storage.read(); let MinedTransaction { info, block_hash, .. } = @@ -2161,7 +2122,7 @@ impl Backend { tx, Some(&block), Some(info), - block.header.base_fee_per_gas.map(|g| g.to_alloy()), + block.header.base_fee_per_gas, )) } @@ -2242,14 +2203,14 @@ impl Backend { fn get_pool_transactions_nonce( pool_transactions: &[Arc], address: Address, -) -> Option { +) -> Option { if let Some(highest_nonce) = pool_transactions .iter() .filter(|tx| *tx.pending_transaction.sender() == address) .map(|tx| tx.pending_transaction.nonce()) .max() { - let tx_count = highest_nonce.saturating_add(U256::from(1)); + let tx_count = highest_nonce.saturating_add(1); return Some(tx_count) } None @@ -2299,7 +2260,7 @@ impl TransactionValidator for Backend { } // Check gas limit, iff block gas limit is set. - if !env.cfg.disable_block_gas_limit && tx.gas_limit() > env.block.gas_limit { + if !env.cfg.disable_block_gas_limit && tx.gas_limit() > env.block.gas_limit.to() { warn!(target: "backend", "[{:?}] gas too high", tx.hash()); return Err(InvalidTransactionError::GasTooHigh(ErrDetail { detail: String::from("tx.gas_limit > env.block.gas_limit"), @@ -2309,15 +2270,14 @@ impl TransactionValidator for Backend { // check nonce let is_deposit_tx = matches!(&pending.transaction.transaction, TypedTransaction::Deposit(_)); - let nonce: u64 = - tx.nonce().try_into().map_err(|_| InvalidTransactionError::NonceMaxValue)?; + let nonce = tx.nonce(); if nonce < account.nonce && !is_deposit_tx { warn!(target: "backend", "[{:?}] nonce too low", tx.hash()); return Err(InvalidTransactionError::NonceTooLow); } if (env.handler_cfg.spec_id as u8) >= (SpecId::LONDON as u8) { - if tx.gas_price() < env.block.basefee && !is_deposit_tx { + if tx.gas_price() < env.block.basefee.to() && !is_deposit_tx { warn!(target: "backend", "max fee per gas={}, too low, block basefee={}",tx.gas_price(), env.block.basefee); return Err(InvalidTransactionError::FeeCapTooLow); } @@ -2335,12 +2295,12 @@ impl TransactionValidator for Backend { let max_cost = tx.max_cost(); let value = tx.value(); // check sufficient funds: `gas * price + value` - let req_funds = max_cost.checked_add(value).ok_or_else(|| { + let req_funds = max_cost.checked_add(value.to()).ok_or_else(|| { warn!(target: "backend", "[{:?}] cost too high", tx.hash()); InvalidTransactionError::InsufficientFunds })?; - if account.balance < req_funds { + if account.balance < U256::from(req_funds) { warn!(target: "backend", "[{:?}] insufficient allowance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender()); return Err(InvalidTransactionError::InsufficientFunds); } @@ -2354,7 +2314,7 @@ impl TransactionValidator for Backend { env: &EnvWithHandlerCfg, ) -> Result<(), InvalidTransactionError> { self.validate_pool_transaction_for(tx, account, env)?; - if tx.nonce().to::() > account.nonce { + if tx.nonce() > account.nonce { return Err(InvalidTransactionError::NonceTooHigh); } Ok(()) @@ -2368,11 +2328,11 @@ pub fn transaction_build( eth_transaction: MaybeImpersonatedTransaction, block: Option<&Block>, info: Option, - base_fee: Option, -) -> Transaction { + base_fee: Option, +) -> WithOtherFields { let mut transaction: Transaction = eth_transaction.clone().into(); - if info.is_some() && transaction.transaction_type.unwrap_or(U64::ZERO).to::() == 0x7E { - transaction.nonce = U64::from(info.as_ref().unwrap().nonce); + if info.is_some() && transaction.transaction_type == Some(0x7E) { + transaction.nonce = info.as_ref().unwrap().nonce; } if eth_transaction.is_dynamic_fee() { @@ -2382,12 +2342,9 @@ pub fn transaction_build( } else { // if transaction is already mined, gas price is considered base fee + priority fee: the // effective gas price. - let base_fee = base_fee.unwrap_or(U256::ZERO); - let max_priority_fee_per_gas = - transaction.max_priority_fee_per_gas.map(|g| g.to::()).unwrap_or(U256::ZERO); - transaction.gas_price = Some( - base_fee.checked_add(max_priority_fee_per_gas).unwrap_or(U256::MAX).to::(), - ); + let base_fee = base_fee.unwrap_or(0); + let max_priority_fee_per_gas = transaction.max_priority_fee_per_gas.unwrap_or(0); + transaction.gas_price = Some(base_fee.saturating_add(max_priority_fee_per_gas)); } } else { transaction.max_fee_per_gas = None; @@ -2397,10 +2354,9 @@ pub fn transaction_build( transaction.block_hash = block.as_ref().map(|block| B256::from(keccak256(alloy_rlp::encode(&block.header)))); - transaction.block_number = block.as_ref().map(|block| U256::from(block.header.number)); + transaction.block_number = block.as_ref().map(|block| block.header.number); - transaction.transaction_index = - info.as_ref().map(|status| U256::from(status.transaction_index)); + transaction.transaction_index = info.as_ref().map(|info| info.transaction_index); // need to check if the signature of the transaction is impersonated, if so then we // can't recover the sender, instead we use the sender from the executed transaction and set the @@ -2422,7 +2378,7 @@ pub fn transaction_build( } transaction.to = info.as_ref().map_or(eth_transaction.to(), |status| status.to); - transaction + WithOtherFields::new(transaction) } /// Prove a storage key's existence or nonexistence in the account's storage diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 8d72b30f1299..434490de575a 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -6,18 +6,15 @@ use crate::eth::{ }, pool::transactions::PoolTransaction, }; -use alloy_network::Sealable; use alloy_primitives::{Bytes, TxHash, B256, U256, U64}; -use alloy_rpc_trace_types::{ +use alloy_rpc_types::{BlockId, BlockNumberOrTag, TransactionInfo as RethTransactionInfo}; +use alloy_rpc_types_trace::{ geth::{DefaultFrame, GethDefaultTracingOptions}, parity::LocalizedTransactionTrace, }; -use alloy_rpc_types::{ - BlockId, BlockNumberOrTag, TransactionInfo as RethTransactionInfo, TransactionReceipt, -}; use anvil_core::eth::{ block::{Block, PartialHeader}, - transaction::{MaybeImpersonatedTransaction, TransactionInfo, TypedReceipt}, + transaction::{MaybeImpersonatedTransaction, ReceiptResponse, TransactionInfo, TypedReceipt}, }; use foundry_evm::{ revm::primitives::Env, @@ -228,18 +225,18 @@ pub struct BlockchainStorage { impl BlockchainStorage { /// Creates a new storage with a genesis block - pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { + pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { // create a dummy genesis block let partial_header = PartialHeader { timestamp, - base_fee: base_fee.map(|b| b.to::()), - gas_limit: env.block.gas_limit.to::(), + base_fee, + gas_limit: env.block.gas_limit.to::(), beneficiary: env.block.coinbase, difficulty: env.block.difficulty, ..Default::default() }; let block = Block::new::(partial_header, vec![], vec![]); - let genesis_hash = block.header.hash(); + let genesis_hash = block.header.hash_slow(); let best_hash = genesis_hash; let best_number: U64 = U64::from(0u64); @@ -339,7 +336,7 @@ pub struct Blockchain { impl Blockchain { /// Creates a new storage with a genesis block - pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { + pub fn new(env: &Env, base_fee: Option, timestamp: u64) -> Self { Self { storage: Arc::new(RwLock::new(BlockchainStorage::new(env, base_fee, timestamp))) } } @@ -408,7 +405,7 @@ impl MinedTransaction { ) .into_localized_transaction_traces(RethTransactionInfo { hash: Some(self.info.transaction_hash), - index: Some(self.info.transaction_index as u64), + index: Some(self.info.transaction_index), block_hash: Some(self.block_hash), block_number: Some(self.block_number), base_fee: None, @@ -418,7 +415,7 @@ impl MinedTransaction { pub fn geth_trace(&self, opts: GethDefaultTracingOptions) -> DefaultFrame { GethTraceBuilder::new(self.info.traces.clone(), TracingInspectorConfig::default_geth()) .geth_traces( - self.receipt.gas_used().to::(), + self.receipt.cumulative_gas_used() as u64, self.info.out.clone().unwrap_or_default().0.into(), opts, ) @@ -429,7 +426,7 @@ impl MinedTransaction { #[derive(Clone, Debug)] pub struct MinedTransactionReceipt { /// The actual json rpc receipt object - pub inner: TransactionReceipt, + pub inner: ReceiptResponse, /// Output data fo the transaction pub out: Option, } diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 57436503965d..9bd3d64a4bee 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -1,7 +1,7 @@ //! Aggregated error type for this module use crate::eth::pool::transactions::PoolTransaction; -use alloy_primitives::{Bytes, SignatureError as AlloySignatureError, U256}; +use alloy_primitives::{Bytes, SignatureError as AlloySignatureError}; use alloy_signer::Error as AlloySignerError; use alloy_transport::TransportError; use anvil_rpc::{ @@ -37,6 +37,8 @@ pub enum BlockchainError { FailedToDecodeSignedTransaction, #[error("Failed to decode transaction")] FailedToDecodeTransaction, + #[error("Failed to decode receipt")] + FailedToDecodeReceipt, #[error("Failed to decode state")] FailedToDecodeStateDump, #[error("Prevrandao not in th EVM's environment after merge")] @@ -180,7 +182,7 @@ pub enum InvalidTransactionError { FeeCapTooLow, /// Thrown during estimate if caller has insufficient funds to cover the tx. #[error("Out of gas: gas required exceeds allowance: {0:?}")] - BasicOutOfGas(U256), + BasicOutOfGas(u128), /// Thrown if executing a transaction failed during estimate/call #[error("execution reverted: {0:?}")] Revert(Option), @@ -348,6 +350,9 @@ impl ToRpcResponseResult for Result { BlockchainError::FailedToDecodeTransaction => { RpcError::invalid_params("Failed to decode transaction") } + BlockchainError::FailedToDecodeReceipt => { + RpcError::invalid_params("Failed to decode receipt") + } BlockchainError::FailedToDecodeStateDump => { RpcError::invalid_params("Failed to decode state dump") } diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index ef2d38eca87f..2b070b281d75 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -2,7 +2,7 @@ use crate::eth::{ backend::{info::StorageInfo, notifications::NewBlockNotifications}, error::BlockchainError, }; -use alloy_primitives::{B256, U256}; +use alloy_primitives::B256; use anvil_core::eth::transaction::TypedTransaction; use foundry_evm::revm::primitives::SpecId; use futures::StreamExt; @@ -20,16 +20,16 @@ use std::{ pub const MAX_FEE_HISTORY_CACHE_SIZE: u64 = 2048u64; /// Initial base fee for EIP-1559 blocks. -pub const INITIAL_BASE_FEE: u64 = 1_000_000_000; +pub const INITIAL_BASE_FEE: u128 = 1_000_000_000; /// Initial default gas price for the first block -pub const INITIAL_GAS_PRICE: u64 = 1_875_000_000; +pub const INITIAL_GAS_PRICE: u128 = 1_875_000_000; /// Bounds the amount the base fee can change between blocks. -pub const BASE_FEE_CHANGE_DENOMINATOR: u64 = 8; +pub const BASE_FEE_CHANGE_DENOMINATOR: u128 = 8; /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) -pub const EIP1559_ELASTICITY_MULTIPLIER: u64 = 2; +pub const EIP1559_ELASTICITY_MULTIPLIER: u128 = 2; pub fn default_elasticity() -> f64 { 1f64 / BASE_FEE_CHANGE_DENOMINATOR as f64 @@ -43,18 +43,18 @@ pub struct FeeManager { /// Tracks the base fee for the next block post London /// /// This value will be updated after a new block was mined - base_fee: Arc>, + base_fee: Arc>, /// The base price to use Pre London /// /// This will be constant value unless changed manually - gas_price: Arc>, + gas_price: Arc>, elasticity: Arc>, } // === impl FeeManager === impl FeeManager { - pub fn new(spec_id: SpecId, base_fee: U256, gas_price: U256) -> Self { + pub fn new(spec_id: SpecId, base_fee: u128, gas_price: u128) -> Self { Self { spec_id, base_fee: Arc::new(RwLock::new(base_fee)), @@ -73,7 +73,7 @@ impl FeeManager { } /// Calculates the current gas price - pub fn gas_price(&self) -> U256 { + pub fn gas_price(&self) -> u128 { if self.is_eip1559() { self.base_fee().saturating_add(self.suggested_priority_fee()) } else { @@ -82,33 +82,33 @@ impl FeeManager { } /// Suggested priority fee to add to the base fee - pub fn suggested_priority_fee(&self) -> U256 { - U256::from(1e9 as u64) + pub fn suggested_priority_fee(&self) -> u128 { + 1e9 as u128 } - pub fn base_fee(&self) -> U256 { + pub fn base_fee(&self) -> u128 { if self.is_eip1559() { *self.base_fee.read() } else { - U256::ZERO + 0 } } /// Returns the suggested fee cap /// /// Note: This currently returns a constant value: [Self::suggested_priority_fee] - pub fn max_priority_fee_per_gas(&self) -> U256 { + pub fn max_priority_fee_per_gas(&self) -> u128 { self.suggested_priority_fee() } /// Returns the current gas price - pub fn set_gas_price(&self, price: U256) { + pub fn set_gas_price(&self, price: u128) { let mut gas = self.gas_price.write(); *gas = price; } /// Returns the current base fee - pub fn set_base_fee(&self, fee: U256) { + pub fn set_base_fee(&self, fee: u128) { trace!(target: "backend::fees", "updated base fee {:?}", fee); let mut base = self.base_fee.write(); *base = fee; @@ -117,26 +117,22 @@ impl FeeManager { /// Calculates the base fee for the next block pub fn get_next_block_base_fee_per_gas( &self, - gas_used: U256, - gas_limit: U256, - last_fee_per_gas: U256, - ) -> u64 { + gas_used: u128, + gas_limit: u128, + last_fee_per_gas: u128, + ) -> u128 { // It's naturally impossible for base fee to be 0; // It means it was set by the user deliberately and therefore we treat it as a constant. // Therefore, we skip the base fee calculation altogether and we return 0. - if self.base_fee().is_zero() { + if self.base_fee() == 0 { return 0 } - calculate_next_block_base_fee( - gas_used.to::(), - gas_limit.to::(), - last_fee_per_gas.to::(), - ) + calculate_next_block_base_fee(gas_used, gas_limit, last_fee_per_gas) } } /// Calculate base fee for next block. [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) spec -pub fn calculate_next_block_base_fee(gas_used: u64, gas_limit: u64, base_fee: u64) -> u64 { +pub fn calculate_next_block_base_fee(gas_used: u128, gas_limit: u128, base_fee: u128) -> u128 { let gas_target = gas_limit / EIP1559_ELASTICITY_MULTIPLIER; if gas_used == gas_target { @@ -144,20 +140,15 @@ pub fn calculate_next_block_base_fee(gas_used: u64, gas_limit: u64, base_fee: u6 } if gas_used > gas_target { let gas_used_delta = gas_used - gas_target; - let base_fee_delta = std::cmp::max( - 1, - base_fee as u128 * gas_used_delta as u128 / - gas_target as u128 / - BASE_FEE_CHANGE_DENOMINATOR as u128, - ); - base_fee + (base_fee_delta as u64) + let base_fee_delta = + std::cmp::max(1, base_fee * gas_used_delta / gas_target / BASE_FEE_CHANGE_DENOMINATOR); + base_fee + base_fee_delta } else { let gas_used_delta = gas_target - gas_used; - let base_fee_per_gas_delta = base_fee as u128 * gas_used_delta as u128 / - gas_target as u128 / - BASE_FEE_CHANGE_DENOMINATOR as u128; + let base_fee_per_gas_delta = + base_fee * gas_used_delta / gas_target / BASE_FEE_CHANGE_DENOMINATOR; - base_fee.saturating_sub(base_fee_per_gas_delta as u64) + base_fee.saturating_sub(base_fee_per_gas_delta) } } @@ -222,11 +213,7 @@ impl FeeHistoryService { let mut block_number: Option = None; let base_fee = self.fees.base_fee(); - let mut item = FeeHistoryCacheItem { - base_fee: base_fee.to::(), - gas_used_ratio: 0f64, - rewards: Vec::new(), - }; + let mut item = FeeHistoryCacheItem { base_fee, gas_used_ratio: 0f64, rewards: Vec::new() }; let current_block = self.storage_info.block(hash); let current_receipts = self.storage_info.receipts(hash); @@ -241,38 +228,34 @@ impl FeeHistoryService { item.gas_used_ratio = gas_used / (gas_target * elasticity); // extract useful tx info (gas_used, effective_reward) - let mut transactions: Vec<(u64, u64)> = receipts + let mut transactions: Vec<(u128, u128)> = receipts .iter() .enumerate() .map(|(i, receipt)| { - let gas_used = receipt.gas_used(); + let gas_used = receipt.cumulative_gas_used(); let effective_reward = match block.transactions.get(i).map(|tx| &tx.transaction) { Some(TypedTransaction::Legacy(t)) => { - U256::from(t.gas_price).saturating_sub(base_fee).to::() + t.tx().gas_price.saturating_sub(base_fee) } Some(TypedTransaction::EIP2930(t)) => { - U256::from(t.gas_price).saturating_sub(base_fee).to::() - } - Some(TypedTransaction::EIP1559(t)) => { - U256::from(t.max_priority_fee_per_gas) - .min(U256::from(t.max_fee_per_gas).saturating_sub(base_fee)) - .to::() + t.tx().gas_price.saturating_sub(base_fee) } + Some(TypedTransaction::EIP1559(t)) => t + .tx() + .max_priority_fee_per_gas + .min(t.tx().max_fee_per_gas.saturating_sub(base_fee)), // TODO: This probably needs to be extended to extract 4844 info. - Some(TypedTransaction::EIP4844(t)) => { - U256::from(t.tx().tx().max_priority_fee_per_gas) - .min( - U256::from(t.tx().tx().max_fee_per_gas) - .saturating_sub(base_fee), - ) - .to::() - } + Some(TypedTransaction::EIP4844(t)) => t + .tx() + .tx() + .max_priority_fee_per_gas + .min(t.tx().tx().max_fee_per_gas.saturating_sub(base_fee)), Some(TypedTransaction::Deposit(_)) => 0, None => 0, }; - (gas_used.to::(), effective_reward) + (gas_used, effective_reward) }) .collect(); @@ -283,7 +266,7 @@ impl FeeHistoryService { item.rewards = reward_percentiles .into_iter() .filter_map(|p| { - let target_gas = (p * gas_used / 100f64) as u64; + let target_gas = (p * gas_used / 100f64) as u128; let mut sum_gas = 0; for (gas_used, effective_reward) in transactions.iter().cloned() { sum_gas += gas_used; @@ -341,26 +324,22 @@ pub type FeeHistoryCache = Arc>>; /// A single item in the whole fee history cache #[derive(Clone, Debug)] pub struct FeeHistoryCacheItem { - pub base_fee: u64, + pub base_fee: u128, pub gas_used_ratio: f64, - pub rewards: Vec, + pub rewards: Vec, } #[derive(Clone, Default)] pub struct FeeDetails { - pub gas_price: Option, - pub max_fee_per_gas: Option, - pub max_priority_fee_per_gas: Option, + pub gas_price: Option, + pub max_fee_per_gas: Option, + pub max_priority_fee_per_gas: Option, } impl FeeDetails { /// All values zero pub fn zero() -> Self { - Self { - gas_price: Some(U256::ZERO), - max_fee_per_gas: Some(U256::ZERO), - max_priority_fee_per_gas: Some(U256::ZERO), - } + Self { gas_price: Some(0), max_fee_per_gas: Some(0), max_priority_fee_per_gas: Some(0) } } /// If neither `gas_price` nor `max_fee_per_gas` is `Some`, this will set both to `0` @@ -368,23 +347,23 @@ impl FeeDetails { let FeeDetails { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = self; let no_fees = gas_price.is_none() && max_fee_per_gas.is_none(); - let gas_price = if no_fees { Some(U256::ZERO) } else { gas_price }; - let max_fee_per_gas = if no_fees { Some(U256::ZERO) } else { max_fee_per_gas }; + let gas_price = if no_fees { Some(0) } else { gas_price }; + let max_fee_per_gas = if no_fees { Some(0) } else { max_fee_per_gas }; Self { gas_price, max_fee_per_gas, max_priority_fee_per_gas } } /// Turns this type into a tuple - pub fn split(self) -> (Option, Option, Option) { + pub fn split(self) -> (Option, Option, Option) { let Self { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = self; (gas_price, max_fee_per_gas, max_priority_fee_per_gas) } /// Creates a new instance from the request's gas related values pub fn new( - request_gas_price: Option, - request_max_fee: Option, - request_priority: Option, + request_gas_price: Option, + request_max_fee: Option, + request_priority: Option, ) -> Result { match (request_gas_price, request_max_fee, request_priority) { (gas_price, None, None) => { diff --git a/crates/anvil/src/eth/otterscan/api.rs b/crates/anvil/src/eth/otterscan/api.rs index ac092f36c7f7..636140f13ffe 100644 --- a/crates/anvil/src/eth/otterscan/api.rs +++ b/crates/anvil/src/eth/otterscan/api.rs @@ -7,11 +7,13 @@ use crate::eth::{ macros::node_info, EthApi, }; -use alloy_primitives::{Address, Bytes, B256, U256, U64}; -use alloy_rpc_trace_types::parity::{ +use alloy_primitives::{Address, Bytes, B256, U256}; +use alloy_rpc_types::{ + Block, BlockId, BlockNumberOrTag as BlockNumber, Transaction, WithOtherFields, +}; +use alloy_rpc_types_trace::parity::{ Action, CallAction, CreateAction, CreateOutput, RewardAction, TraceOutput, }; -use alloy_rpc_types::{Block, BlockId, BlockNumberOrTag as BlockNumber, Transaction}; use itertools::Itertools; impl EthApi { @@ -68,7 +70,7 @@ impl EthApi { node_info!("ots_getTransactionError"); if let Some(receipt) = self.backend.mined_transaction_receipt(hash) { - if receipt.inner.status_code == Some(U64::ZERO) { + if !receipt.inner.inner.as_receipt_with_bloom().receipt.status { return Ok(receipt.out.map(|b| b.0.into())) } } @@ -238,7 +240,7 @@ impl EthApi { &self, address: Address, nonce: U256, - ) -> Result> { + ) -> Result>> { node_info!("ots_getTransactionBySenderAndNonce"); let from = self.get_fork().map(|f| f.block_number() + 1).unwrap_or_default(); diff --git a/crates/anvil/src/eth/otterscan/types.rs b/crates/anvil/src/eth/otterscan/types.rs index ff1f99ce5d13..d7e75c02c7e4 100644 --- a/crates/anvil/src/eth/otterscan/types.rs +++ b/crates/anvil/src/eth/otterscan/types.rs @@ -3,8 +3,9 @@ use crate::eth::{ error::{BlockchainError, Result}, }; use alloy_primitives::{Address, Bytes, B256, U256 as rU256, U256}; -use alloy_rpc_trace_types::parity::{Action, CallType, LocalizedTransactionTrace}; -use alloy_rpc_types::{Block, BlockTransactions, Transaction, TransactionReceipt}; +use alloy_rpc_types::{Block, BlockTransactions, Transaction, WithOtherFields}; +use alloy_rpc_types_trace::parity::{Action, CallType, LocalizedTransactionTrace}; +use anvil_core::eth::transaction::ReceiptResponse; use foundry_evm::{revm::interpreter::InstructionResult, traces::CallKind}; use futures::future::join_all; use serde::Serialize; @@ -40,7 +41,7 @@ pub struct Issuance { #[derive(Clone, Serialize, Debug)] pub struct OtsBlockTransactions { pub fullblock: OtsBlock, - pub receipts: Vec, + pub receipts: Vec, } /// Patched Receipt struct, to include the additional `timestamp` field expected by Otterscan @@ -48,7 +49,7 @@ pub struct OtsBlockTransactions { #[serde(rename_all = "camelCase")] pub struct OtsTransactionReceipt { #[serde(flatten)] - receipt: TransactionReceipt, + receipt: ReceiptResponse, timestamp: u64, } @@ -63,7 +64,7 @@ pub struct OtsContractCreator { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct OtsSearchTransactions { - pub txs: Vec, + pub txs: Vec>, pub receipts: Vec, pub first_page: bool, pub last_page: bool, @@ -132,22 +133,22 @@ impl OtsBlockDetails { block_txs.iter().map(|tx| async { backend.transaction_receipt(*tx).await }); // fetch all receipts - let receipts: Vec = join_all(receipts_futs) + let receipts = join_all(receipts_futs) .await .into_iter() .map(|r| match r { Ok(Some(r)) => Ok(r), _ => Err(BlockchainError::DataUnavailable), }) - .collect::>()?; + .collect::>>()?; - let total_fees = receipts.iter().fold(U256::ZERO, |acc, receipt| { - acc + receipt.gas_used.unwrap() * (U256::from(receipt.effective_gas_price)) - }); + let total_fees = receipts + .iter() + .fold(0, |acc, receipt| acc + receipt.gas_used * receipt.effective_gas_price); Ok(Self { block: block.into(), - total_fees, + total_fees: U256::from(total_fees), // issuance has no meaningful value in anvil's backend. just default to 0 issuance: Default::default(), }) @@ -198,7 +199,7 @@ impl OtsBlockTransactions { let receipt_futs = block_txs.iter().map(|tx| async { backend.transaction_receipt(*tx).await }); - let receipts: Vec = join_all(receipt_futs) + let receipts = join_all(receipt_futs) .await .into_iter() .map(|r| match r { @@ -225,7 +226,7 @@ impl OtsSearchTransactions { ) -> Result { let txs_futs = hashes.iter().map(|hash| async { backend.transaction_by_hash(*hash).await }); - let txs: Vec = join_all(txs_futs) + let txs: Vec<_> = join_all(txs_futs) .await .into_iter() .map(|t| match t { @@ -237,11 +238,8 @@ impl OtsSearchTransactions { join_all(hashes.iter().map(|hash| async { match backend.transaction_receipt(*hash).await { Ok(Some(receipt)) => { - let timestamp = backend - .get_block(receipt.block_number.unwrap().to::()) - .unwrap() - .header - .timestamp; + let timestamp = + backend.get_block(receipt.block_number.unwrap()).unwrap().header.timestamp; Ok(OtsTransactionReceipt { receipt, timestamp }) } Ok(None) => Err(BlockchainError::DataUnavailable), diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index 312bde4818bf..a88bc369cd94 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -1,5 +1,5 @@ use crate::eth::{error::PoolError, util::hex_fmt_many}; -use alloy_primitives::{Address, TxHash, U256}; +use alloy_primitives::{Address, TxHash}; use anvil_core::eth::transaction::{PendingTransaction, TypedTransaction}; use parking_lot::RwLock; use std::{ @@ -65,10 +65,10 @@ impl FromStr for TransactionOrder { /// Metric value for the priority of a transaction. /// -/// The `TransactionPriority` determines the ordering of two transactions that have all their +/// The `TransactionPriority` determines the ordering of two transactions that have all their /// markers satisfied. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] -pub struct TransactionPriority(pub U256); +pub struct TransactionPriority(pub u128); /// Internal Transaction type #[derive(Clone, PartialEq, Eq)] @@ -92,7 +92,7 @@ impl PoolTransaction { } /// Returns the gas pric of this transaction - pub fn gas_price(&self) -> U256 { + pub fn gas_price(&self) -> u128 { self.pending_transaction.transaction.gas_price() } } @@ -679,7 +679,7 @@ impl ReadyTransaction { &self.transaction.transaction.provides } - pub fn gas_price(&self) -> U256 { + pub fn gas_price(&self) -> u128 { self.transaction.transaction.gas_price() } } diff --git a/crates/anvil/src/eth/sign.rs b/crates/anvil/src/eth/sign.rs index 4886d96f0468..949a517fa769 100644 --- a/crates/anvil/src/eth/sign.rs +++ b/crates/anvil/src/eth/sign.rs @@ -1,8 +1,10 @@ use crate::eth::error::BlockchainError; +use alloy_consensus::{SignableTransaction, Signed}; use alloy_dyn_abi::TypedData; -use alloy_network::{Signed, Transaction}; -use alloy_primitives::{Address, Signature, B256, U256}; -use alloy_signer::{LocalWallet, Signer as AlloySigner, SignerSync as AlloySignerSync}; +use alloy_network::TxSignerSync; +use alloy_primitives::{Address, Signature, B256}; +use alloy_signer::Signer as AlloySigner; +use alloy_signer_wallet::LocalWallet; use anvil_core::eth::transaction::{ optimism::{DepositTransaction, DepositTransactionRequest}, TypedTransaction, TypedTransactionRequest, @@ -84,17 +86,13 @@ impl Signer for DevSigner { // typed data. signer.set_chain_id(None); - Ok(signer - .sign_hash( - payload.eip712_signing_hash().map_err(|_| BlockchainError::NoSignerAvailable)?, - ) - .await?) + Ok(signer.sign_dynamic_typed_data(payload).await?) } async fn sign_hash(&self, address: Address, hash: B256) -> Result { let signer = self.accounts.get(&address).ok_or(BlockchainError::NoSignerAvailable)?; - Ok(signer.sign_hash(hash).await?) + Ok(signer.sign_hash(&hash).await?) } fn sign_transaction( @@ -160,7 +158,7 @@ pub fn build_typed_transaction( source_hash, mint, is_system_tx, - nonce: U256::ZERO, + nonce: 0, }) } }; diff --git a/crates/anvil/src/lib.rs b/crates/anvil/src/lib.rs index afc600a3b34d..11a860bc385b 100644 --- a/crates/anvil/src/lib.rs +++ b/crates/anvil/src/lib.rs @@ -17,7 +17,7 @@ use crate::{ tasks::TaskManager, }; use alloy_primitives::{Address, U256}; -use alloy_signer::{LocalWallet, Signer as AlloySigner}; +use alloy_signer_wallet::LocalWallet; use eth::backend::fork::ClientFork; use foundry_common::provider::alloy::{ProviderBuilder, RetryProvider}; use foundry_evm::revm; @@ -311,7 +311,7 @@ impl NodeHandle { } /// Default gas price for all txs - pub fn gas_price(&self) -> U256 { + pub fn gas_price(&self) -> u128 { self.config.get_gas_price() } diff --git a/crates/anvil/src/pubsub.rs b/crates/anvil/src/pubsub.rs index b0b8b33bbecd..a063ee6697b2 100644 --- a/crates/anvil/src/pubsub.rs +++ b/crates/anvil/src/pubsub.rs @@ -2,10 +2,8 @@ use crate::{ eth::{backend::notifications::NewBlockNotifications, error::to_rpc_result}, StorageInfo, }; -use alloy_consensus::ReceiptWithBloom; -use alloy_network::Sealable; -use alloy_primitives::{Log, TxHash, B256, U256}; -use alloy_rpc_types::{pubsub::SubscriptionResult, FilteredParams, Log as AlloyLog}; +use alloy_primitives::{TxHash, B256}; +use alloy_rpc_types::{pubsub::SubscriptionResult, FilteredParams, Log}; use anvil_core::eth::{block::Block, subscription::SubscriptionId, transaction::TypedReceipt}; use anvil_rpc::{request::Version, response::ResponseResult}; use futures::{channel::mpsc::Receiver, ready, Stream, StreamExt}; @@ -22,7 +20,7 @@ pub struct LogsSubscription { pub blocks: NewBlockNotifications, pub storage: StorageInfo, pub filter: FilteredParams, - pub queued: VecDeque, + pub queued: VecDeque, pub id: SubscriptionId, } @@ -145,13 +143,14 @@ impl Stream for EthSubscription { } /// Returns all the logs that match the given filter -pub fn filter_logs( - block: Block, - receipts: Vec, - filter: &FilteredParams, -) -> Vec { +pub fn filter_logs(block: Block, receipts: Vec, filter: &FilteredParams) -> Vec { /// Determines whether to add this log - fn add_log(block_hash: B256, l: &Log, block: &Block, params: &FilteredParams) -> bool { + fn add_log( + block_hash: B256, + l: &alloy_primitives::Log, + block: &Block, + params: &FilteredParams, + ) -> bool { if params.filter.is_some() { let block_number = block.header.number; if !params.filter_block_range(block_number) || @@ -165,29 +164,22 @@ pub fn filter_logs( true } - let block_hash = block.header.hash(); + let block_hash = block.header.hash_slow(); let mut logs = vec![]; let mut log_index: u32 = 0; for (receipt_index, receipt) in receipts.into_iter().enumerate() { - let receipt: ReceiptWithBloom = receipt.into(); - let receipt_logs = receipt.receipt.logs; - let transaction_hash: Option = if !receipt_logs.is_empty() { - Some(block.transactions[receipt_index].hash()) - } else { - None - }; - for log in receipt_logs.into_iter() { - if add_log(block_hash, &log, &block, filter) { - logs.push(AlloyLog { - address: log.address, - topics: log.topics().to_vec(), - data: log.data.data, + let transaction_hash = block.transactions[receipt_index].hash(); + for log in receipt.logs() { + if add_log(block_hash, log, &block, filter) { + logs.push(Log { + inner: log.clone(), block_hash: Some(block_hash), - block_number: Some(U256::from(block.header.number)), - transaction_hash, - transaction_index: Some(U256::from(receipt_index)), - log_index: Some(U256::from(log_index)), + block_number: Some(block.header.number), + transaction_hash: Some(transaction_hash), + transaction_index: Some(receipt_index as u64), + log_index: Some(log_index as u64), removed: false, + block_timestamp: Some(block.header.timestamp), }); } log_index += 1; diff --git a/crates/anvil/src/tasks/mod.rs b/crates/anvil/src/tasks/mod.rs index 429f8d5d3291..e42b0437ca68 100644 --- a/crates/anvil/src/tasks/mod.rs +++ b/crates/anvil/src/tasks/mod.rs @@ -1,12 +1,13 @@ //! Task management support use crate::{shutdown::Shutdown, tasks::block_listener::BlockListener, EthApi}; +use alloy_network::AnyNetwork; +use alloy_primitives::B256; +use alloy_provider::Provider; +use alloy_rpc_types::Block; +use alloy_transport::Transport; use anvil_core::types::Forking; -use ethers::{ - prelude::Middleware, - providers::{JsonRpcClient, PubsubClient}, - types::{Block, H256}, -}; +use futures::StreamExt; use std::{fmt, future::Future}; use tokio::{runtime::Handle, task::JoinHandle}; @@ -51,32 +52,33 @@ impl TaskManager { /// block /// /// ``` + /// use alloy_network::Ethereum; + /// use alloy_provider::RootProvider; /// use anvil::{spawn, NodeConfig}; - /// use ethers::providers::Provider; - /// use std::sync::Arc; + /// /// # async fn t() { /// let endpoint = "http://...."; /// let (api, handle) = spawn(NodeConfig::default().with_eth_rpc_url(Some(endpoint))).await; /// - /// let provider = Arc::new(Provider::try_from(endpoint).unwrap()); + /// let provider = RootProvider::connect_builtin(endpoint).await.unwrap(); /// /// handle.task_manager().spawn_reset_on_new_polled_blocks(provider, api); /// # } /// ``` - pub fn spawn_reset_on_new_polled_blocks

(&self, provider: P, api: EthApi) + pub fn spawn_reset_on_new_polled_blocks(&self, provider: P, api: EthApi) where - P: Middleware + Clone + Unpin + 'static + Send + Sync, -

::Provider: JsonRpcClient, + P: Provider + Clone + Unpin + 'static, + T: Transport + Clone, { self.spawn_block_poll_listener(provider.clone(), move |hash| { let provider = provider.clone(); let api = api.clone(); async move { - if let Ok(Some(block)) = provider.get_block(hash).await { + if let Ok(Some(block)) = provider.get_block(hash.into(), false).await { let _ = api .anvil_reset(Some(Forking { json_rpc_url: None, - block_number: block.number.map(|b| b.as_u64()), + block_number: block.header.number, })) .await; } @@ -87,16 +89,21 @@ impl TaskManager { /// Spawns a new [`BlockListener`] task that listens for new blocks (poll-based) See also /// [`Provider::watch_blocks`] and executes the future the `task_factory` returns for the new /// block hash - pub fn spawn_block_poll_listener(&self, provider: P, task_factory: F) + pub fn spawn_block_poll_listener(&self, provider: P, task_factory: F) where - P: Middleware + Unpin + 'static, -

::Provider: JsonRpcClient, - F: Fn(H256) -> Fut + Unpin + Send + Sync + 'static, + P: Provider + 'static, + T: Transport + Clone, + F: Fn(B256) -> Fut + Unpin + Send + Sync + 'static, Fut: Future + Send, { let shutdown = self.on_shutdown.clone(); self.spawn(async move { - let blocks = provider.watch_blocks().await.unwrap(); + let blocks = provider + .watch_blocks() + .await + .unwrap() + .into_stream() + .flat_map(futures::stream::iter); BlockListener::new(shutdown, blocks, task_factory).await; }); } @@ -105,21 +112,23 @@ impl TaskManager { /// block /// /// ``` + /// use alloy_network::Ethereum; + /// use alloy_provider::RootProvider; /// use anvil::{spawn, NodeConfig}; - /// use ethers::providers::Provider; + /// /// # async fn t() { /// let (api, handle) = spawn(NodeConfig::default().with_eth_rpc_url(Some("http://...."))).await; /// - /// let provider = Provider::connect("ws://...").await.unwrap(); + /// let provider = RootProvider::connect_builtin("ws://...").await.unwrap(); /// /// handle.task_manager().spawn_reset_on_subscribed_blocks(provider, api); /// /// # } /// ``` - pub fn spawn_reset_on_subscribed_blocks

(&self, provider: P, api: EthApi) + pub fn spawn_reset_on_subscribed_blocks(&self, provider: P, api: EthApi) where - P: Middleware + Unpin + 'static + Send + Sync, -

::Provider: PubsubClient, + P: Provider + 'static, + T: Transport + Clone, { self.spawn_block_subscription(provider, move |block| { let api = api.clone(); @@ -127,7 +136,7 @@ impl TaskManager { let _ = api .anvil_reset(Some(Forking { json_rpc_url: None, - block_number: block.number.map(|b| b.as_u64()), + block_number: block.header.number, })) .await; } @@ -137,16 +146,16 @@ impl TaskManager { /// Spawns a new [`BlockListener`] task that listens for new blocks (via subscription) See also /// [`Provider::subscribe_blocks()`] and executes the future the `task_factory` returns for the /// new block hash - pub fn spawn_block_subscription(&self, provider: P, task_factory: F) + pub fn spawn_block_subscription(&self, provider: P, task_factory: F) where - P: Middleware + Unpin + 'static, -

::Provider: PubsubClient, - F: Fn(Block) -> Fut + Unpin + Send + Sync + 'static, + P: Provider + 'static, + T: Transport + Clone, + F: Fn(Block) -> Fut + Unpin + Send + Sync + 'static, Fut: Future + Send, { let shutdown = self.on_shutdown.clone(); self.spawn(async move { - let blocks = provider.subscribe_blocks().await.unwrap(); + let blocks = provider.subscribe_blocks().await.unwrap().into_stream(); BlockListener::new(shutdown, blocks, task_factory).await; }); } diff --git a/crates/anvil/tests/it/anvil_api.rs b/crates/anvil/tests/it/anvil_api.rs index 2f37f9b56d10..d2826b9ed0a7 100644 --- a/crates/anvil/tests/it/anvil_api.rs +++ b/crates/anvil/tests/it/anvil_api.rs @@ -44,7 +44,7 @@ async fn can_set_block_gas_limit() { api.mine_one().await; let latest_block = api.block_by_number(alloy_rpc_types::BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert_eq!(block_gas_limit.to_alloy(), latest_block.header.gas_limit); + assert_eq!(block_gas_limit.as_u128(), latest_block.header.gas_limit); } // Ref @@ -445,10 +445,10 @@ async fn can_get_node_info() { hard_fork: SpecId::CANCUN, transaction_order: "fees".to_owned(), environment: NodeEnvironment { - base_fee: U256::from_str("0x3b9aca00").unwrap().to_alloy(), + base_fee: alloy_primitives::U256::from_str("0x3b9aca00").unwrap().to(), chain_id: 0x7a69, - gas_limit: U256::from_str("0x1c9c380").unwrap().to_alloy(), - gas_price: U256::from_str("0x77359400").unwrap().to_alloy(), + gas_limit: alloy_primitives::U256::from_str("0x1c9c380").unwrap().to(), + gas_price: alloy_primitives::U256::from_str("0x77359400").unwrap().to(), }, fork_config: NodeForkConfig { fork_url: None, @@ -616,16 +616,16 @@ async fn test_fork_revert_call_latest_block_timestamp() { ); assert_eq!( - multicall.get_current_block_timestamp().await.unwrap(), - latest_block.header.timestamp.to_ethers() + multicall.get_current_block_timestamp().await.unwrap().as_u64(), + latest_block.header.timestamp ); assert_eq!( multicall.get_current_block_difficulty().await.unwrap(), latest_block.header.difficulty.to_ethers() ); assert_eq!( - multicall.get_current_block_gas_limit().await.unwrap(), - latest_block.header.gas_limit.to_ethers() + multicall.get_current_block_gas_limit().await.unwrap().as_u128(), + latest_block.header.gas_limit ); assert_eq!( multicall.get_current_block_coinbase().await.unwrap(), diff --git a/crates/anvil/tests/it/api.rs b/crates/anvil/tests/it/api.rs index 216e9581389b..acf9a0f9834a 100644 --- a/crates/anvil/tests/it/api.rs +++ b/crates/anvil/tests/it/api.rs @@ -5,12 +5,12 @@ use crate::{ utils::ethers_http_provider, }; use alloy_primitives::{Address as rAddress, B256, U256 as rU256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use alloy_rpc_types::{ request::{TransactionInput as CallInput, TransactionRequest as CallRequest}, state::{AccountOverride, StateOverride}, + WithOtherFields, }; -use alloy_signer::Signer as AlloySigner; use anvil::{ eth::{api::CLIENT_VERSION, EthApi}, spawn, NodeConfig, CHAIN_ID, @@ -213,7 +213,7 @@ async fn can_call_on_pending_block() { .call() .await .unwrap(); - assert_eq!(block.header.timestamp, block_timestamp.to_alloy()); + assert_eq!(block.header.timestamp, block_timestamp.as_u64()); let block_gas_limit = pending_contract .get_current_block_gas_limit() @@ -221,7 +221,7 @@ async fn can_call_on_pending_block() { .call() .await .unwrap(); - assert_eq!(block.header.gas_limit, block_gas_limit.to_alloy()); + assert_eq!(block.header.gas_limit, block_gas_limit.as_u128()); let block_coinbase = pending_contract .get_current_block_coinbase() @@ -244,11 +244,11 @@ where { let result = api .call( - CallRequest { + WithOtherFields::new(CallRequest { input: CallInput::maybe_input(call.tx.data().cloned().map(|b| b.0.into())), to: Some(to.to_alloy()), ..Default::default() - }, + }), None, Some(overrides), ) diff --git a/crates/anvil/tests/it/fork.rs b/crates/anvil/tests/it/fork.rs index 39946383e105..5f94a95aefba 100644 --- a/crates/anvil/tests/it/fork.rs +++ b/crates/anvil/tests/it/fork.rs @@ -5,12 +5,11 @@ use crate::{ utils::{self, ethers_http_provider}, }; use alloy_primitives::{address, U256 as rU256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider as AlloyProvider; use alloy_rpc_types::{ request::{TransactionInput, TransactionRequest as CallRequest}, - BlockNumberOrTag, + BlockNumberOrTag, WithOtherFields, }; -use alloy_signer::Signer as AlloySigner; use anvil::{eth::EthApi, spawn, NodeConfig, NodeHandle}; use anvil_core::types::Forking; use ethers::{ @@ -23,7 +22,7 @@ use ethers::{ }, }; use foundry_common::{ - provider::ethers::get_http_provider, + provider::alloy::get_http_provider, rpc, rpc::next_http_rpc_endpoint, types::{ToAlloy, ToEthers}, @@ -290,7 +289,7 @@ async fn test_fork_snapshotting() { let provider = handle.http_provider(); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + rU256::from(1)); + assert_eq!(nonce, initial_nonce + 1); let to_balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); @@ -329,7 +328,7 @@ async fn test_fork_snapshotting_repeated() { let _ = tx_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + rU256::from(1)); + assert_eq!(nonce, initial_nonce + 1); let to_balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); @@ -383,7 +382,7 @@ async fn test_fork_snapshotting_blocks() { assert_eq!(block_number_after, block_number + 1); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + rU256::from(1)); + assert_eq!(nonce, initial_nonce + 1); let to_balance = provider.get_balance(to, None).await.unwrap(); assert_eq!(balance_before.saturating_add(amount), to_balance); @@ -397,12 +396,12 @@ async fn test_fork_snapshotting_blocks() { // repeat transaction let _ = tx_provider.send_transaction(tx.clone(), None).await.unwrap().await.unwrap().unwrap(); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + rU256::from(1)); + assert_eq!(nonce, initial_nonce + 1); // revert again: nothing to revert since snapshot gone assert!(!api.evm_revert(snapshot).await.unwrap()); let nonce = provider.get_transaction_count(from, None).await.unwrap(); - assert_eq!(nonce, initial_nonce + rU256::from(1)); + assert_eq!(nonce, initial_nonce + 1); let block_number_after = provider.get_block_number().await.unwrap(); assert_eq!(block_number_after, block_number + 1); } @@ -468,8 +467,8 @@ async fn can_reset_properly() { let (origin_api, origin_handle) = spawn(NodeConfig::test()).await; let account = origin_handle.dev_accounts().next().unwrap(); let origin_provider = origin_handle.http_provider(); - let origin_nonce = rU256::from(1u64); - origin_api.anvil_set_nonce(account, origin_nonce).await.unwrap(); + let origin_nonce = 1u64; + origin_api.anvil_set_nonce(account, rU256::from(origin_nonce)).await.unwrap(); assert_eq!(origin_nonce, origin_provider.get_transaction_count(account, None).await.unwrap()); @@ -486,10 +485,7 @@ async fn can_reset_properly() { let tx = fork_tx_provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap(); // nonce incremented by 1 - assert_eq!( - origin_nonce + rU256::from(1), - fork_provider.get_transaction_count(account, None).await.unwrap() - ); + assert_eq!(origin_nonce + 1, fork_provider.get_transaction_count(account, None).await.unwrap()); // resetting to origin state fork_api.anvil_reset(Some(Forking::default())).await.unwrap(); @@ -754,13 +750,19 @@ async fn test_reset_fork_on_new_blocks() { let anvil_provider = ethers_http_provider(&handle.http_endpoint()); let endpoint = next_http_rpc_endpoint(); - let provider = Arc::new(get_http_provider(&endpoint).interval(Duration::from_secs(2))); + let provider = Arc::new(get_http_provider(&endpoint)); let current_block = anvil_provider.get_block_number().await.unwrap(); handle.task_manager().spawn_reset_on_new_polled_blocks(provider.clone(), api); - let mut stream = provider.watch_blocks().await.unwrap(); + let mut stream = provider + .watch_blocks() + .await + .unwrap() + .with_poll_interval(Duration::from_secs(2)) + .into_stream() + .flat_map(futures::stream::iter); // the http watcher may fetch multiple blocks at once, so we set a timeout here to offset edge // cases where the stream immediately returns a block tokio::time::sleep(Chain::Mainnet.average_blocktime_hint().unwrap()).await; @@ -788,11 +790,11 @@ async fn test_fork_call() { let res1 = api .call( - CallRequest { + WithOtherFields::new(CallRequest { to: Some(to.to_alloy()), input: input.to_alloy().into(), ..Default::default() - }, + }), None, None, ) @@ -810,7 +812,7 @@ async fn test_fork_block_timestamp() { api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert!(initial_block.header.timestamp.to::() < latest_block.header.timestamp.to::()); + assert!(initial_block.header.timestamp < latest_block.header.timestamp); } #[tokio::test(flavor = "multi_thread")] @@ -821,14 +823,11 @@ async fn test_fork_snapshot_block_timestamp() { api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); let initial_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); api.evm_revert(snapshot_id).await.unwrap(); - api.evm_set_next_block_timestamp(initial_block.header.timestamp.to::()).unwrap(); + api.evm_set_next_block_timestamp(initial_block.header.timestamp).unwrap(); api.anvil_mine(Some(rU256::from(1)), None).await.unwrap(); let latest_block = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); - assert_eq!( - initial_block.header.timestamp.to::(), - latest_block.header.timestamp.to::() - ); + assert_eq!(initial_block.header.timestamp, latest_block.header.timestamp); } #[tokio::test(flavor = "multi_thread")] @@ -1092,7 +1091,7 @@ async fn test_fork_reset_basefee() { let latest = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); // basefee of +1 block: - assert_eq!(latest.header.base_fee_per_gas.unwrap(), rU256::from(59455969592u64)); + assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59455969592u128); // now reset to block 18835000 -1 api.anvil_reset(Some(Forking { json_rpc_url: None, block_number: Some(18835000u64 - 1) })) @@ -1103,7 +1102,7 @@ async fn test_fork_reset_basefee() { let latest = api.block_by_number(BlockNumberOrTag::Latest).await.unwrap().unwrap(); // basefee of the forked block: - assert_eq!(latest.header.base_fee_per_gas.unwrap(), rU256::from(59017001138u64)); + assert_eq!(latest.header.base_fee_per_gas.unwrap(), 59017001138u128); } // @@ -1180,11 +1179,11 @@ async fn test_fork_execution_reverted() { let resp = api .call( - CallRequest { + WithOtherFields::new(CallRequest { to: Some(address!("Fd6CC4F251eaE6d02f9F7B41D1e80464D3d2F377")), input: TransactionInput::new("0x8f283b3c".as_bytes().into()), ..Default::default() - }, + }), Some(target.into()), None, ) diff --git a/crates/anvil/tests/it/gas.rs b/crates/anvil/tests/it/gas.rs index 4f6098bfb2df..e2d194340c57 100644 --- a/crates/anvil/tests/it/gas.rs +++ b/crates/anvil/tests/it/gas.rs @@ -1,7 +1,6 @@ //! Gas related tests use crate::utils::ethers_http_provider; -use alloy_primitives::U256; use anvil::{eth::fees::INITIAL_BASE_FEE, spawn, NodeConfig}; use ethers::{ prelude::Middleware, @@ -10,16 +9,13 @@ use ethers::{ TransactionRequest, }, }; -use foundry_common::types::ToAlloy; -const GAS_TRANSFER: u64 = 21_000u64; +const GAS_TRANSFER: u128 = 21_000; #[tokio::test(flavor = "multi_thread")] async fn test_basefee_full_block() { let (_api, handle) = spawn( - NodeConfig::test() - .with_base_fee(Some(INITIAL_BASE_FEE.to_alloy())) - .with_gas_limit(Some(GAS_TRANSFER.to_alloy())), + NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE)).with_gas_limit(Some(GAS_TRANSFER)), ) .await; let provider = ethers_http_provider(&handle.http_endpoint()); @@ -34,15 +30,15 @@ async fn test_basefee_full_block() { assert!(next_base_fee > base_fee); // max increase, full block - assert_eq!(next_base_fee.as_u64(), INITIAL_BASE_FEE + 125_000_000); + assert_eq!(next_base_fee.as_u128(), INITIAL_BASE_FEE + 125_000_000); } #[tokio::test(flavor = "multi_thread")] async fn test_basefee_half_block() { let (_api, handle) = spawn( NodeConfig::test() - .with_base_fee(Some(INITIAL_BASE_FEE.to_alloy())) - .with_gas_limit(Some(GAS_TRANSFER.to_alloy() * U256::from(2))), + .with_base_fee(Some(INITIAL_BASE_FEE)) + .with_gas_limit(Some(GAS_TRANSFER * 2)), ) .await; let provider = ethers_http_provider(&handle.http_endpoint()); @@ -54,12 +50,11 @@ async fn test_basefee_half_block() { provider.get_block(BlockNumber::Latest).await.unwrap().unwrap().base_fee_per_gas.unwrap(); // unchanged, half block - assert_eq!(next_base_fee.as_u64(), INITIAL_BASE_FEE); + assert_eq!(next_base_fee.as_u128(), INITIAL_BASE_FEE); } #[tokio::test(flavor = "multi_thread")] async fn test_basefee_empty_block() { - let (api, handle) = - spawn(NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE.to_alloy()))).await; + let (api, handle) = spawn(NodeConfig::test().with_base_fee(Some(INITIAL_BASE_FEE))).await; let provider = ethers_http_provider(&handle.http_endpoint()); let tx = TransactionRequest::new().to(Address::random()).value(1337u64); @@ -79,8 +74,8 @@ async fn test_basefee_empty_block() { #[tokio::test(flavor = "multi_thread")] async fn test_respect_base_fee() { - let base_fee = 50u64; - let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee.to_alloy()))).await; + let base_fee = 50u128; + let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; let provider = ethers_http_provider(&handle.http_endpoint()); let mut tx = TypedTransaction::default(); tx.set_value(100u64); @@ -99,8 +94,8 @@ async fn test_respect_base_fee() { #[tokio::test(flavor = "multi_thread")] async fn test_tip_above_fee_cap() { - let base_fee = 50u64; - let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee.to_alloy()))).await; + let base_fee = 50u128; + let (_api, handle) = spawn(NodeConfig::test().with_base_fee(Some(base_fee))).await; let provider = ethers_http_provider(&handle.http_endpoint()); let tx = TypedTransaction::Eip1559( Eip1559TransactionRequest::new() diff --git a/crates/anvil/tests/it/genesis.rs b/crates/anvil/tests/it/genesis.rs index f5f5fec9cc7d..0b243db3edb9 100644 --- a/crates/anvil/tests/it/genesis.rs +++ b/crates/anvil/tests/it/genesis.rs @@ -3,8 +3,8 @@ use std::str::FromStr; use alloy_genesis::Genesis; -use alloy_primitives::{Address, U256, U64}; -use alloy_providers::tmp::TempProvider; +use alloy_primitives::{Address, U256}; +use alloy_provider::Provider; use anvil::{spawn, NodeConfig}; #[tokio::test(flavor = "multi_thread")] @@ -41,7 +41,7 @@ async fn can_apply_genesis() { let provider = handle.http_provider(); - assert_eq!(provider.get_chain_id().await.unwrap(), U64::from(19763u64)); + assert_eq!(provider.get_chain_id().await.unwrap(), 19763u64); let addr: Address = Address::from_str("71562b71999873db5b286df957af199ec94617f7").unwrap(); let balance = provider.get_balance(addr, None).await.unwrap(); diff --git a/crates/anvil/tests/it/otterscan.rs b/crates/anvil/tests/it/otterscan.rs index 6bbd987a8476..4c344264034c 100644 --- a/crates/anvil/tests/it/otterscan.rs +++ b/crates/anvil/tests/it/otterscan.rs @@ -7,7 +7,6 @@ use crate::{ }; use alloy_primitives::U256 as rU256; use alloy_rpc_types::{BlockNumberOrTag, BlockTransactions}; -use alloy_signer::Signer as AlloySigner; use anvil::{ eth::otterscan::types::{ OtsInternalOperation, OtsInternalOperationType, OtsTrace, OtsTraceType, @@ -33,8 +32,8 @@ async fn can_call_erigon_get_header_by_number() { let res0 = api.erigon_get_header_by_number(0.into()).await.unwrap().unwrap(); let res1 = api.erigon_get_header_by_number(1.into()).await.unwrap().unwrap(); - assert_eq!(res0.header.number, Some(rU256::from(0))); - assert_eq!(res1.header.number, Some(rU256::from(1))); + assert_eq!(res0.header.number, Some(0)); + assert_eq!(res1.header.number, Some(1)); } #[tokio::test(flavor = "multi_thread")] @@ -487,7 +486,7 @@ async fn can_call_ots_get_block_transactions() { result.receipts.iter().enumerate().for_each(|(i, receipt)| { let expected = hashes.pop_front(); - assert_eq!(expected, receipt.transaction_hash.map(|h| h.to_ethers())); + assert_eq!(expected, Some(receipt.transaction_hash.to_ethers())); assert_eq!( expected.map(|h| h.to_alloy()), result.fullblock.block.transactions.hashes().nth(i).copied(), @@ -528,7 +527,7 @@ async fn can_call_ots_search_transactions_before() { assert_eq!(hashes.pop(), Some(tx.hash.to_ethers())); }); - block = result.txs.last().unwrap().block_number.unwrap().to::() - 1; + block = result.txs.last().unwrap().block_number.unwrap() - 1; } assert!(hashes.is_empty()); @@ -564,7 +563,7 @@ async fn can_call_ots_search_transactions_after() { assert_eq!(hashes.pop_back(), Some(tx.hash.to_ethers())); }); - block = result.txs.last().unwrap().block_number.unwrap().to::() + 1; + block = result.txs.last().unwrap().block_number.unwrap() + 1; } assert!(hashes.is_empty()); diff --git a/crates/anvil/tests/it/transaction.rs b/crates/anvil/tests/it/transaction.rs index 18a74e1a9d8a..46c059374c60 100644 --- a/crates/anvil/tests/it/transaction.rs +++ b/crates/anvil/tests/it/transaction.rs @@ -2,13 +2,12 @@ use crate::{ abi::*, utils::{ethers_http_provider, ethers_ws_provider}, }; -use alloy_primitives::U256 as rU256; +use alloy_primitives::{Bytes, U256 as rU256}; use alloy_rpc_types::{ request::TransactionRequest as AlloyTransactionRequest, state::{AccountOverride, StateOverride}, - BlockNumberOrTag, + BlockNumberOrTag, WithOtherFields, }; -use alloy_signer::Signer as AlloySigner; use anvil::{spawn, Hardfork, NodeConfig}; use ethers::{ abi::ethereum_types::BigEndianHash, @@ -21,7 +20,7 @@ use ethers::{ Address, BlockNumber, Transaction, TransactionReceipt, H256, U256, }, }; -use foundry_common::types::{to_call_request_from_tx_request, ToAlloy, ToEthers}; +use foundry_common::types::{ToAlloy, ToEthers}; use futures::{future::join_all, FutureExt, StreamExt}; use std::{collections::HashSet, sync::Arc, time::Duration}; use tokio::time::timeout; @@ -434,11 +433,11 @@ async fn get_blocktimestamp_works() { api.block_by_number(alloy_rpc_types::BlockNumberOrTag::Latest).await.unwrap().unwrap(); let timestamp = contract.get_current_block_timestamp().call().await.unwrap(); - assert_eq!(timestamp, latest_block.header.timestamp.to_ethers()); + assert_eq!(timestamp.as_u64(), latest_block.header.timestamp); // repeat call same result let timestamp = contract.get_current_block_timestamp().call().await.unwrap(); - assert_eq!(timestamp, latest_block.header.timestamp.to_ethers()); + assert_eq!(timestamp.as_u64(), latest_block.header.timestamp); // mock timestamp let next_timestamp = timestamp.as_u64() + 1337; @@ -962,9 +961,12 @@ async fn estimates_gas_on_pending_by_default() { let tx = TransactionRequest::new().from(sender).to(recipient).value(1e18 as u64); client.send_transaction(tx, None).await.unwrap(); - let tx = - TransactionRequest::new().from(recipient).to(sender).value(1e10 as u64).data(vec![0x42]); - api.estimate_gas(to_call_request_from_tx_request(tx), None, None).await.unwrap(); + let tx = AlloyTransactionRequest::default() + .from(recipient.to_alloy()) + .to(Some(sender.to_alloy())) + .value(rU256::from(1e10)) + .input(Bytes::from(vec![0x42]).into()); + api.estimate_gas(WithOtherFields::new(tx), None, None).await.unwrap(); } #[tokio::test(flavor = "multi_thread")] @@ -975,11 +977,13 @@ async fn test_estimate_gas() { let sender = wallet.address(); let recipient = Address::random(); - let tx = - TransactionRequest::new().from(recipient).to(sender).value(1e10 as u64).data(vec![0x42]); + let tx = AlloyTransactionRequest::default() + .from(recipient.to_alloy()) + .to(Some(sender.to_alloy())) + .value(rU256::from(1e10)) + .input(Bytes::from(vec![0x42]).into()); // Expect the gas estimation to fail due to insufficient funds. - let error_result = - api.estimate_gas(to_call_request_from_tx_request(tx.clone()), None, None).await; + let error_result = api.estimate_gas(WithOtherFields::new(tx.clone()), None, None).await; assert!(error_result.is_err(), "Expected an error due to insufficient funds"); let error_message = error_result.unwrap_err().to_string(); @@ -998,15 +1002,12 @@ async fn test_estimate_gas() { // Estimate gas with state override implying sufficient funds. let gas_estimate = api - .estimate_gas(to_call_request_from_tx_request(tx), None, Some(state_override)) + .estimate_gas(WithOtherFields::new(tx), None, Some(state_override)) .await .expect("Failed to estimate gas with state override"); // Assert the gas estimate meets the expected minimum. - assert!( - gas_estimate >= alloy_primitives::U256::from(21000), - "Gas estimate is lower than expected minimum" - ); + assert!(gas_estimate >= rU256::from(21000), "Gas estimate is lower than expected minimum"); } #[tokio::test(flavor = "multi_thread")] @@ -1032,8 +1033,7 @@ async fn test_reject_gas_too_low() { // #[tokio::test(flavor = "multi_thread")] async fn can_call_with_high_gas_limit() { - let (_api, handle) = - spawn(NodeConfig::test().with_gas_limit(Some(U256::from(100_000_000).to_alloy()))).await; + let (_api, handle) = spawn(NodeConfig::test().with_gas_limit(Some(100_000_000))).await; let provider = ethers_http_provider(&handle.http_endpoint()); let wallet = handle.dev_wallets().next().unwrap().to_ethers(); @@ -1094,8 +1094,8 @@ async fn can_mine_multiple_in_block() { }; // broadcast it via the eth_sendTransaction API - let first = api.send_transaction(tx.clone()).await.unwrap(); - let second = api.send_transaction(tx.clone()).await.unwrap(); + let first = api.send_transaction(WithOtherFields::new(tx.clone())).await.unwrap(); + let second = api.send_transaction(WithOtherFields::new(tx.clone())).await.unwrap(); api.anvil_mine(Some(rU256::from(1)), Some(rU256::ZERO)).await.unwrap(); diff --git a/crates/anvil/tests/it/wsapi.rs b/crates/anvil/tests/it/wsapi.rs index c2073f5617f7..ce0c4d6a4387 100644 --- a/crates/anvil/tests/it/wsapi.rs +++ b/crates/anvil/tests/it/wsapi.rs @@ -1,6 +1,6 @@ //! general eth api tests with websocket provider -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use anvil::{spawn, NodeConfig}; use ethers::types::U256; use foundry_common::types::ToAlloy; diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 5361b3dea8b5..6b6dbae5197c 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -15,7 +15,11 @@ name = "cast" path = "bin/main.rs" [build-dependencies] -vergen = { workspace = true, default-features = false, features = ["build", "git", "gitcl"] } +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } [dependencies] # lib @@ -30,12 +34,20 @@ alloy-dyn-abi.workspace = true alloy-json-abi.workspace = true alloy-primitives.workspace = true alloy-rlp.workspace = true -alloy-providers.workspace = true +alloy-provider = { workspace = true, features = ["pubsub"] } +alloy-transport.workspace = true alloy-rpc-types.workspace = true +alloy-json-rpc.workspace = true alloy-signer.workspace = true +alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } +alloy-contract.workspace = true +alloy-consensus = { workspace = true, features = ["serde"] } +alloy-network.workspace = true +alloy-sol-types.workspace = true +alloy-chains.workspace = true ethers-core.workspace = true -ethers-providers.workspace = true +ethers-contract.workspace = true chrono.workspace = true evm-disassembler.workspace = true @@ -48,17 +60,11 @@ serde_json.workspace = true serde.workspace = true # aws -rusoto_core = { version = "0.48", default-features = false } -rusoto_kms = { version = "0.48", default-features = false } +aws-sdk-kms = { version = "1", default-features = false } # bin foundry-cli.workspace = true -ethers-contract.workspace = true -ethers-middleware.workspace = true -ethers-signers.workspace = true -eth-keystore = "0.5" - clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } clap_complete = "4" clap_complete_fig = "4" @@ -86,7 +92,7 @@ criterion = "0.5" [features] default = ["rustls"] rustls = ["foundry-cli/rustls", "foundry-wallets/rustls"] -openssl = ["foundry-cli/openssl", "foundry-wallets/openssl"] +openssl = ["foundry-cli/openssl"] asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["dep:tikv-jemallocator"] diff --git a/crates/cast/bin/cmd/access_list.rs b/crates/cast/bin/cmd/access_list.rs index 2ac5a0488468..ed572628de66 100644 --- a/crates/cast/bin/cmd/access_list.rs +++ b/crates/cast/bin/cmd/access_list.rs @@ -1,13 +1,16 @@ -use cast::{Cast, TxBuilder}; +use alloy_network::{AnyNetwork, TransactionBuilder}; +use alloy_primitives::Address; +use alloy_provider::Provider; +use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; +use alloy_transport::Transport; +use cast::Cast; use clap::Parser; -use ethers_core::types::{BlockId, NameOrAddress}; -use ethers_providers::Middleware; -use eyre::{Result, WrapErr}; +use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils, + utils::{self, parse_function_args}, }; -use foundry_common::types::ToEthers; +use foundry_common::ens::NameOrAddress; use foundry_config::{Chain, Config}; use std::str::FromStr; @@ -62,18 +65,37 @@ impl AccessListArgs { let provider = utils::get_provider(&config)?; let chain = utils::get_chain(config.chain, &provider).await?; let sender = eth.wallet.sender().await; - - access_list(&provider, sender.to_ethers(), to, sig, args, data, tx, chain, block, to_json) - .await?; + let etherscan_api_key = config.get_etherscan_api_key(Some(chain)); + + let to = match to { + Some(to) => Some(to.resolve(&provider).await?), + None => None, + }; + + access_list( + &provider, + etherscan_api_key.as_deref(), + sender, + to, + sig, + args, + data, + tx, + chain, + block, + to_json, + ) + .await?; Ok(()) } } #[allow(clippy::too_many_arguments)] -async fn access_list, T: Into>( - provider: M, - from: F, - to: Option, +async fn access_list, T: Transport + Clone>( + provider: P, + etherscan_api_key: Option<&str>, + from: Address, + to: Option

, sig: Option, args: Vec, data: Option, @@ -81,32 +103,35 @@ async fn access_list, T: Into, to_json: bool, -) -> Result<()> -where - M::Error: 'static, -{ - let mut builder = TxBuilder::new(&provider, from, to, chain, tx.legacy).await?; - builder - .gas(tx.gas_limit) - .gas_price(tx.gas_price) - .priority_gas_price(tx.priority_gas_price) - .nonce(tx.nonce); - - builder.value(tx.value); - - if let Some(sig) = sig { - builder.set_args(sig.as_str(), args).await?; +) -> Result<()> { + let mut req = WithOtherFields::::default() + .with_to(to.into()) + .with_from(from) + .with_value(tx.value.unwrap_or_default()) + .with_chain_id(chain.id()); + + if let Some(gas_limit) = tx.gas_limit { + req.set_gas_limit(gas_limit.to()); } - if let Some(data) = data { - // Note: `sig+args` and `data` are mutually exclusive - builder.set_data(hex::decode(data).wrap_err("Expected hex encoded function data")?); + + if let Some(nonce) = tx.nonce { + req.set_nonce(nonce.to()); } - let builder_output = builder.peek(); + let data = if let Some(sig) = sig { + parse_function_args(&sig, args, to, chain, &provider, etherscan_api_key).await?.0 + } else if let Some(data) = data { + // Note: `sig+args` and `data` are mutually exclusive + hex::decode(data)? + } else { + Vec::new() + }; + + req.set_input(data.into()); let cast = Cast::new(&provider); - let access_list: String = cast.access_list(builder_output, block, to_json).await?; + let access_list: String = cast.access_list(&req, block, to_json).await?; println!("{}", access_list); diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index ece090bf74ae..eeb09d5c0442 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -1,23 +1,19 @@ +use alloy_network::TransactionBuilder; use alloy_primitives::U256; -use cast::{Cast, TxBuilder}; +use alloy_rpc_types::{BlockId, TransactionRequest, WithOtherFields}; +use cast::Cast; use clap::Parser; -use ethers_core::types::{BlockId, NameOrAddress}; -use eyre::{Result, WrapErr}; +use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils::{self, handle_traces, parse_ether_value, TraceResult}, -}; -use foundry_common::{ - runtime_client::RuntimeClient, - types::{ToAlloy, ToEthers}, + utils::{self, handle_traces, parse_ether_value, parse_function_args, TraceResult}, }; +use foundry_common::ens::NameOrAddress; use foundry_compilers::EvmVersion; use foundry_config::{find_project_root_path, Config}; use foundry_evm::{executors::TracingExecutor, opts::EvmOpts}; use std::str::FromStr; -type Provider = ethers_providers::Provider; - /// CLI arguments for `cast call`. #[derive(Debug, Parser)] pub struct CallArgs { @@ -118,19 +114,43 @@ impl CallArgs { let provider = utils::get_provider(&config)?; let chain = utils::get_chain(config.chain, &provider).await?; let sender = eth.wallet.sender().await; + let etherscan_api_key = config.get_etherscan_api_key(Some(chain)); + + let to = match to { + Some(to) => Some(to.resolve(&provider).await?), + None => None, + }; - let mut builder: TxBuilder<'_, Provider> = - TxBuilder::new(&provider, sender.to_ethers(), to, chain, tx.legacy).await?; + let mut req = WithOtherFields::::default() + .with_to(to.into()) + .with_from(sender) + .with_value(tx.value.unwrap_or_default()); - builder - .gas(tx.gas_limit) - .etherscan_api_key(config.get_etherscan_api_key(Some(chain))) - .gas_price(tx.gas_price) - .priority_gas_price(tx.priority_gas_price) - .nonce(tx.nonce); + if let Some(nonce) = tx.nonce { + req.set_nonce(nonce.to()); + } - match command { + let (data, func) = match command { Some(CallSubcommands::Create { code, sig, args, value }) => { + if let Some(value) = value { + req.set_value(value); + } + + let mut data = hex::decode(code)?; + + if let Some(s) = sig { + let (mut constructor_args, _) = parse_function_args( + &s, + args, + None, + chain, + &provider, + etherscan_api_key.as_deref(), + ) + .await?; + data.append(&mut constructor_args); + } + if trace { let figment = Config::figment_with_root(find_project_root_path(None).unwrap()) .merge(eth.rpc); @@ -144,8 +164,8 @@ impl CallArgs { let trace = match executor.deploy( sender, - code.into_bytes().into(), - value.unwrap_or(U256::ZERO), + data.into(), + req.value.unwrap_or_default(), None, ) { Ok(deploy_result) => TraceResult::from(deploy_result), @@ -157,12 +177,26 @@ impl CallArgs { return Ok(()); } - // fill the builder after the conditional so we dont move values - fill_create(&mut builder, value, code, sig, args).await?; + (data, None) } _ => { // fill first here because we need to use the builder in the conditional - fill_tx(&mut builder, tx.value, sig, args, data).await?; + let (data, func) = if let Some(sig) = sig { + parse_function_args( + &sig, + args, + to, + chain, + &provider, + etherscan_api_key.as_deref(), + ) + .await? + } else if let Some(data) = data { + // Note: `sig+args` and `data` are mutually exclusive + (hex::decode(data)?, None) + } else { + (Vec::new(), None) + }; if trace { let figment = Config::figment_with_root(find_project_root_path(None).unwrap()) @@ -175,71 +209,28 @@ impl CallArgs { let mut executor = TracingExecutor::new(env, fork, evm_version, debug); - let (tx, _) = builder.build(); - let trace = TraceResult::from(executor.call_raw_committing( sender, - tx.to_addr().copied().expect("an address to be here").to_alloy(), - tx.data().cloned().unwrap_or_default().to_vec().into(), - tx.value().copied().unwrap_or_default().to_alloy(), + req.to.expect("an address to be here"), + data.into(), + req.value.unwrap_or_default(), )?); handle_traces(trace, &config, chain, labels, debug).await?; return Ok(()); } + + (data, func) } }; - let builder_output = builder.build(); - println!("{}", Cast::new(provider).call(builder_output, block).await?); - - Ok(()) - } -} - -/// fills the builder from create arg -async fn fill_create( - builder: &mut TxBuilder<'_, Provider>, - value: Option, - code: String, - sig: Option, - args: Vec, -) -> Result<()> { - builder.value(value); - - let mut data = hex::decode(code)?; - - if let Some(s) = sig { - let (mut sigdata, _func) = builder.create_args(&s, args).await?; - data.append(&mut sigdata); - } - - builder.set_data(data); - - Ok(()) -} - -/// fills the builder from args -async fn fill_tx( - builder: &mut TxBuilder<'_, Provider>, - value: Option, - sig: Option, - args: Vec, - data: Option, -) -> Result<()> { - builder.value(value); + req.set_input(data.into()); - if let Some(sig) = sig { - builder.set_args(sig.as_str(), args).await?; - } + println!("{}", Cast::new(provider).call(&req, func.as_ref(), block).await?); - if let Some(data) = data { - // Note: `sig+args` and `data` are mutually exclusive - builder.set_data(hex::decode(data).wrap_err("Expected hex encoded function data")?); + Ok(()) } - - Ok(()) } #[cfg(test)] diff --git a/crates/cast/bin/cmd/estimate.rs b/crates/cast/bin/cmd/estimate.rs index 56fdc40d336e..55149937b8a6 100644 --- a/crates/cast/bin/cmd/estimate.rs +++ b/crates/cast/bin/cmd/estimate.rs @@ -1,12 +1,14 @@ +use alloy_network::TransactionBuilder; use alloy_primitives::U256; -use cast::{Cast, TxBuilder}; +use alloy_provider::Provider; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; use clap::Parser; -use ethers_core::types::NameOrAddress; use eyre::Result; use foundry_cli::{ opts::{EtherscanOpts, RpcOpts}, - utils::{self, parse_ether_value}, + utils::{self, parse_ether_value, parse_function_args}, }; +use foundry_common::ens::NameOrAddress; use foundry_config::{figment::Figment, Config}; use std::str::FromStr; @@ -81,35 +83,47 @@ impl EstimateArgs { let figment = Figment::from(Config::figment()).merge(etherscan).merge(rpc); let config = Config::try_from(figment)?; - let provider = utils::get_provider(&config)?; let chain = utils::get_chain(config.chain, &provider).await?; let api_key = config.get_etherscan_api_key(Some(chain)); - let mut builder = TxBuilder::new(&provider, from, to, chain, false).await?; - builder.etherscan_api_key(api_key); + let from = from.resolve(&provider).await?; + let to = match to { + Some(to) => Some(to.resolve(&provider).await?), + None => None, + }; + + let mut req = WithOtherFields::::default() + .with_to(to.into()) + .with_from(from) + .with_value(value.unwrap_or_default()); - match command { + let data = match command { Some(EstimateSubcommands::Create { code, sig, args, value }) => { - builder.value(value); + if let Some(value) = value { + req.set_value(value); + } let mut data = hex::decode(code)?; if let Some(s) = sig { - let (mut sigdata, _func) = builder.create_args(&s, args).await?; - data.append(&mut sigdata); + let (mut constructor_args, _) = + parse_function_args(&s, args, to, chain, &provider, api_key.as_deref()) + .await?; + data.append(&mut constructor_args); } - builder.set_data(data); + data } _ => { let sig = sig.ok_or_else(|| eyre::eyre!("Function signature must be provided."))?; - builder.value(value).set_args(sig.as_str(), args).await?; + parse_function_args(&sig, args, to, chain, &provider, api_key.as_deref()).await?.0 } }; - let builder_output = builder.peek(); - let gas = Cast::new(&provider).estimate(builder_output).await?; + req.set_input(data.into()); + + let gas = provider.estimate_gas(&req, None).await?; println!("{gas}"); Ok(()) } diff --git a/crates/cast/bin/cmd/find_block.rs b/crates/cast/bin/cmd/find_block.rs index 5038ded7b620..f75f2c82f263 100644 --- a/crates/cast/bin/cmd/find_block.rs +++ b/crates/cast/bin/cmd/find_block.rs @@ -1,10 +1,8 @@ -use alloy_primitives::{U256, U64}; +use alloy_provider::Provider; use cast::Cast; use clap::Parser; -use ethers_providers::Middleware; use eyre::Result; use foundry_cli::{opts::RpcOpts, utils}; -use foundry_common::types::{ToAlloy, ToEthers}; use foundry_config::Config; use futures::join; @@ -22,7 +20,7 @@ impl FindBlockArgs { pub async fn run(self) -> Result<()> { let FindBlockArgs { timestamp, rpc } = self; - let ts_target = U256::from(timestamp); + let ts_target = timestamp; let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; @@ -30,43 +28,43 @@ impl FindBlockArgs { let cast_provider = Cast::new(provider); let res = join!(cast_provider.timestamp(last_block_num), cast_provider.timestamp(1)); - let ts_block_latest = res.0?; - let ts_block_1 = res.1?; + let ts_block_latest: u64 = res.0?.to(); + let ts_block_1: u64 = res.1?.to(); let block_num = if ts_block_latest < ts_target { // If the most recent block's timestamp is below the target, return it - last_block_num.to_alloy() + last_block_num } else if ts_block_1 > ts_target { // If the target timestamp is below block 1's timestamp, return that - U64::from(1_u64) + 1 } else { // Otherwise, find the block that is closest to the timestamp - let mut low_block = U64::from(1_u64); // block 0 has a timestamp of 0: https://github.com/ethereum/go-ethereum/issues/17042#issuecomment-559414137 - let mut high_block = last_block_num.to_alloy(); - let mut matching_block: Option = None; + let mut low_block = 1_u64; // block 0 has a timestamp of 0: https://github.com/ethereum/go-ethereum/issues/17042#issuecomment-559414137 + let mut high_block = last_block_num; + let mut matching_block = None; while high_block > low_block && matching_block.is_none() { // Get timestamp of middle block (this approach approach to avoids overflow) let high_minus_low_over_2 = high_block .checked_sub(low_block) .ok_or_else(|| eyre::eyre!("unexpected underflow")) .unwrap() - .checked_div(U64::from(2_u64)) + .checked_div(2_u64) .unwrap(); let mid_block = high_block.checked_sub(high_minus_low_over_2).unwrap(); - let ts_mid_block = cast_provider.timestamp(mid_block.to_ethers()).await?; + let ts_mid_block = cast_provider.timestamp(mid_block).await?.to::(); // Check if we've found a match or should keep searching if ts_mid_block == ts_target { matching_block = Some(mid_block) - } else if high_block.checked_sub(low_block).unwrap() == U64::from(1_u64) { + } else if high_block.checked_sub(low_block).unwrap() == 1_u64 { // The target timestamp is in between these blocks. This rounds to the // highest block if timestamp is equidistant between blocks let res = join!( - cast_provider.timestamp(high_block.to_ethers()), - cast_provider.timestamp(low_block.to_ethers()) + cast_provider.timestamp(high_block), + cast_provider.timestamp(low_block) ); - let ts_high = res.0.unwrap(); - let ts_low = res.1.unwrap(); + let ts_high: u64 = res.0.unwrap().to(); + let ts_low: u64 = res.1.unwrap().to(); let high_diff = ts_high.checked_sub(ts_target).unwrap(); let low_diff = ts_target.checked_sub(ts_low).unwrap(); let is_low = low_diff < high_diff; diff --git a/crates/cast/bin/cmd/logs.rs b/crates/cast/bin/cmd/logs.rs index e7816afa3898..e97521fe92df 100644 --- a/crates/cast/bin/cmd/logs.rs +++ b/crates/cast/bin/cmd/logs.rs @@ -1,18 +1,14 @@ +use alloy_dyn_abi::{DynSolType, DynSolValue, Specifier}; +use alloy_json_abi::Event; +use alloy_primitives::{Address, B256}; +use alloy_rpc_types::{BlockId, BlockNumberOrTag, Filter, FilterBlockOption, FilterSet, Topic}; use cast::Cast; use clap::Parser; -use ethers_core::{ - abi::{ - token::{LenientTokenizer, StrictTokenizer, Tokenizer}, - Address, Event, HumanReadableParser, ParamType, RawTopicFilter, Token, Topic, TopicFilter, - }, - types::{ - BlockId, BlockNumber, Filter, FilterBlockOption, NameOrAddress, ValueOrArray, H256, U256, - }, -}; -use ethers_providers::Middleware; -use eyre::{Result, WrapErr}; +use eyre::Result; use foundry_cli::{opts::EthereumOpts, utils}; +use foundry_common::ens::NameOrAddress; use foundry_config::Config; +use hex::FromHex; use itertools::Itertools; use std::{io, str::FromStr}; @@ -80,13 +76,7 @@ impl LogsArgs { let cast = Cast::new(&provider); let address = match address { - Some(address) => { - let address = match address { - NameOrAddress::Name(name) => provider.resolve_name(&name).await?, - NameOrAddress::Address(address) => address, - }; - Some(address) - } + Some(address) => Some(address.resolve(&provider).await?), None => None, }; @@ -114,47 +104,36 @@ impl LogsArgs { /// successful, `topics_or_args` is parsed as indexed inputs and converted to topics. Otherwise, /// `sig_or_topic` is prepended to `topics_or_args` and used as raw topics. fn build_filter( - from_block: Option, - to_block: Option, + from_block: Option, + to_block: Option, address: Option
, sig_or_topic: Option, topics_or_args: Vec, ) -> Result { let block_option = FilterBlockOption::Range { from_block, to_block }; - let topic_filter = match sig_or_topic { + let filter = match sig_or_topic { // Try and parse the signature as an event signature - Some(sig_or_topic) => match HumanReadableParser::parse_event(sig_or_topic.as_str()) { + Some(sig_or_topic) => match foundry_common::abi::get_event(sig_or_topic.as_str()) { Ok(event) => build_filter_event_sig(event, topics_or_args)?, Err(_) => { let topics = [vec![sig_or_topic], topics_or_args].concat(); build_filter_topics(topics)? } }, - None => TopicFilter::default(), + None => Filter::default(), }; - // Convert from TopicFilter to Filter - let topics = - vec![topic_filter.topic0, topic_filter.topic1, topic_filter.topic2, topic_filter.topic3] - .into_iter() - .map(|topic| match topic { - Topic::Any => None, - Topic::This(topic) => Some(ValueOrArray::Value(Some(topic))), - _ => unreachable!(), - }) - .collect::>(); - - let filter = Filter { - block_option, - address: address.map(ValueOrArray::Value), - topics: [topics[0].clone(), topics[1].clone(), topics[2].clone(), topics[3].clone()], - }; + let mut filter = filter.select(block_option); + + if let Some(address) = address { + filter = filter.address(address) + } Ok(filter) } -/// Creates a TopicFilter from the given event signature and arguments. -fn build_filter_event_sig(event: Event, args: Vec) -> Result { +/// Creates a [Filter] from the given event signature and arguments. +fn build_filter_event_sig(event: Event, args: Vec) -> Result { let args = args.iter().map(|arg| arg.as_str()).collect::>(); // Match the args to indexed inputs. Enumerate so that the ordering can be restored @@ -164,128 +143,75 @@ fn build_filter_event_sig(event: Event, args: Vec) -> Result>>()? + .into_iter() .enumerate() .partition(|(_, (_, arg))| !arg.is_empty()); // Only parse the inputs with arguments - let indexed_tokens = parse_params(with_args.iter().map(|(_, p)| *p), true)?; + let indexed_tokens = with_args + .iter() + .map(|(_, (kind, arg))| kind.coerce_str(arg)) + .collect::, _>>()?; // Merge the inputs restoring the original ordering - let mut tokens = with_args + let mut topics = with_args .into_iter() .zip(indexed_tokens) .map(|((i, _), t)| (i, Some(t))) .chain(without_args.into_iter().map(|(i, _)| (i, None))) .sorted_by(|(i1, _), (i2, _)| i1.cmp(i2)) - .map(|(_, token)| token) - .collect::>(); + .map(|(_, token)| { + token + .map(|token| Topic::from(B256::from_slice(token.abi_encode().as_slice()))) + .unwrap_or(Topic::default()) + }) + .collect::>(); - tokens.resize(3, None); + topics.resize(3, Topic::default()); - let raw = RawTopicFilter { - topic0: tokens[0].clone().map_or(Topic::Any, Topic::This), - topic1: tokens[1].clone().map_or(Topic::Any, Topic::This), - topic2: tokens[2].clone().map_or(Topic::Any, Topic::This), - }; + let filter = Filter::new() + .event_signature(event.selector()) + .topic1(topics[0].clone()) + .topic2(topics[1].clone()) + .topic3(topics[2].clone()); - // Let filter do the hardwork of converting arguments to topics - Ok(event.filter(raw)?) + Ok(filter) } -/// Creates a TopicFilter from raw topic hashes. -fn build_filter_topics(topics: Vec) -> Result { +/// Creates a [Filter] from raw topic hashes. +fn build_filter_topics(topics: Vec) -> Result { let mut topics = topics .into_iter() - .map(|topic| if topic.is_empty() { Ok(None) } else { H256::from_str(&topic).map(Some) }) - .collect::, _>>()?; - - topics.resize(4, None); - - Ok(TopicFilter { - topic0: topics[0].map_or(Topic::Any, Topic::This), - topic1: topics[1].map_or(Topic::Any, Topic::This), - topic2: topics[2].map_or(Topic::Any, Topic::This), - topic3: topics[3].map_or(Topic::Any, Topic::This), - }) -} - -fn parse_params<'a, I: IntoIterator>( - params: I, - lenient: bool, -) -> eyre::Result> { - let mut tokens = Vec::new(); - - for (param, value) in params { - let mut token = if lenient { - LenientTokenizer::tokenize(param, value) - } else { - StrictTokenizer::tokenize(param, value) - }; - if token.is_err() && value.starts_with("0x") { - match param { - ParamType::FixedBytes(32) => { - if value.len() < 66 { - let padded_value = [value, &"0".repeat(66 - value.len())].concat(); - token = if lenient { - LenientTokenizer::tokenize(param, &padded_value) - } else { - StrictTokenizer::tokenize(param, &padded_value) - }; - } - } - ParamType::Uint(_) => { - // try again if value is hex - if let Ok(value) = U256::from_str(value).map(|v| v.to_string()) { - token = if lenient { - LenientTokenizer::tokenize(param, &value) - } else { - StrictTokenizer::tokenize(param, &value) - }; - } - } - // TODO: Not sure what to do here. Put the no effect in for now, but that is not - // ideal. We could attempt massage for every value type? - _ => {} + .map(|topic| { + if topic.is_empty() { + Ok(Topic::default()) + } else { + Ok(Topic::from(B256::from_hex(topic.as_str())?)) } - } + }) + .collect::>>>()?; - let token = token.map(sanitize_token).wrap_err_with(|| { - format!("Failed to parse `{value}`, expected value of type: {param}") - })?; - tokens.push(token); - } - Ok(tokens) -} + topics.resize(4, Topic::default()); -pub fn sanitize_token(token: Token) -> Token { - match token { - Token::Array(tokens) => { - let mut sanitized = Vec::with_capacity(tokens.len()); - for token in tokens { - let token = match token { - Token::String(val) => { - let val = match val.as_str() { - // this is supposed to be an empty string - "\"\"" | "''" => String::new(), - _ => val, - }; - Token::String(val) - } - _ => sanitize_token(token), - }; - sanitized.push(token) - } - Token::Array(sanitized) - } - _ => token, - } + let filter = Filter::new() + .event_signature(topics[0].clone()) + .topic1(topics[1].clone()) + .topic2(topics[2].clone()) + .topic3(topics[3].clone()); + + Ok(filter) } #[cfg(test)] mod tests { use super::*; - use ethers_core::types::H160; + use alloy_primitives::{U160, U256 as rU256}; + use alloy_rpc_types::ValueOrArray; const ADDRESS: &str = "0x4D1A2e2bB4F88F0250f26Ffff098B0b30B26BF38"; const TRANSFER_SIG: &str = "Transfer(address indexed,address indexed,uint256)"; @@ -294,13 +220,13 @@ mod tests { #[test] fn test_build_filter_basic() { - let from_block = Some(BlockNumber::from(1337)); - let to_block = Some(BlockNumber::Latest); + let from_block = Some(BlockNumberOrTag::from(1337)); + let to_block = Some(BlockNumberOrTag::Latest); let address = Address::from_str(ADDRESS).ok(); let expected = Filter { block_option: FilterBlockOption::Range { from_block, to_block }, - address: Some(ValueOrArray::Value(address.unwrap())), - topics: [None, None, None, None], + address: ValueOrArray::Value(address.unwrap()).into(), + topics: [vec![].into(), vec![].into(), vec![].into(), vec![].into()], }; let filter = build_filter(from_block, to_block, address, None, vec![]).unwrap(); assert_eq!(filter, expected) @@ -310,8 +236,13 @@ mod tests { fn test_build_filter_sig() { let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, - topics: [Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), None, None, None], + address: vec![].into(), + topics: [ + B256::from_str(TRANSFER_TOPIC).unwrap().into(), + vec![].into(), + vec![].into(), + vec![].into(), + ], }; let filter = build_filter(None, None, None, Some(TRANSFER_SIG.to_string()), vec![]).unwrap(); @@ -322,8 +253,13 @@ mod tests { fn test_build_filter_mismatch() { let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, - topics: [Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), None, None, None], + address: vec![].into(), + topics: [ + B256::from_str(TRANSFER_TOPIC).unwrap().into(), + vec![].into(), + vec![].into(), + vec![].into(), + ], }; let filter = build_filter( None, @@ -338,14 +274,16 @@ mod tests { #[test] fn test_build_filter_sig_with_arguments() { + let addr = Address::from_str(ADDRESS).unwrap(); + let addr = rU256::from(U160::from_be_bytes(addr.0 .0)); let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, + address: vec![].into(), topics: [ - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - Some(H160::from_str(ADDRESS).unwrap().into()), - None, - None, + B256::from_str(TRANSFER_TOPIC).unwrap().into(), + addr.into(), + vec![].into(), + vec![].into(), ], }; let filter = build_filter( @@ -361,14 +299,16 @@ mod tests { #[test] fn test_build_filter_sig_with_skipped_arguments() { + let addr = Address::from_str(ADDRESS).unwrap(); + let addr = rU256::from(U160::from_be_bytes(addr.0 .0)); let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, + address: vec![].into(), topics: [ - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - None, - Some(H160::from_str(ADDRESS).unwrap().into()), - None, + vec![B256::from_str(TRANSFER_TOPIC).unwrap()].into(), + vec![].into(), + addr.into(), + vec![].into(), ], }; let filter = build_filter( @@ -386,12 +326,12 @@ mod tests { fn test_build_filter_with_topics() { let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, + address: vec![].into(), topics: [ - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - None, - None, + vec![B256::from_str(TRANSFER_TOPIC).unwrap()].into(), + vec![B256::from_str(TRANSFER_TOPIC).unwrap()].into(), + vec![].into(), + vec![].into(), ], }; let filter = build_filter( @@ -410,12 +350,12 @@ mod tests { fn test_build_filter_with_skipped_topic() { let expected = Filter { block_option: FilterBlockOption::Range { from_block: None, to_block: None }, - address: None, + address: vec![].into(), topics: [ - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - None, - Some(H256::from_str(TRANSFER_TOPIC).unwrap().into()), - None, + vec![B256::from_str(TRANSFER_TOPIC).unwrap()].into(), + vec![].into(), + vec![B256::from_str(TRANSFER_TOPIC).unwrap()].into(), + vec![].into(), ], }; let filter = build_filter( @@ -443,7 +383,7 @@ mod tests { .unwrap() .to_string(); - assert_eq!(err, "Failed to parse `1234`, expected value of type: address"); + assert_eq!(err, "parser error:\n1234\n^\nInvalid string length"); } #[test] @@ -453,7 +393,7 @@ mod tests { .unwrap() .to_string(); - assert_eq!(err, "Invalid character 's' at position 1"); + assert_eq!(err, "Odd number of digits"); } #[test] @@ -463,7 +403,7 @@ mod tests { .unwrap() .to_string(); - assert_eq!(err, "Invalid input length"); + assert_eq!(err, "Invalid string length"); } #[test] @@ -479,6 +419,6 @@ mod tests { .unwrap() .to_string(); - assert_eq!(err, "Invalid input length"); + assert_eq!(err, "Invalid string length"); } } diff --git a/crates/cast/bin/cmd/mktx.rs b/crates/cast/bin/cmd/mktx.rs index dc7f78461dd3..def6d46f4b27 100644 --- a/crates/cast/bin/cmd/mktx.rs +++ b/crates/cast/bin/cmd/mktx.rs @@ -1,15 +1,15 @@ use crate::tx; +use alloy_network::{eip2718::Encodable2718, EthereumSigner, TransactionBuilder}; +use alloy_primitives::U64; +use alloy_provider::Provider; +use alloy_signer::Signer; use clap::Parser; -use ethers_core::types::NameOrAddress; -use ethers_middleware::MiddlewareBuilder; -use ethers_providers::Middleware; -use ethers_signers::Signer; use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, - utils, + utils::{self, get_provider}, }; -use foundry_common::types::ToAlloy; +use foundry_common::ens::NameOrAddress; use foundry_config::Config; use std::str::FromStr; @@ -45,7 +45,7 @@ pub struct MakeTxArgs { #[derive(Debug, Parser)] pub enum MakeTxSubcommands { /// Use to deploy raw contract bytecode. - #[clap(name = "--create")] + #[command(name = "--create")] Create { /// The initialization bytecode of the contract to deploy. code: String, @@ -86,23 +86,21 @@ impl MakeTxArgs { let signer = eth.wallet.signer().await?; let from = signer.address(); - tx::validate_from_address(eth.wallet.from, from.to_alloy())?; + tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = Some(provider.get_transaction_count(from, None).await?.to_alloy()); + tx.nonce = Some(U64::from(provider.get_transaction_count(from, None).await?)); } - let provider = provider.with_signer(signer); + let provider = get_provider(&config)?; - let (mut tx, _) = + let (tx, _) = tx::build_tx(&provider, from, to, code, sig, args, tx, chain, api_key).await?; - // Fill nonce, gas limit, gas price, and max priority fee per gas if needed - provider.fill_transaction(&mut tx, None).await?; + let tx = tx.build(&EthereumSigner::new(signer)).await?; - let signature = provider.sign_transaction(&tx, from).await?; - let signed_tx = tx.rlp_signed(&signature); - println!("{signed_tx}"); + let signed_tx = hex::encode(tx.encoded_2718()); + println!("0x{signed_tx}"); Ok(()) } diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 3fc09565cde6..33c26e2a907b 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -1,5 +1,5 @@ use alloy_primitives::U256; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use alloy_rpc_types::BlockTransactions; use cast::revm::primitives::EnvWithHandlerCfg; use clap::Parser; @@ -109,19 +109,15 @@ impl RunArgs { .wrap_err_with(|| format!("tx not found: {:?}", tx_hash))?; // check if the tx is a system transaction - if is_known_system_sender(tx.from) || - tx.transaction_type.map(|ty| ty.to::()) == Some(SYSTEM_TRANSACTION_TYPE) - { + if is_known_system_sender(tx.from) || tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) { return Err(eyre::eyre!( "{:?} is a system transaction.\nReplaying system transactions is currently not supported.", tx.hash )); } - let tx_block_number = tx - .block_number - .ok_or_else(|| eyre::eyre!("tx may still be pending: {:?}", tx_hash))? - .to::(); + let tx_block_number = + tx.block_number.ok_or_else(|| eyre::eyre!("tx may still be pending: {:?}", tx_hash))?; // fetch the block the transaction was mined in let block = provider.get_block(tx_block_number.into(), true).await?; @@ -136,12 +132,12 @@ impl RunArgs { env.block.number = U256::from(tx_block_number); if let Some(block) = &block { - env.block.timestamp = block.header.timestamp; + env.block.timestamp = U256::from(block.header.timestamp); env.block.coinbase = block.header.miner; env.block.difficulty = block.header.difficulty; env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); - env.block.basefee = block.header.base_fee_per_gas.unwrap_or_default(); - env.block.gas_limit = block.header.gas_limit; + env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default()); + env.block.gas_limit = U256::from(block.header.gas_limit); // TODO: we need a smarter way to map the block to the corresponding evm_version for // commonly used chains @@ -174,8 +170,7 @@ impl RunArgs { // we skip them otherwise this would cause // reverts if is_known_system_sender(tx.from) || - tx.transaction_type.map(|ty| ty.to::()) == - Some(SYSTEM_TRANSACTION_TYPE) + tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) { update_progress!(pb, index); continue; diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index 092d759eb022..b24de5fba294 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -1,19 +1,17 @@ use crate::tx; +use alloy_network::{AnyNetwork, EthereumSigner}; +use alloy_primitives::{Address, U64}; +use alloy_provider::{Provider, ProviderBuilder}; +use alloy_signer::Signer; +use alloy_transport::Transport; use cast::Cast; use clap::Parser; -use ethers_core::types::NameOrAddress; -use ethers_middleware::SignerMiddleware; -use ethers_providers::Middleware; -use ethers_signers::Signer; use eyre::Result; use foundry_cli::{ opts::{EthereumOpts, TransactionOpts}, utils, }; -use foundry_common::{ - cli_warn, - types::{ToAlloy, ToEthers}, -}; +use foundry_common::{cli_warn, ens::NameOrAddress}; use foundry_config::{Chain, Config}; use std::str::FromStr; @@ -38,7 +36,7 @@ pub struct SendTxArgs { /// The number of confirmations until the receipt is fetched. #[arg(long, default_value = "1")] - confirmations: usize, + confirmations: u64, /// Print the transaction receipt as JSON. #[arg(long, short, help_heading = "Display options")] @@ -114,6 +112,11 @@ impl SendTxArgs { let chain = utils::get_chain(config.chain, &provider).await?; let api_key = config.get_etherscan_api_key(Some(chain)); + let to = match to { + Some(to) => Some(to.resolve(&provider).await?), + None => None, + }; + // Case 1: // Default to sending via eth_sendTransaction if the --unlocked flag is passed. // This should be the only way this RPC method is used as it requires a local node @@ -121,15 +124,15 @@ impl SendTxArgs { if unlocked { // only check current chain id if it was specified in the config if let Some(config_chain) = config.chain { - let current_chain_id = provider.get_chainid().await?.as_u64(); + let current_chain_id = provider.get_chain_id().await?; let config_chain_id = config_chain.id(); // switch chain if current chain id is not the same as the one specified in the // config if config_chain_id != current_chain_id { cli_warn!("Switching to chain {}", config_chain); provider - .request( - "wallet_switchEthereumChain", + .raw_request( + "wallet_switchEthereumChain".into(), [serde_json::json!({ "chainId": format!("0x{:x}", config_chain_id), })], @@ -139,17 +142,13 @@ impl SendTxArgs { } if resend { - tx.nonce = Some( - provider - .get_transaction_count(config.sender.to_ethers(), None) - .await? - .to_alloy(), - ); + tx.nonce = + Some(U64::from(provider.get_transaction_count(config.sender, None).await?)); } cast_send( provider, - config.sender.to_ethers(), + config.sender, to, code, sig, @@ -171,13 +170,15 @@ impl SendTxArgs { let signer = eth.wallet.signer().await?; let from = signer.address(); - tx::validate_from_address(eth.wallet.from, from.to_alloy())?; + tx::validate_from_address(eth.wallet.from, from)?; if resend { - tx.nonce = Some(provider.get_transaction_count(from, None).await?.to_alloy()); + tx.nonce = Some(U64::from(provider.get_transaction_count(from, None).await?)); } - let provider = SignerMiddleware::new_with_provider_chain(provider, signer).await?; + let signer = EthereumSigner::from(signer); + let provider = + ProviderBuilder::<_, _, AnyNetwork>::default().signer(signer).on_provider(provider); cast_send( provider, @@ -199,10 +200,10 @@ impl SendTxArgs { } #[allow(clippy::too_many_arguments)] -async fn cast_send, T: Into>( - provider: M, - from: F, - to: Option, +async fn cast_send, T: Transport + Clone>( + provider: P, + from: Address, + to: Option
, code: Option, sig: Option, args: Vec, @@ -210,19 +211,16 @@ async fn cast_send, T: Into chain: Chain, etherscan_api_key: Option, cast_async: bool, - confs: usize, + confs: u64, to_json: bool, -) -> Result<()> -where - M::Error: 'static, -{ - let builder_output = +) -> Result<()> { + let (tx, _) = tx::build_tx(&provider, from, to, code, sig, args, tx, chain, etherscan_api_key).await?; let cast = Cast::new(provider); - let pending_tx = cast.send(builder_output).await?; - let tx_hash = *pending_tx; + let pending_tx = cast.send(tx).await?; + let tx_hash = pending_tx.inner().tx_hash(); if cast_async { println!("{tx_hash:#x}"); diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index 397bd07e2b1b..0bf041b8a9cf 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -1,10 +1,12 @@ use crate::opts::parse_slot; -use alloy_primitives::{B256, U256}; +use alloy_network::AnyNetwork; +use alloy_primitives::{Address, B256, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::BlockId; +use alloy_transport::Transport; use cast::Cast; use clap::Parser; use comfy_table::{presets::ASCII_MARKDOWN, Table}; -use ethers_core::types::{BlockId, NameOrAddress}; -use ethers_providers::Middleware; use eyre::Result; use foundry_block_explorers::Client; use foundry_cli::{ @@ -14,8 +16,7 @@ use foundry_cli::{ use foundry_common::{ abi::find_source, compile::{etherscan_project, ProjectCompiler}, - provider::ethers::RetryProvider, - types::{ToAlloy, ToEthers}, + ens::NameOrAddress, }; use foundry_compilers::{ artifacts::StorageLayout, Artifact, ConfigurableContractArtifact, Project, Solc, @@ -80,19 +81,19 @@ impl StorageArgs { let config = Config::from(&self); let Self { address, slot, block, build, .. } = self; - let provider = utils::get_provider(&config)?; + let address = address.resolve(&provider).await?; // Slot was provided, perform a simple RPC call if let Some(slot) = slot { let cast = Cast::new(provider); - println!("{}", cast.storage(address, slot.to_ethers(), block).await?); + println!("{}", cast.storage(address, slot, block).await?); return Ok(()); } // No slot was provided // Get deployed bytecode at given address - let address_code = provider.get_code(address.clone(), block).await?.to_alloy(); + let address_code = provider.get_code_at(address, block.unwrap_or_default()).await?; if address_code.is_empty() { eyre::bail!("Provided address has no deployed code and thus no storage"); } @@ -107,8 +108,7 @@ impl StorageArgs { artifact.get_deployed_bytecode_bytes().is_some_and(|b| *b == address_code) }); if let Some((_, artifact)) = artifact { - return fetch_and_print_storage(provider, address.clone(), block, artifact, true) - .await; + return fetch_and_print_storage(provider, address, block, artifact, true).await; } } @@ -123,11 +123,7 @@ impl StorageArgs { let chain = utils::get_chain(config.chain, &provider).await?; let api_key = config.get_etherscan_api_key(Some(chain)).unwrap_or_default(); let client = Client::new(chain, api_key)?; - let addr = address - .as_address() - .ok_or_else(|| eyre::eyre!("Could not resolve address"))? - .to_alloy(); - let source = find_source(client, addr).await?; + let source = find_source(client, address).await?; let metadata = source.items.first().unwrap(); if metadata.is_vyper() { eyre::bail!("Contract at provided address is not a valid Solidity contract") @@ -209,9 +205,9 @@ impl StorageValue { } } -async fn fetch_and_print_storage( - provider: RetryProvider, - address: NameOrAddress, +async fn fetch_and_print_storage, T: Transport + Clone>( + provider: P, + address: Address, block: Option, artifact: &ConfigurableContractArtifact, pretty: bool, @@ -226,18 +222,17 @@ async fn fetch_and_print_storage( } } -async fn fetch_storage_slots( - provider: RetryProvider, - address: NameOrAddress, +async fn fetch_storage_slots, T: Transport + Clone>( + provider: P, + address: Address, block: Option, layout: &StorageLayout, ) -> Result> { let requests = layout.storage.iter().map(|storage_slot| async { let slot = B256::from(U256::from_str(&storage_slot.slot)?); - let raw_slot_value = - provider.get_storage_at(address.clone(), slot.to_ethers(), block).await?.to_alloy(); + let raw_slot_value = provider.get_storage_at(address, slot.into(), block).await?; - let value = StorageValue { slot, raw_slot_value }; + let value = StorageValue { slot, raw_slot_value: raw_slot_value.into() }; Ok(value) }); diff --git a/crates/cast/bin/cmd/wallet/list.rs b/crates/cast/bin/cmd/wallet/list.rs index 2790366a37c7..d4ca3f013437 100644 --- a/crates/cast/bin/cmd/wallet/list.rs +++ b/crates/cast/bin/cmd/wallet/list.rs @@ -1,7 +1,7 @@ use clap::Parser; use eyre::Result; -use foundry_common::{fs, types::ToAlloy}; +use foundry_common::fs; use foundry_config::Config; use foundry_wallets::multi_wallet::MultiWalletOptsBuilder; @@ -61,7 +61,7 @@ impl ListArgs { .available_senders(self.max_senders.unwrap()) .await? .iter() - .for_each(|sender| println!("{} ({})", sender.to_alloy(), $label)); + .for_each(|sender| println!("{} ({})", sender, $label)); } } Err(e) => { diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 50467421c415..60e21a2fb732 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -1,13 +1,13 @@ +use alloy_dyn_abi::TypedData; use alloy_primitives::{Address, Signature}; -use alloy_signer::{ +use alloy_signer::Signer; +use alloy_signer_wallet::{ coins_bip39::{English, Mnemonic}, - LocalWallet, MnemonicBuilder, Signer as AlloySigner, + LocalWallet, MnemonicBuilder, }; use clap::Parser; -use ethers_core::types::transaction::eip712::TypedData; -use ethers_signers::Signer; use eyre::{Context, Result}; -use foundry_common::{fs, types::ToAlloy}; +use foundry_common::fs; use foundry_config::Config; use foundry_wallets::{RawWalletOpts, WalletOpts, WalletSigner}; use rand::thread_rng; @@ -258,7 +258,7 @@ impl WalletSubcommands { .signer() .await?; let addr = wallet.address(); - println!("{}", addr.to_alloy().to_checksum(None)); + println!("{}", addr.to_checksum(None)); } WalletSubcommands::Sign { message, data, from_file, no_hash, wallet } => { let wallet = wallet.signer().await?; @@ -270,13 +270,13 @@ impl WalletSubcommands { // data is a json string serde_json::from_str(&message)? }; - wallet.sign_typed_data(&typed_data).await? + wallet.sign_dynamic_typed_data(&typed_data).await? } else if no_hash { - wallet.sign_hash(&message.parse()?).await? + wallet.sign_hash(&hex::decode(&message)?[..].try_into()?).await? } else { - wallet.sign_message(Self::hex_str_to_bytes(&message)?).await? + wallet.sign_message(&Self::hex_str_to_bytes(&message)?).await? }; - println!("0x{sig}"); + println!("0x{}", hex::encode(sig.as_bytes())); } WalletSubcommands::Verify { message, signature, address } => { let recovered_address = Self::recover_address_from_message(&message, &signature)?; @@ -335,11 +335,11 @@ flag to set your key via: }; let mut rng = thread_rng(); - eth_keystore::encrypt_key( - &dir, + let (wallet, _) = LocalWallet::encrypt_keystore( + dir, &mut rng, private_key, - &password, + password, Some(&account_name), )?; let address = wallet.address(); diff --git a/crates/cast/bin/cmd/wallet/vanity.rs b/crates/cast/bin/cmd/wallet/vanity.rs index c3485ddfa96f..28ada95b11cb 100644 --- a/crates/cast/bin/cmd/wallet/vanity.rs +++ b/crates/cast/bin/cmd/wallet/vanity.rs @@ -1,5 +1,6 @@ use alloy_primitives::Address; -use alloy_signer::{k256::ecdsa::SigningKey, utils::secret_key_to_address, LocalWallet, Signer}; +use alloy_signer::{k256::ecdsa::SigningKey, utils::secret_key_to_address}; +use alloy_signer_wallet::LocalWallet; use clap::{builder::TypedValueParser, Parser}; use eyre::Result; use rayon::iter::{self, ParallelIterator}; diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index a81c1acd8b5a..8c0181367b70 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -2,24 +2,23 @@ extern crate tracing; use alloy_primitives::{keccak256, Address, B256}; -use cast::{Cast, SimpleCast, TxBuilder}; +use alloy_provider::Provider; +use alloy_rpc_types::{BlockId, BlockNumberOrTag::Latest}; +use cast::{Cast, SimpleCast}; use clap::{CommandFactory, Parser}; use clap_complete::generate; -use ethers_core::types::{BlockId, BlockNumber::Latest, NameOrAddress}; -use ethers_providers::{Middleware, Provider}; use eyre::Result; use foundry_cli::{handler, prompt, stdin, utils}; use foundry_common::{ abi::get_event, - fmt::format_tokens, + ens::ProviderEnsExt, + fmt::{format_tokens, format_uint_exp}, fs, - runtime_client::RuntimeClient, selectors::{ decode_calldata, decode_event_topic, decode_function_selector, decode_selectors, import_selectors, parse_signatures, pretty_calldata, ParsedSignatures, SelectorImportData, SelectorType, }, - types::{ToAlloy, ToEthers}, }; use foundry_config::Config; use std::time::Instant; @@ -214,35 +213,16 @@ async fn main() -> Result<()> { CastSubcommand::Balance { block, who, ether, rpc, erc20 } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let account_addr = who.resolve(&provider).await?; match erc20 { Some(token) => { - let chain = utils::get_chain(config.chain, &provider).await?; - let mut builder: TxBuilder<'_, Provider> = TxBuilder::new( - &provider, - NameOrAddress::Address(Address::ZERO.to_ethers()), - Some(NameOrAddress::Address(token.to_ethers())), - chain, - true, - ) - .await?; - - let account_addr = match who { - NameOrAddress::Name(ens_name) => provider.resolve_name(&ens_name).await?, - NameOrAddress::Address(addr) => addr, - }; - - builder - .set_args( - "balanceOf(address) returns (uint256)", - vec![format!("{account_addr:#x}")], - ) - .await?; - let builder_output = builder.build(); - println!("{}", Cast::new(provider).call(builder_output, block).await?); + let balance = + Cast::new(&provider).erc20_balance(token, account_addr, block).await?; + println!("{}", format_uint_exp(balance)); } None => { - let value = Cast::new(provider).balance(who, block).await?; + let value = Cast::new(&provider).balance(account_addr, block).await?; if ether { println!("{}", SimpleCast::from_wei(&value.to_string(), "eth")?); } else { @@ -287,16 +267,18 @@ async fn main() -> Result<()> { CastSubcommand::Client { rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; - println!("{}", provider.client_version().await?); + println!("{}", provider.get_client_version().await?); } CastSubcommand::Code { block, who, disassemble, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; println!("{}", Cast::new(provider).code(who, block, disassemble).await?); } CastSubcommand::Codesize { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; println!("{}", Cast::new(provider).codesize(who, block).await?); } CastSubcommand::ComputeAddress { address, nonce, rpc } => { @@ -304,7 +286,7 @@ async fn main() -> Result<()> { let provider = utils::get_provider(&config)?; let address: Address = stdin::unwrap_line(address)?.parse()?; - let computed = Cast::new(&provider).compute_address(address, nonce).await?; + let computed = Cast::new(provider).compute_address(address, nonce).await?; println!("Computed Address: {}", computed.to_checksum(None)); } CastSubcommand::Disassemble { bytecode } => { @@ -345,24 +327,26 @@ async fn main() -> Result<()> { CastSubcommand::Implementation { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; println!("{}", Cast::new(provider).implementation(who, block).await?); } CastSubcommand::Admin { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; println!("{}", Cast::new(provider).admin(who, block).await?); } CastSubcommand::Nonce { block, who, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; + let who = who.resolve(&provider).await?; println!("{}", Cast::new(provider).nonce(who, block).await?); } CastSubcommand::Proof { address, slots, rpc, block } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; - let value = provider - .get_proof(address, slots.into_iter().map(|s| s.to_ethers()).collect(), block) - .await?; + let address = address.resolve(&provider).await?; + let value = provider.get_proof(address, slots.into_iter().collect(), block).await?; println!("{}", serde_json::to_string(&value)?); } CastSubcommand::Rpc(cmd) => cmd.run().await?, @@ -377,13 +361,12 @@ async fn main() -> Result<()> { let provider = utils::get_provider(&config)?; let cast = Cast::new(&provider); let pending_tx = cast.publish(raw_tx).await?; - let tx_hash = *pending_tx; + let tx_hash = pending_tx.inner().tx_hash(); if cast_async { println!("{tx_hash:#x}"); } else { - let receipt = - pending_tx.await?.ok_or_else(|| eyre::eyre!("tx {tx_hash} not found"))?; + let receipt = pending_tx.get_receipt().await?; println!("{}", serde_json::json!(receipt)); } } @@ -470,9 +453,9 @@ async fn main() -> Result<()> { let provider = utils::get_provider(&config)?; let who = stdin::unwrap_line(who)?; - let name = provider.lookup_address(who.to_ethers()).await?; + let name = provider.lookup_address(who).await?; if verify { - let address = provider.resolve_name(&name).await?.to_alloy(); + let address = provider.resolve_name(&name).await?; eyre::ensure!( address == who, "Forward lookup verification failed: got `{name:?}`, expected `{who:?}`" @@ -493,7 +476,7 @@ async fn main() -> Result<()> { "forward lookup verification failed. got {name}, expected {who}" ); } - println!("{}", address.to_alloy().to_checksum(None)); + println!("{}", address.to_checksum(None)); } // Misc @@ -555,14 +538,7 @@ async fn main() -> Result<()> { CastSubcommand::Logs(cmd) => cmd.run().await?, CastSubcommand::DecodeTransaction { tx } => { let tx = stdin::unwrap_line(tx)?; - let (tx, sig) = SimpleCast::decode_raw_transaction(&tx)?; - - // Serialize tx, sig and constructed a merged json string - let mut tx = serde_json::to_value(&tx)?; - let tx_map = tx.as_object_mut().unwrap(); - serde_json::to_value(sig)?.as_object().unwrap().iter().for_each(|(k, v)| { - tx_map.entry(k).or_insert(v.clone()); - }); + let tx = SimpleCast::decode_raw_transaction(&tx)?; println!("{}", serde_json::to_string_pretty(&tx)?); } diff --git a/crates/cast/bin/opts.rs b/crates/cast/bin/opts.rs index 5a1af1fdc20a..51f32b145c83 100644 --- a/crates/cast/bin/opts.rs +++ b/crates/cast/bin/opts.rs @@ -5,10 +5,11 @@ use crate::cmd::{ wallet::WalletSubcommands, }; use alloy_primitives::{Address, B256, U256}; +use alloy_rpc_types::BlockId; use clap::{Parser, Subcommand, ValueHint}; -use ethers_core::types::{BlockId, NameOrAddress}; use eyre::Result; use foundry_cli::opts::{EtherscanOpts, RpcOpts}; +use foundry_common::ens::NameOrAddress; use std::{path::PathBuf, str::FromStr}; const VERSION_MESSAGE: &str = concat!( @@ -423,7 +424,7 @@ pub enum CastSubcommand { /// The number of confirmations until the receipt is fetched #[arg(long, default_value = "1")] - confirmations: usize, + confirmations: u64, /// Exit immediately if the transaction was not found. #[arg(id = "async", long = "async", env = "CAST_ASYNC", alias = "cast-async")] @@ -906,9 +907,9 @@ pub fn parse_slot(s: &str) -> Result { #[cfg(test)] mod tests { use super::*; + use alloy_rpc_types::{BlockNumberOrTag, RpcBlockHash}; use cast::SimpleCast; use clap::CommandFactory; - use ethers_core::types::BlockNumber; #[test] fn verify_cli() { @@ -997,30 +998,34 @@ mod tests { let test_cases = [ TestCase { input: "0".to_string(), - expect: BlockId::Number(BlockNumber::Number(0u64.into())), + expect: BlockId::Number(BlockNumberOrTag::Number(0u64)), }, TestCase { input: "0x56462c47c03df160f66819f0a79ea07def1569f8aac0fe91bb3a081159b61b4a" .to_string(), - expect: BlockId::Hash( + expect: BlockId::Hash(RpcBlockHash::from_hash( "0x56462c47c03df160f66819f0a79ea07def1569f8aac0fe91bb3a081159b61b4a" .parse() .unwrap(), - ), + None, + )), + }, + TestCase { + input: "latest".to_string(), + expect: BlockId::Number(BlockNumberOrTag::Latest), }, - TestCase { input: "latest".to_string(), expect: BlockId::Number(BlockNumber::Latest) }, TestCase { input: "earliest".to_string(), - expect: BlockId::Number(BlockNumber::Earliest), + expect: BlockId::Number(BlockNumberOrTag::Earliest), }, TestCase { input: "pending".to_string(), - expect: BlockId::Number(BlockNumber::Pending), + expect: BlockId::Number(BlockNumberOrTag::Pending), }, - TestCase { input: "safe".to_string(), expect: BlockId::Number(BlockNumber::Safe) }, + TestCase { input: "safe".to_string(), expect: BlockId::Number(BlockNumberOrTag::Safe) }, TestCase { input: "finalized".to_string(), - expect: BlockId::Number(BlockNumber::Finalized), + expect: BlockId::Number(BlockNumberOrTag::Finalized), }, ]; diff --git a/crates/cast/bin/tx.rs b/crates/cast/bin/tx.rs index cd155de011d2..98f7bf67e4c4 100644 --- a/crates/cast/bin/tx.rs +++ b/crates/cast/bin/tx.rs @@ -1,9 +1,12 @@ -use alloy_primitives::Address; -use cast::{TxBuilder, TxBuilderOutput}; -use ethers_core::types::NameOrAddress; -use ethers_providers::Middleware; +use alloy_json_abi::Function; +use alloy_network::{AnyNetwork, TransactionBuilder}; +use alloy_primitives::{Address, U256}; +use alloy_provider::Provider; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_transport::Transport; use eyre::Result; -use foundry_cli::opts::TransactionOpts; +use foundry_cli::{opts::TransactionOpts, utils::parse_function_args}; +use foundry_common::ens::NameOrAddress; use foundry_config::Chain; /// Prevents a misconfigured hwlib from sending a transaction that defies user-specified --from @@ -34,40 +37,86 @@ pub fn validate_to_address(code: &Option, to: &Option) -> } #[allow(clippy::too_many_arguments)] -pub async fn build_tx, T: Into>( - provider: &M, +pub async fn build_tx< + P: Provider, + T: Transport + Clone, + F: Into, + TO: Into, +>( + provider: &P, from: F, - to: Option, + to: Option, code: Option, sig: Option, args: Vec, tx: TransactionOpts, chain: impl Into, etherscan_api_key: Option, -) -> Result { - let mut builder = TxBuilder::new(provider, from, to, chain, tx.legacy).await?; - builder - .etherscan_api_key(etherscan_api_key) - .gas(tx.gas_limit) - .gas_price(tx.gas_price) - .priority_gas_price(tx.priority_gas_price) - .value(tx.value) - .nonce(tx.nonce); +) -> Result<(WithOtherFields, Option)> { + let chain = chain.into(); + + let from = from.into().resolve(provider).await?; + let to = if let Some(to) = to { Some(to.into().resolve(provider).await?) } else { None }; + + let mut req = WithOtherFields::new(TransactionRequest::default()) + .with_to(to.into()) + .with_from(from) + .with_value(tx.value.unwrap_or_default()) + .with_chain_id(chain.id()); + + req.set_nonce(if let Some(nonce) = tx.nonce { + nonce.to() + } else { + provider.get_transaction_count(from, None).await? + }); + + if tx.legacy || chain.is_legacy() { + req.set_gas_price(if let Some(gas_price) = tx.gas_price { + gas_price.to() + } else { + provider.get_gas_price().await? + }); + } else { + let (max_fee, priority_fee) = match (tx.gas_price, tx.priority_gas_price) { + (Some(gas_price), Some(priority_gas_price)) => (gas_price, priority_gas_price), + (_, _) => { + let estimate = provider.estimate_eip1559_fees(None).await?; + ( + tx.gas_price.unwrap_or(U256::from(estimate.max_fee_per_gas)), + tx.priority_gas_price.unwrap_or(U256::from(estimate.max_priority_fee_per_gas)), + ) + } + }; + + req.set_max_fee_per_gas(max_fee.to()); + req.set_max_priority_fee_per_gas(priority_fee.to()); + } let params = sig.as_deref().map(|sig| (sig, args)); - if let Some(code) = code { + let (data, func) = if let Some(code) = code { let mut data = hex::decode(code)?; if let Some((sig, args)) = params { - let (mut sigdata, _) = builder.create_args(sig, args).await?; + let (mut sigdata, _) = + parse_function_args(sig, args, None, chain, provider, etherscan_api_key.as_deref()) + .await?; data.append(&mut sigdata); } - builder.set_data(data); + (data, None) + } else if let Some((sig, args)) = params { + parse_function_args(sig, args, None, chain, provider, etherscan_api_key.as_deref()).await? } else { - builder.args(params).await?; - } + (Vec::new(), None) + }; + + req.set_input(data.into()); + + req.set_gas_limit(if let Some(gas_limit) = tx.gas_limit { + gas_limit.to() + } else { + provider.estimate_gas(&req, None).await? + }); - let builder_output = builder.build(); - Ok(builder_output) + Ok((req, func)) } diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 38498786ea18..338510eb9419 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1,66 +1,70 @@ +use alloy_consensus::TxEnvelope; use alloy_dyn_abi::{DynSolType, DynSolValue, FunctionExt}; -use alloy_json_abi::ContractObject; +use alloy_json_abi::{ContractObject, Function}; +use alloy_network::AnyNetwork; use alloy_primitives::{ utils::{keccak256, ParseUnits, Unit}, - Address, Bytes, Keccak256, B256, I256, U256, + Address, Keccak256, TxHash, B256, I256, U256, +}; +use alloy_provider::{ + network::eip2718::{Decodable2718, Encodable2718}, + PendingTransactionBuilder, Provider, }; use alloy_rlp::Decodable; +use alloy_rpc_types::{BlockId, BlockNumberOrTag, Filter, TransactionRequest, WithOtherFields}; +use alloy_sol_types::sol; +use alloy_transport::Transport; use base::{Base, NumberWithBase, ToBase}; use chrono::DateTime; -use ethers_core::{ - types::{ - transaction::eip2718::TypedTransaction, BlockId, BlockNumber, Filter, NameOrAddress, - Signature, H160, H256, U64, - }, - utils::rlp, -}; -use ethers_providers::{Middleware, PendingTransaction, PubsubClient}; use evm_disassembler::{disassemble_bytes, disassemble_str, format_operations}; use eyre::{Context, ContextCompat, Result}; use foundry_block_explorers::Client; use foundry_common::{ abi::{encode_function_args, get_func}, fmt::*, - types::{ToAlloy, ToEthers}, TransactionReceiptWithRevertReason, }; use foundry_config::Chain; use futures::{future::Either, FutureExt, StreamExt}; use rayon::prelude::*; use std::{ + borrow::Cow, io, + marker::PhantomData, path::PathBuf, str::FromStr, sync::atomic::{AtomicBool, Ordering}, }; use tokio::signal::ctrl_c; -use tx::TxBuilderPeekOutput; use foundry_common::abi::encode_function_args_packed; pub use foundry_evm::*; -pub use rusoto_core::{ - credential::ChainProvider as AwsChainProvider, region::Region as AwsRegion, - request::HttpClient as AwsHttpClient, Client as AwsClient, -}; -pub use rusoto_kms::KmsClient; -pub use tx::{TxBuilder, TxBuilderOutput}; pub mod base; pub mod errors; mod rlp_converter; -mod tx; use rlp_converter::Item; // TODO: CastContract with common contract initializers? Same for CastProviders? -pub struct Cast { - provider: M, +sol! { + #[sol(rpc)] + interface IERC20 { + #[derive(Debug)] + function balanceOf(address owner) external view returns (uint256); + } } -impl Cast +pub struct Cast { + provider: P, + transport: PhantomData, +} + +impl Cast where - M::Error: 'static, + T: Transport + Clone, + P: Provider, { /// Creates a new Cast instance from the provided client /// @@ -68,16 +72,16 @@ where /// /// ``` /// use cast::Cast; - /// use ethers_providers::{Http, Provider}; + /// use foundry_common::provider::alloy::get_http_provider; /// /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let provider = get_http_provider("http://localhost:8545"); /// let cast = Cast::new(provider); /// # Ok(()) /// # } /// ``` - pub fn new(provider: M) -> Self { - Self { provider } + pub fn new(provider: P) -> Self { + Self { provider, transport: PhantomData } } /// Makes a read-only call to the specified address @@ -88,10 +92,12 @@ where /// use cast::{Cast, TxBuilder}; /// use ethers_core::types::Address; /// use ethers_providers::{Http, Provider}; + /// use foundry_common::provider::alloy::get_http_provider; /// use std::str::FromStr; /// /// # async fn foo() -> eyre::Result<()> { /// let provider = Provider::::try_from("http://localhost:8545")?; + /// let alloy_provider = get_http_provider("http://localhost:8545"); /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; /// let sig = "function greeting(uint256 i) public returns (string)"; /// let args = vec!["5".to_owned()]; @@ -99,7 +105,7 @@ where /// TxBuilder::new(&provider, Address::zero(), Some(to), Chain::Mainnet, false).await?; /// builder.set_args(sig, args).await?; /// let builder_output = builder.build(); - /// let cast = Cast::new(provider); + /// let cast = Cast::new(provider, alloy_provider); /// let data = cast.call(builder_output, None).await?; /// println!("{}", data); /// # Ok(()) @@ -107,11 +113,11 @@ where /// ``` pub async fn call<'a>( &self, - builder_output: TxBuilderOutput, + req: &WithOtherFields, + func: Option<&Function>, block: Option, ) -> Result { - let (tx, func) = builder_output; - let res = self.provider.call(&tx, block).await?; + let res = self.provider.call(req, block).await?; let mut decoded = vec![]; @@ -123,8 +129,10 @@ where // ensure the address is a contract if res.is_empty() { // check that the recipient is a contract that can be called - if let Some(NameOrAddress::Address(addr)) = tx.to() { - if let Ok(code) = self.provider.get_code(*addr, block).await { + if let Some(addr) = req.to { + if let Ok(code) = + self.provider.get_code_at(addr, block.unwrap_or_default()).await + { if code.is_empty() { eyre::bail!("contract {addr:?} does not have any code") } @@ -174,19 +182,18 @@ where /// ``` pub async fn access_list( &self, - builder_output: TxBuilderPeekOutput<'_>, + req: &WithOtherFields, block: Option, to_json: bool, ) -> Result { - let (tx, _) = builder_output; - let access_list = self.provider.create_access_list(tx, block).await?; + let access_list = self.provider.create_access_list(req, block).await?; let res = if to_json { serde_json::to_string(&access_list)? } else { let mut s = vec![format!("gas used: {}", access_list.gas_used), "access list:".to_string()]; for al in access_list.access_list.0 { - s.push(format!("- address: {}", &al.address.to_alloy().to_checksum(None))); + s.push(format!("- address: {}", &al.address.to_checksum(None))); if !al.storage_keys.is_empty() { s.push(" keys:".to_string()); for key in al.storage_keys { @@ -200,12 +207,8 @@ where Ok(res) } - pub async fn balance + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { - Ok(self.provider.get_balance(who, block).await?.to_alloy()) + pub async fn balance(&self, who: Address, block: Option) -> Result { + Ok(self.provider.get_balance(who, block).await?) } /// Sends a transaction to the specified address @@ -236,14 +239,13 @@ where /// # Ok(()) /// # } /// ``` - pub async fn send<'a>( + pub async fn send( &self, - builder_output: TxBuilderOutput, - ) -> Result> { - let (tx, _) = builder_output; - let res = self.provider.send_transaction(tx, None).await?; + tx: WithOtherFields, + ) -> Result> { + let res = self.provider.send_transaction(tx).await?; - Ok::<_, eyre::Error>(res) + Ok(res) } /// Publishes a raw transaction to the network @@ -262,50 +264,18 @@ where /// # Ok(()) /// # } /// ``` - pub async fn publish(&self, mut raw_tx: String) -> Result> { + pub async fn publish( + &self, + mut raw_tx: String, + ) -> Result> { raw_tx = match raw_tx.strip_prefix("0x") { Some(s) => s.to_string(), None => raw_tx, }; - let tx = Bytes::from(hex::decode(raw_tx)?); - let res = self.provider.send_raw_transaction(tx.0.into()).await?; - - Ok::<_, eyre::Error>(res) - } - - /// Estimates the gas cost of a transaction - /// - /// # Example - /// - /// ```ignore - /// use alloy_primitives::U256; - /// use cast::{Cast, TxBuilder}; - /// use ethers_core::types::Address; - /// use ethers_providers::{Http, Provider}; - /// use std::str::FromStr; - /// - /// # async fn foo() -> eyre::Result<()> { - /// let provider = Provider::::try_from("http://localhost:8545")?; - /// let from = Address::from_str("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")?; - /// let to = Address::from_str("0xB3C95ff08316fb2F2e3E52Ee82F8e7b605Aa1304")?; - /// let sig = "greet(string)()"; - /// let args = vec!["5".to_owned()]; - /// let value = U256::from_str("1").unwrap(); - /// let mut builder = TxBuilder::new(&provider, from, Some(to), Chain::Mainnet, false).await?; - /// builder.set_value(value).set_args(sig, args).await?; - /// let builder_output = builder.peek(); - /// let cast = Cast::new(&provider); - /// let data = cast.estimate(builder_output).await?; - /// println!("{}", data); - /// # Ok(()) - /// # } - /// ``` - pub async fn estimate(&self, builder_output: TxBuilderPeekOutput<'_>) -> Result { - let (tx, _) = builder_output; + let tx = hex::decode(raw_tx)?; + let res = self.provider.send_raw_transaction(&tx).await?; - let res = self.provider.estimate_gas(tx, None).await?; - - Ok::<_, eyre::Error>(res.to_alloy()) + Ok(res) } /// # Example @@ -322,53 +292,39 @@ where /// # Ok(()) /// # } /// ``` - pub async fn block>( + pub async fn block>( &self, - block: T, + block: B, full: bool, field: Option, to_json: bool, ) -> Result { let block = block.into(); - let block = if full { - let block = self - .provider - .get_block_with_txs(block) - .await? - .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; - if let Some(ref field) = field { - get_pretty_block_attr(&block, field) - .unwrap_or_else(|| format!("{field} is not a valid block field")) - } else if to_json { - serde_json::to_value(&block).unwrap().to_string() - } else { - block.pretty() + if let Some(ref field) = field { + if field == "transactions" && !full { + eyre::bail!("use --full to view transactions") } + } + + let block = self + .provider + .get_block(block, full) + .await? + .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; + + let block = if let Some(ref field) = field { + get_pretty_block_attr(&block, field) + .unwrap_or_else(|| format!("{field} is not a valid block field")) + } else if to_json { + serde_json::to_value(&block).unwrap().to_string() } else { - let block = self - .provider - .get_block(block) - .await? - .ok_or_else(|| eyre::eyre!("block {:?} not found", block))?; - - if let Some(ref field) = field { - if field == "transactions" { - "use --full to view transactions".to_string() - } else { - get_pretty_block_attr(&block, field) - .unwrap_or_else(|| format!("{field} is not a valid block field")) - } - } else if to_json { - serde_json::to_value(&block).unwrap().to_string() - } else { - block.pretty() - } + block.pretty() }; Ok(block) } - async fn block_field_as_num>(&self, block: T, field: String) -> Result { + async fn block_field_as_num>(&self, block: B, field: String) -> Result { let block = block.into(); let block_field = Cast::block( self, @@ -388,18 +344,18 @@ where Ok(ret) } - pub async fn base_fee>(&self, block: T) -> Result { + pub async fn base_fee>(&self, block: B) -> Result { Cast::block_field_as_num(self, block, String::from("baseFeePerGas")).await } - pub async fn age>(&self, block: T) -> Result { + pub async fn age>(&self, block: B) -> Result { let timestamp_str = Cast::block_field_as_num(self, block, String::from("timestamp")).await?.to_string(); let datetime = DateTime::from_timestamp(timestamp_str.parse::().unwrap(), 0).unwrap(); Ok(datetime.format("%a %b %e %H:%M:%S %Y").to_string()) } - pub async fn timestamp>(&self, block: T) -> Result { + pub async fn timestamp>(&self, block: B) -> Result { Cast::block_field_as_num(self, block, "timestamp".to_string()).await } @@ -466,16 +422,16 @@ where }) } - pub async fn chain_id(&self) -> Result { - Ok(self.provider.get_chainid().await?.to_alloy()) + pub async fn chain_id(&self) -> Result { + Ok(self.provider.get_chain_id().await?) } - pub async fn block_number(&self) -> Result { + pub async fn block_number(&self) -> Result { Ok(self.provider.get_block_number().await?) } - pub async fn gas_price(&self) -> Result { - Ok(self.provider.get_gas_price().await?.to_alloy()) + pub async fn gas_price(&self) -> Result { + Ok(self.provider.get_gas_price().await?) } /// # Example @@ -495,12 +451,8 @@ where /// # Ok(()) /// # } /// ``` - pub async fn nonce + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { - Ok(self.provider.get_transaction_count(who, block).await?.to_alloy().to()) + pub async fn nonce(&self, who: Address, block: Option) -> Result { + Ok(self.provider.get_transaction_count(who, block).await?) } /// # Example @@ -520,15 +472,11 @@ where /// # Ok(()) /// # } /// ``` - pub async fn implementation + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { + pub async fn implementation(&self, who: Address, block: Option) -> Result { let slot = - H256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; - let value = self.provider.get_storage_at(who, slot, block).await?; - let addr: H160 = value.into(); + B256::from_str("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc")?; + let value = self.provider.get_storage_at(who, slot.into(), block).await?; + let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) } @@ -549,15 +497,11 @@ where /// # Ok(()) /// # } /// ``` - pub async fn admin + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { + pub async fn admin(&self, who: Address, block: Option) -> Result { let slot = - H256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?; - let value = self.provider.get_storage_at(who, slot, block).await?; - let addr: H160 = value.into(); + B256::from_str("0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")?; + let value = self.provider.get_storage_at(who, slot.into(), block).await?; + let addr = Address::from_word(value.into()); Ok(format!("{addr:?}")) } @@ -579,8 +523,7 @@ where /// # } /// ``` pub async fn compute_address(&self, address: Address, nonce: Option) -> Result
{ - let unpacked = - if let Some(n) = nonce { n } else { self.nonce(address.to_ethers(), None).await? }; + let unpacked = if let Some(n) = nonce { n } else { self.nonce(address, None).await? }; Ok(address.create(unpacked)) } @@ -601,17 +544,17 @@ where /// # Ok(()) /// # } /// ``` - pub async fn code + Send + Sync>( + pub async fn code( &self, - who: T, + who: Address, block: Option, disassemble: bool, ) -> Result { if disassemble { - let code = self.provider.get_code(who, block).await?.to_vec(); + let code = self.provider.get_code_at(who, block.unwrap_or_default()).await?.to_vec(); Ok(format_operations(disassemble_bytes(code)?)?) } else { - Ok(format!("{}", self.provider.get_code(who, block).await?)) + Ok(format!("{}", self.provider.get_code_at(who, block.unwrap_or_default()).await?)) } } @@ -632,12 +575,8 @@ where /// # Ok(()) /// # } /// ``` - pub async fn codesize + Send + Sync>( - &self, - who: T, - block: Option, - ) -> Result { - let code = self.provider.get_code(who, block).await?.to_vec(); + pub async fn codesize(&self, who: Address, block: Option) -> Result { + let code = self.provider.get_code_at(who, block.unwrap_or_default()).await?.to_vec(); Ok(format!("{}", code.len())) } @@ -663,15 +602,11 @@ where raw: bool, to_json: bool, ) -> Result { - let tx_hash = H256::from_str(&tx_hash).wrap_err("invalid tx hash")?; - let tx = self - .provider - .get_transaction(tx_hash) - .await? - .ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))?; + let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; + let tx = self.provider.get_transaction_by_hash(tx_hash).await?; Ok(if raw { - format!("0x{}", hex::encode(tx.rlp())) + format!("0x{}", hex::encode(TxEnvelope::try_from(tx.inner)?.encoded_2718())) } else if let Some(field) = field { get_pretty_tx_attr(&tx, field.as_str()) .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.to_string()))? @@ -702,11 +637,11 @@ where &self, tx_hash: String, field: Option, - confs: usize, + confs: u64, cast_async: bool, to_json: bool, ) -> Result { - let tx_hash = H256::from_str(&tx_hash).wrap_err("invalid tx hash")?; + let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; let mut receipt: TransactionReceiptWithRevertReason = match self.provider.get_transaction_receipt(tx_hash).await? { @@ -717,13 +652,10 @@ where if cast_async { eyre::bail!("tx not found: {:?}", tx_hash) } else { - let tx = PendingTransaction::new(tx_hash, self.provider.provider()); - tx.confirmations(confs).await?.ok_or_else(|| { - eyre::eyre!( - "tx not found, might have been dropped from mempool: {:?}", - tx_hash - ) - })? + PendingTransactionBuilder::new(self.provider.root(), tx_hash) + .with_required_confirmations(confs) + .get_receipt() + .await? } } } @@ -761,11 +693,14 @@ where /// # Ok(()) /// # } /// ``` - pub async fn rpc(&self, method: &str, params: T) -> Result + pub async fn rpc(&self, method: &str, params: V) -> Result where - T: std::fmt::Debug + serde::Serialize + Send + Sync, + V: alloy_json_rpc::RpcParam, { - let res = self.provider.provider().request::(method, params).await?; + let res = self + .provider + .raw_request::(Cow::Owned(method.to_string()), params) + .await?; Ok(serde_json::to_string(&res)?) } @@ -789,13 +724,16 @@ where /// # Ok(()) /// # } /// ``` - pub async fn storage + Send + Sync>( + pub async fn storage( &self, - from: T, - slot: H256, + from: Address, + slot: B256, block: Option, ) -> Result { - Ok(format!("{:?}", self.provider.get_storage_at(from, slot, block).await?)) + Ok(format!( + "{:?}", + B256::from(self.provider.get_storage_at(from, slot.into(), block).await?) + )) } pub async fn filter_logs(&self, filter: Filter, to_json: bool) -> Result { @@ -851,13 +789,13 @@ where pub async fn convert_block_number( &self, block: Option, - ) -> Result, eyre::Error> { + ) -> Result, eyre::Error> { match block { Some(block) => match block { BlockId::Number(block_number) => Ok(Some(block_number)), BlockId::Hash(hash) => { - let block = self.provider.get_block(hash).await?; - Ok(block.map(|block| block.number.unwrap()).map(BlockNumber::from)) + let block = self.provider.get_block_by_hash(hash.block_hash, false).await?; + Ok(block.map(|block| block.header.number.unwrap()).map(BlockNumberOrTag::from)) } }, None => Ok(None), @@ -890,16 +828,13 @@ where filter: Filter, output: &mut dyn io::Write, to_json: bool, - ) -> Result<()> - where - ::Provider: PubsubClient, - { + ) -> Result<()> { // Initialize the subscription stream for logs - let mut subscription = self.provider.subscribe_logs(&filter).await?; + let mut subscription = self.provider.subscribe_logs(&filter).await?.into_stream(); // Check if a to_block is specified, if so, subscribe to blocks let mut block_subscription = if filter.get_to_block().is_some() { - Some(self.provider.subscribe_blocks().await?) + Some(self.provider.subscribe_blocks().await?.into_stream()) } else { None }; @@ -922,7 +857,7 @@ where Either::Right(futures::future::pending()) } => { if let (Some(block), Some(to_block)) = (block, to_block_number) { - if block.number.map_or(false, |bn| bn > to_block) { + if block.header.number.map_or(false, |bn| bn > to_block) { break; } } @@ -958,6 +893,20 @@ where Ok(()) } + + pub async fn erc20_balance( + &self, + token: Address, + owner: Address, + block: Option, + ) -> Result { + Ok(IERC20::new(token, &self.provider) + .balanceOf(owner) + .block(block.unwrap_or_default()) + .call() + .await? + ._0) + } } pub struct InterfaceSource { @@ -2027,13 +1976,13 @@ impl SimpleCast { /// ``` /// use cast::SimpleCast as Cast; /// - /// let tx = "0x02f8f582a86a82058d8459682f008508351050808303fd84948e42f2f4101563bf679975178e880fd87d3efd4e80b884659ac74b00000000000000000000000080f0c1c49891dcfdd40b6e0f960f84e6042bcb6f000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000007ff4e20000000000000000000000000000000000000000000000000000000000000064c001a05d429597befe2835396206781b199122f2e8297327ed4a05483339e7a8b2022aa04c23a7f70fb29dda1b4ee342fb10a625e9b8ddc6a603fb4e170d4f6f37700cb8"; - /// let (tx, sig) = Cast::decode_raw_transaction(&tx)?; + /// let tx = "0x02f8f582a86a82058d8459682f008508351050808303fd84948e42f2f4101563bf679975178e880fd87d3efd4e80b884659ac74b00000000000000000000000080f0c1c49891dcfdd40b6e0f960f84e6042bcb6f000000000000000000000000b97ef9ef8734c71904d8002f8b6bc66dd9c48a6e00000000000000000000000000000000000000000000000000000000007ff4e20000000000000000000000000000000000000000000000000000000000000064c001a05d429597befe2835396206781b199122f2e8297327ed4a05483339e7a8b2022aa04c23a7f70fb29dda1b4ee342fb10a625e9b8ddc6a603fb4e170d4f6f37700cb8"; + /// let tx_envelope = Cast::decode_raw_transaction(&tx)?; /// # Ok::<(), eyre::Report>(()) - pub fn decode_raw_transaction(tx: &str) -> Result<(TypedTransaction, Signature)> { + pub fn decode_raw_transaction(tx: &str) -> Result { let tx_hex = hex::decode(strip_0x(tx))?; - let tx_rlp = rlp::Rlp::new(tx_hex.as_slice()); - Ok(TypedTransaction::decode_signed(&tx_rlp)?) + let tx = TxEnvelope::decode_2718(&mut tx_hex.as_slice())?; + Ok(tx) } } diff --git a/crates/cast/src/tx.rs b/crates/cast/src/tx.rs deleted file mode 100644 index 1bfd0c40ff67..000000000000 --- a/crates/cast/src/tx.rs +++ /dev/null @@ -1,402 +0,0 @@ -use crate::errors::FunctionSignatureError; -use alloy_json_abi::Function; -use alloy_primitives::{Address, U256}; -use ethers_core::types::{ - transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, NameOrAddress, - TransactionRequest, -}; -use ethers_providers::Middleware; -use eyre::{eyre, Result}; -use foundry_common::{ - abi::{encode_function_args, get_func, get_func_etherscan}, - types::{ToAlloy, ToEthers}, -}; -use foundry_config::Chain; -use futures::future::join_all; - -pub type TxBuilderOutput = (TypedTransaction, Option); -pub type TxBuilderPeekOutput<'a> = (&'a TypedTransaction, &'a Option); - -/// Transaction builder -/// -/// # Examples -/// -/// ``` -/// # async fn foo() -> eyre::Result<()> { -/// # use alloy_primitives::U256; -/// # use cast::TxBuilder; -/// # use foundry_config::NamedChain; -/// let provider = ethers_providers::test_provider::MAINNET.provider(); -/// let mut builder = -/// TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false).await?; -/// builder.gas(Some(U256::from(1))); -/// let (tx, _) = builder.build(); -/// # Ok(()) -/// # } -/// ``` -pub struct TxBuilder<'a, M: Middleware> { - to: Option
, - chain: Chain, - tx: TypedTransaction, - func: Option, - etherscan_api_key: Option, - provider: &'a M, -} - -impl<'a, M: Middleware> TxBuilder<'a, M> { - /// Create a new TxBuilder - /// `provider` - provider to use - /// `from` - 'from' field. Could be an ENS name - /// `to` - `to`. Could be a ENS - /// `chain` - chain to construct the tx for - /// `legacy` - use type 1 transaction - pub async fn new, T: Into>( - provider: &'a M, - from: F, - to: Option, - chain: impl Into, - legacy: bool, - ) -> Result> { - let chain = chain.into(); - let from_addr = resolve_ens(provider, from).await?; - - let mut tx: TypedTransaction = if chain.is_legacy() || legacy { - TransactionRequest::new().from(from_addr.to_ethers()).chain_id(chain.id()).into() - } else { - Eip1559TransactionRequest::new().from(from_addr.to_ethers()).chain_id(chain.id()).into() - }; - - let to_addr = if let Some(to) = to { - let addr = resolve_ens(provider, to).await?; - tx.set_to(addr.to_ethers()); - Some(addr) - } else { - None - }; - Ok(Self { to: to_addr, chain, tx, func: None, etherscan_api_key: None, provider }) - } - - /// Set gas for tx - pub fn set_gas(&mut self, v: U256) -> &mut Self { - self.tx.set_gas(v.to_ethers()); - self - } - - /// Set gas for tx, if `v` is not None - pub fn gas(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_gas(value); - } - self - } - - /// Set gas price - pub fn set_gas_price(&mut self, v: U256) -> &mut Self { - self.tx.set_gas_price(v.to_ethers()); - self - } - - /// Set gas price, if `v` is not None - pub fn gas_price(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_gas_price(value); - } - self - } - - /// Set priority gas price - pub fn set_priority_gas_price(&mut self, v: U256) -> &mut Self { - if let TypedTransaction::Eip1559(tx) = &mut self.tx { - tx.max_priority_fee_per_gas = Some(v.to_ethers()) - } - self - } - - /// Set priority gas price, if `v` is not None - pub fn priority_gas_price(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_priority_gas_price(value); - } - self - } - - /// Set value - pub fn set_value(&mut self, v: U256) -> &mut Self { - self.tx.set_value(v.to_ethers()); - self - } - - /// Set value, if `v` is not None - pub fn value(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_value(value); - } - self - } - - /// Set nonce - pub fn set_nonce(&mut self, v: U256) -> &mut Self { - self.tx.set_nonce(v.to_ethers()); - self - } - - /// Set nonce, if `v` is not None - pub fn nonce(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_nonce(value); - } - self - } - - /// Set etherscan API key. Used to look up function signature buy name - pub fn set_etherscan_api_key(&mut self, v: String) -> &mut Self { - self.etherscan_api_key = Some(v); - self - } - - /// Set etherscan API key, if `v` is not None - pub fn etherscan_api_key(&mut self, v: Option) -> &mut Self { - if let Some(value) = v { - self.set_etherscan_api_key(value); - } - self - } - - pub fn set_data(&mut self, v: Vec) -> &mut Self { - self.tx.set_data(v.into()); - self - } - - pub async fn create_args( - &mut self, - sig: &str, - args: Vec, - ) -> Result<(Vec, Function)> { - if sig.trim().is_empty() { - return Err(FunctionSignatureError::MissingSignature.into()) - } - - let args = resolve_name_args(&args, self.provider).await; - - let func = if sig.contains('(') { - // a regular function signature with parentheses - get_func(sig)? - } else if sig.starts_with("0x") { - // if only calldata is provided, returning a dummy function - get_func("x()")? - } else { - get_func_etherscan( - sig, - self.to.ok_or(FunctionSignatureError::MissingToAddress)?, - &args, - self.chain, - self.etherscan_api_key.as_ref().ok_or_else(|| { - FunctionSignatureError::MissingEtherscan { sig: sig.to_string() } - })?, - ) - .await? - }; - - if sig.starts_with("0x") { - Ok((hex::decode(sig)?, func)) - } else { - Ok((encode_function_args(&func, &args)?, func)) - } - } - - /// Set function arguments - /// `sig` can be: - /// * a fragment (`do(uint32,string)`) - /// * selector + abi-encoded calldata - /// (`0xcdba2fd40000000000000000000000000000000000000000000000000000000000007a69`) - /// * only function name (`do`) - in this case, etherscan lookup is performed on `tx.to`'s - /// contract - pub async fn set_args( - &mut self, - sig: &str, - args: Vec, - ) -> Result<&mut TxBuilder<'a, M>> { - let (data, func) = self.create_args(sig, args).await?; - self.tx.set_data(data.into()); - self.func = Some(func); - Ok(self) - } - - /// Set function arguments, if `value` is not None - pub async fn args( - &mut self, - value: Option<(&str, Vec)>, - ) -> Result<&mut TxBuilder<'a, M>> { - if let Some((sig, args)) = value { - return self.set_args(sig, args).await - } - Ok(self) - } - - /// Consuming build: returns typed transaction and optional function call - pub fn build(self) -> TxBuilderOutput { - (self.tx, self.func) - } - - /// Non-consuming build: peek into the tx content - pub fn peek(&self) -> TxBuilderPeekOutput { - (&self.tx, &self.func) - } -} - -async fn resolve_ens>( - provider: &M, - addr: T, -) -> Result
{ - let from_addr = match addr.into() { - NameOrAddress::Name(ref ens_name) => provider.resolve_name(ens_name).await, - NameOrAddress::Address(addr) => Ok(addr), - } - .map_err(|x| eyre!("Failed to resolve ENS name: {x}"))?; - Ok(from_addr.to_alloy()) -} - -async fn resolve_name_args(args: &[String], provider: &M) -> Vec { - join_all(args.iter().map(|arg| async { - if arg.contains('.') { - let addr = provider.resolve_name(arg).await; - match addr { - Ok(addr) => format!("{addr:?}"), - Err(_) => arg.to_string(), - } - } else { - arg.to_string() - } - })) - .await -} - -#[cfg(test)] -mod tests { - use crate::TxBuilder; - use alloy_primitives::{Address, U256}; - use async_trait::async_trait; - use ethers_core::types::{transaction::eip2718::TypedTransaction, NameOrAddress, H160}; - use ethers_providers::{JsonRpcClient, Middleware, ProviderError}; - use foundry_common::types::ToEthers; - use foundry_config::NamedChain; - use serde::{de::DeserializeOwned, Serialize}; - use std::str::FromStr; - - const ADDR_1: &str = "0000000000000000000000000000000000000001"; - const ADDR_2: &str = "0000000000000000000000000000000000000002"; - - #[derive(Debug)] - struct MyProvider {} - - #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait)] - impl JsonRpcClient for MyProvider { - type Error = ProviderError; - - async fn request( - &self, - _method: &str, - _params: T, - ) -> Result { - Err(ProviderError::CustomError("There is no request".to_string())) - } - } - #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] - #[cfg_attr(not(target_arch = "wasm32"), async_trait)] - impl Middleware for MyProvider { - type Error = ProviderError; - type Provider = MyProvider; - type Inner = MyProvider; - - fn inner(&self) -> &Self::Inner { - self - } - - async fn resolve_name(&self, ens_name: &str) -> Result { - match ens_name { - "a.eth" => Ok(H160::from_str(ADDR_1).unwrap()), - "b.eth" => Ok(H160::from_str(ADDR_2).unwrap()), - _ => unreachable!("don't know how to resolve {ens_name}"), - } - } - } - #[tokio::test(flavor = "multi_thread")] - async fn builder_new_non_legacy() -> eyre::Result<()> { - let provider = MyProvider {}; - let builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false).await?; - let (tx, args) = builder.build(); - assert_eq!(*tx.from().unwrap(), Address::from_str(ADDR_1).unwrap().to_ethers()); - assert_eq!( - *tx.to().unwrap(), - NameOrAddress::Address(Address::from_str(ADDR_2).unwrap().to_ethers()) - ); - assert_eq!(args, None); - - match tx { - TypedTransaction::Eip1559(_) => {} - _ => { - panic!("Wrong tx type"); - } - } - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn builder_new_legacy() -> eyre::Result<()> { - let provider = MyProvider {}; - let builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, true).await?; - // don't check anything other than the tx type - the rest is covered in the non-legacy case - let (tx, _) = builder.build(); - match tx { - TypedTransaction::Legacy(_) => {} - _ => { - panic!("Wrong tx type"); - } - } - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn builder_fields() -> eyre::Result<()> { - let provider = MyProvider {}; - let mut builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false) - .await - .unwrap(); - builder - .gas(Some(U256::from(12u32))) - .gas_price(Some(U256::from(34u32))) - .value(Some(U256::from(56u32))) - .nonce(Some(U256::from(78u32))); - - builder.etherscan_api_key(Some(String::from("what a lovely day"))); // not testing for this :-/ - let (tx, _) = builder.build(); - - assert_eq!(tx.gas().unwrap().as_u32(), 12); - assert_eq!(tx.gas_price().unwrap().as_u32(), 34); - assert_eq!(tx.value().unwrap().as_u32(), 56); - assert_eq!(tx.nonce().unwrap().as_u32(), 78); - assert_eq!(tx.chain_id().unwrap().as_u32(), 1); - Ok(()) - } - - #[tokio::test(flavor = "multi_thread")] - async fn builder_args() -> eyre::Result<()> { - let provider = MyProvider {}; - let mut builder = - TxBuilder::new(&provider, "a.eth", Some("b.eth"), NamedChain::Mainnet, false) - .await - .unwrap(); - builder.args(Some(("what_a_day(int)", vec![String::from("31337")]))).await?; - let (_, function_maybe) = builder.build(); - - assert_ne!(function_maybe, None); - let function = function_maybe.unwrap(); - assert_eq!(function.name, String::from("what_a_day")); - // could test function.inputs() but that should be covered by utils's unit test - Ok(()) - } -} diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 8006867f4c50..17cddc6076c2 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -24,9 +24,10 @@ alloy-json-abi.workspace = true alloy-primitives.workspace = true alloy-genesis.workspace = true alloy-sol-types.workspace = true -alloy-providers.workspace = true +alloy-provider.workspace = true alloy-rpc-types.workspace = true -alloy-signer = { workspace = true, features = ["mnemonic", "keystore"] } +alloy-signer.workspace = true +alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } parking_lot = "0.12" eyre.workspace = true diff --git a/crates/cheatcodes/src/env.rs b/crates/cheatcodes/src/env.rs index 231b47c97eef..d9022d1b3f08 100644 --- a/crates/cheatcodes/src/env.rs +++ b/crates/cheatcodes/src/env.rs @@ -2,7 +2,6 @@ use crate::{string, Cheatcode, Cheatcodes, Error, Result, Vm::*}; use alloy_dyn_abi::DynSolType; -use alloy_primitives::Bytes; use alloy_sol_types::SolValue; use std::{env, sync::OnceLock}; @@ -233,7 +232,7 @@ impl Cheatcode for envOr_12Call { impl Cheatcode for envOr_13Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { name, delim, defaultValue } = self; - let default = defaultValue.iter().map(|vec| vec.clone().into()).collect::>(); + let default = defaultValue.to_vec(); env_array_default(name, delim, &default, &DynSolType::Bytes) } } diff --git a/crates/cheatcodes/src/error.rs b/crates/cheatcodes/src/error.rs index 8b5f8102d8e3..508e7173ee77 100644 --- a/crates/cheatcodes/src/error.rs +++ b/crates/cheatcodes/src/error.rs @@ -1,6 +1,7 @@ use crate::Vm; use alloy_primitives::{Address, Bytes}; -use alloy_signer::{Error as SignerError, WalletError}; +use alloy_signer::Error as SignerError; +use alloy_signer_wallet::WalletError; use alloy_sol_types::SolError; use foundry_common::errors::FsPathError; use foundry_config::UnresolvedEnvVarError; diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index d7328fcc0ea7..b8f55b37c2f7 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -3,7 +3,6 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; use alloy_genesis::{Genesis, GenesisAccount}; use alloy_primitives::{Address, Bytes, B256, U256}; -use alloy_signer::Signer; use alloy_sol_types::SolValue; use foundry_common::fs::{read_json_file, write_json_file}; use foundry_evm_core::{ @@ -14,7 +13,10 @@ use revm::{ primitives::{Account, Bytecode, SpecId, KECCAK_EMPTY}, InnerEvmContext, }; -use std::{collections::HashMap, path::Path}; +use std::{ + collections::{BTreeMap, HashMap}, + path::Path, +}; mod fork; pub(crate) mod mapping; @@ -74,7 +76,7 @@ impl Cheatcode for loadAllocsCall { ensure!(path.exists(), "allocs file does not exist: {pathToAllocsJson}"); // Let's first assume we're reading a file with only the allocs. - let allocs: HashMap = match read_json_file(path) { + let allocs: BTreeMap = match read_json_file(path) { Ok(allocs) => allocs, Err(_) => { // Let's try and read from a genesis file, and extract allocs. @@ -112,7 +114,7 @@ impl Cheatcode for dumpStateCall { .ecx .journaled_state .state() - .into_iter() + .iter_mut() .filter(|(key, val)| !skip(key, val)) .map(|(key, val)| { ( diff --git a/crates/cheatcodes/src/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs index f5402a0d38d2..d2a8ff4ebbeb 100644 --- a/crates/cheatcodes/src/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -1,10 +1,10 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{B256, U256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use alloy_rpc_types::Filter; use alloy_sol_types::SolValue; use eyre::WrapErr; -use foundry_common::{provider::alloy::ProviderBuilder, types::ToEthers}; +use foundry_common::provider::alloy::ProviderBuilder; use foundry_compilers::utils::RuntimeOrHandle; use foundry_evm_core::fork::CreateFork; @@ -64,7 +64,12 @@ impl Cheatcode for createSelectFork_2Call { impl Cheatcode for rollFork_0Call { fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { let Self { blockNumber } = self; - ccx.ecx.db.roll_fork(None, *blockNumber, &mut ccx.ecx.env, &mut ccx.ecx.journaled_state)?; + ccx.ecx.db.roll_fork( + None, + (*blockNumber).to(), + &mut ccx.ecx.env, + &mut ccx.ecx.journaled_state, + )?; Ok(Default::default()) } } @@ -87,7 +92,7 @@ impl Cheatcode for rollFork_2Call { let Self { forkId, blockNumber } = self; ccx.ecx.db.roll_fork( Some(*forkId), - *blockNumber, + (*blockNumber).to(), &mut ccx.ecx.env, &mut ccx.ecx.journaled_state, )?; @@ -225,7 +230,7 @@ impl Cheatcode for rpcCall { let method: &'static str = Box::new(method.clone()).leak(); let params_json: serde_json::Value = serde_json::from_str(params)?; let result = RuntimeOrHandle::new() - .block_on(provider.raw_request(method, params_json)) + .block_on(provider.raw_request(method.into(), params_json)) .map_err(|err| fmt_err!("{method:?}: {err}"))?; let result_as_tokens = crate::json::json_value_to_token(&result) @@ -252,36 +257,35 @@ impl Cheatcode for eth_getLogsCall { let provider = ProviderBuilder::new(&url).build()?; let mut filter = Filter::new().address(*target).from_block(from_block).to_block(to_block); for (i, topic) in topics.iter().enumerate() { - let topic = topic.to_ethers(); // todo: needed because rust wants to convert FixedBytes<32> to U256 to convert it back // to FixedBytes<32> and then to Topic for some reason removing the // From impl in alloy does not fix the situation, and it is not possible to impl // From> either because of a conflicting impl match i { - 0 => filter = filter.event_signature(U256::from_be_bytes(topic.to_fixed_bytes())), - 1 => filter = filter.topic1(U256::from_be_bytes(topic.to_fixed_bytes())), - 2 => filter = filter.topic2(U256::from_be_bytes(topic.to_fixed_bytes())), - 3 => filter = filter.topic3(U256::from_be_bytes(topic.to_fixed_bytes())), + 0 => filter = filter.event_signature(*topic), + 1 => filter = filter.topic1(*topic), + 2 => filter = filter.topic2(*topic), + 3 => filter = filter.topic3(*topic), _ => unreachable!(), }; } // todo: handle the errors somehow let logs = RuntimeOrHandle::new() - .block_on(provider.get_logs(filter)) + .block_on(provider.get_logs(&filter)) .wrap_err("failed to get logs")?; let eth_logs = logs .into_iter() .map(|log| EthGetLogs { - emitter: log.address, - topics: log.topics.into_iter().collect(), - data: log.data.0.into(), + emitter: log.address(), + topics: log.topics().to_vec(), + data: log.inner.data.data, blockHash: log.block_hash.unwrap_or_default(), - blockNumber: log.block_number.unwrap_or_default().to(), + blockNumber: log.block_number.unwrap_or_default(), transactionHash: log.transaction_hash.unwrap_or_default(), - transactionIndex: log.transaction_index.unwrap_or_default().to(), - logIndex: log.log_index.unwrap_or_default(), + transactionIndex: log.transaction_index.unwrap_or_default(), + logIndex: U256::from(log.log_index.unwrap_or_default()), removed: log.removed, }) .collect::>(); diff --git a/crates/cheatcodes/src/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs index 645c14c58c82..6a266a4108fb 100644 --- a/crates/cheatcodes/src/evm/mock.rs +++ b/crates/cheatcodes/src/evm/mock.rs @@ -94,9 +94,9 @@ impl Cheatcode for mockCallRevert_1Call { fn mock_call( state: &mut Cheatcodes, callee: &Address, - cdata: &Vec, + cdata: &Bytes, value: Option<&U256>, - rdata: &Vec, + rdata: &Bytes, ret_type: InstructionResult, ) { state.mocked_calls.entry(*callee).or_default().insert( diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 54335a5e529b..3ba07b5b6cd2 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -495,8 +495,8 @@ fn ffi(state: &Cheatcodes, input: &[String]) -> Result { }; Ok(FfiResult { exitCode: output.status.code().unwrap_or(69), - stdout: encoded_stdout, - stderr: output.stderr, + stdout: encoded_stdout.into(), + stderr: output.stderr.into(), }) } @@ -537,6 +537,7 @@ fn prompt( mod tests { use super::*; use crate::CheatsConfig; + use alloy_primitives::Bytes; use std::sync::Arc; fn cheats() -> Cheatcodes { @@ -554,7 +555,7 @@ mod tests { let cheats = cheats(); let args = ["echo".to_string(), hex::encode(msg)]; let output = ffi(&cheats, &args).unwrap(); - assert_eq!(output.stdout, msg); + assert_eq!(output.stdout, Bytes::from(msg)); } #[test] @@ -563,7 +564,7 @@ mod tests { let cheats = cheats(); let args = ["echo".to_string(), msg.to_string()]; let output = ffi(&cheats, &args).unwrap(); - assert_eq!(output.stdout, msg.as_bytes()); + assert_eq!(output.stdout, Bytes::from(msg.as_bytes())); } #[test] diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 934cae0c75d9..cd2d49ba6307 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -15,7 +15,7 @@ use crate::{ CheatsConfig, CheatsCtxt, Error, Result, Vm, Vm::AccountAccess, }; -use alloy_primitives::{Address, Bytes, Log, B256, U256, U64}; +use alloy_primitives::{Address, Bytes, Log, B256, U256}; use alloy_rpc_types::request::{TransactionInput, TransactionRequest}; use alloy_sol_types::{SolInterface, SolValue}; use foundry_common::{evm::Breakpoints, provider::alloy::RpcUrl}; @@ -447,9 +447,9 @@ impl Inspector for Cheatcodes { oldBalance: old_balance, newBalance: old_balance + value, value, - data: vec![], + data: Bytes::new(), reverted: false, - deployedCode: vec![], + deployedCode: Bytes::new(), storageAccesses: vec![], depth: ecx.journaled_state.depth(), }; @@ -554,9 +554,9 @@ impl Inspector for Cheatcodes { oldBalance: balance, newBalance: balance, value: U256::ZERO, - data: vec![], + data: Bytes::new(), reverted: false, - deployedCode: vec![], + deployedCode: Bytes::new(), storageAccesses: vec![], depth: ecx.journaled_state.depth(), }; @@ -710,7 +710,7 @@ impl Inspector for Cheatcodes { if let Some(storage_recorded_logs) = &mut self.recorded_logs { storage_recorded_logs.push(Vm::Log { topics: log.data.topics().to_vec(), - data: log.data.data.to_vec(), + data: log.data.data.clone(), emitter: log.address, }); } @@ -870,9 +870,9 @@ impl Inspector for Cheatcodes { to: Some(call.contract), value: Some(call.transfer.value), input: TransactionInput::new(call.input.clone()), - nonce: Some(U64::from(account.info.nonce)), + nonce: Some(account.info.nonce), gas: if is_fixed_gas_limit { - Some(U256::from(call.gas_limit)) + Some(call.gas_limit as u128) } else { None }, @@ -938,9 +938,9 @@ impl Inspector for Cheatcodes { oldBalance: old_balance, newBalance: U256::ZERO, // updated on call_end value: call.transfer.value, - data: call.input.to_vec(), + data: call.input.clone(), reverted: false, - deployedCode: vec![], + deployedCode: Bytes::new(), storageAccesses: vec![], // updated on step depth: ecx.journaled_state.depth(), }]); @@ -1047,7 +1047,7 @@ impl Inspector for Cheatcodes { // The gas limit of the call. gasLimit: gas.limit(), // The total gas used. - gasTotalUsed: gas.spend(), + gasTotalUsed: gas.spent(), // The amount of gas used for memory expansion. gasMemoryUsed: gas.memory(), // The amount of gas refunded. @@ -1295,9 +1295,9 @@ impl Inspector for Cheatcodes { to, value: Some(call.value), input: TransactionInput::new(bytecode), - nonce: Some(U64::from(nonce)), + nonce: Some(nonce), gas: if is_fixed_gas_limit { - Some(U256::from(call.gas_limit)) + Some(call.gas_limit as u128) } else { None }, @@ -1365,10 +1365,10 @@ impl Inspector for Cheatcodes { oldBalance: U256::ZERO, // updated on create_end newBalance: U256::ZERO, // updated on create_end value: call.value, - data: call.init_code.to_vec(), + data: call.init_code.clone(), reverted: false, - deployedCode: vec![], // updated on create_end - storageAccesses: vec![], // updated on create_end + deployedCode: Bytes::new(), // updated on create_end + storageAccesses: vec![], // updated on create_end depth, }]); } @@ -1468,13 +1468,8 @@ impl Inspector for Cheatcodes { ecx.journaled_state.load_account(address, &mut ecx.db) { create_access.newBalance = created_acc.info.balance; - create_access.deployedCode = created_acc - .info - .code - .clone() - .unwrap_or_default() - .original_bytes() - .into(); + create_access.deployedCode = + created_acc.info.code.clone().unwrap_or_default().original_bytes(); } } } @@ -1563,10 +1558,10 @@ fn apply_create2_deployer( oldBalance: U256::ZERO, // updated on create_end newBalance: U256::ZERO, // updated on create_end value: call.value, - data: calldata, + data: calldata.into(), reverted: false, - deployedCode: vec![], // updated on create_end - storageAccesses: vec![], // updated on create_end + deployedCode: Bytes::new(), // updated on create_end + storageAccesses: vec![], // updated on create_end depth: ecx.journaled_state.depth(), }]) } @@ -1702,8 +1697,8 @@ fn append_storage_access( oldBalance: U256::ZERO, newBalance: U256::ZERO, value: U256::ZERO, - data: vec![], - deployedCode: vec![], + data: Bytes::new(), + deployedCode: Bytes::new(), depth: entry.depth, }; last.push(resume_record); diff --git a/crates/cheatcodes/src/script.rs b/crates/cheatcodes/src/script.rs index ff4e3a14841a..a28a8be490ca 100644 --- a/crates/cheatcodes/src/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -2,7 +2,7 @@ use crate::{Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{Address, U256}; -use alloy_signer::{LocalWallet, Signer}; +use alloy_signer_wallet::LocalWallet; use foundry_config::Config; use foundry_wallets::{multi_wallet::MultiWallet, WalletSigner}; use parking_lot::Mutex; diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 02c747adb2ff..2fdc99476e4b 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -25,7 +25,7 @@ const DUMMY_CREATE_ADDRESS: Address = address!("00000000000000000000000000000000 /// This then allows us to customize the matching behavior for each call data on the /// `ExpectedCallData` struct and track how many times we've actually seen the call on the second /// element of the tuple. -pub type ExpectedCallTracker = HashMap, (ExpectedCallData, u64)>>; +pub type ExpectedCallTracker = HashMap>; #[derive(Clone, Debug)] pub struct ExpectedCallData { @@ -318,7 +318,7 @@ impl Cheatcode for expectSafeMemoryCallCall { fn expect_call( state: &mut Cheatcodes, target: &Address, - calldata: &Vec, + calldata: &Bytes, value: Option<&U256>, mut gas: Option, mut min_gas: Option, @@ -351,7 +351,7 @@ fn expect_call( "counted expected calls can only bet set once" ); expecteds.insert( - calldata.to_vec(), + calldata.clone(), (ExpectedCallData { value: value.copied(), gas, min_gas, count, call_type }, 0), ); } diff --git a/crates/cheatcodes/src/utils.rs b/crates/cheatcodes/src/utils.rs index f9edc8e40d7c..7544abc0a624 100644 --- a/crates/cheatcodes/src/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -2,15 +2,15 @@ use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{keccak256, Address, B256, U256}; -use alloy_signer::{ +use alloy_signer::{Signer, SignerSync}; +use alloy_signer_wallet::{ coins_bip39::{ ChineseSimplified, ChineseTraditional, Czech, English, French, Italian, Japanese, Korean, Portuguese, Spanish, Wordlist, }, - LocalWallet, MnemonicBuilder, Signer, SignerSync, + LocalWallet, MnemonicBuilder, }; use alloy_sol_types::SolValue; -use foundry_common::types::{ToAlloy, ToEthers}; use foundry_evm_core::{constants::DEFAULT_CREATE2_DEPLOYER, utils::RuntimeOrHandle}; use k256::{ ecdsa::SigningKey, @@ -157,22 +157,22 @@ fn create_wallet(private_key: &U256, label: Option<&str>, state: &mut Cheatcodes .abi_encode()) } -fn encode_vrs(v: u8, r: U256, s: U256) -> Vec { - (U256::from(v), B256::from(r), B256::from(s)).abi_encode() +fn encode_vrs(sig: alloy_primitives::Signature) -> Vec { + let v = sig.v().y_parity_byte_non_eip155().unwrap_or(sig.v().y_parity_byte()); + + (U256::from(v), B256::from(sig.r()), B256::from(sig.s())).abi_encode() } pub(super) fn sign(private_key: &U256, digest: &B256) -> Result { // The `ecrecover` precompile does not use EIP-155. No chain ID is needed. let wallet = parse_wallet(private_key)?; - let sig = wallet.sign_hash_sync(*digest)?; + let sig = wallet.sign_hash_sync(digest)?; let recovered = sig.recover_address_from_prehash(digest)?; assert_eq!(recovered, wallet.address()); - let v = sig.v().y_parity_byte_non_eip155().unwrap_or(sig.v().y_parity_byte()); - - Ok(encode_vrs(v, sig.r(), sig.s())) + Ok(encode_vrs(sig)) } pub(super) fn sign_with_wallet( @@ -206,10 +206,10 @@ pub(super) fn sign_with_wallet( .block_on(wallet.sign_hash(digest)) .map_err(|err| fmt_err!("{err}"))?; - let recovered = sig.recover(digest.to_ethers()).map_err(|err| fmt_err!("{err}"))?; - assert_eq!(recovered.to_alloy(), signer); + let recovered = sig.recover_address_from_prehash(digest).map_err(|err| fmt_err!("{err}"))?; + assert_eq!(recovered, signer); - Ok(encode_vrs(sig.v as u8, sig.r.to_alloy(), sig.s.to_alloy())) + Ok(encode_vrs(sig)) } pub(super) fn sign_p256(private_key: &U256, digest: &B256, _state: &mut Cheatcodes) -> Result { diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index aad365bb52b0..fcc95d55e822 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -22,9 +22,9 @@ foundry-compilers = { workspace = true, features = ["full"] } alloy-dyn-abi.workspace = true alloy-json-abi.workspace = true alloy-primitives.workspace = true - -ethers-core.workspace = true -ethers-providers.workspace = true +alloy-provider.workspace = true +alloy-transport.workspace = true +alloy-chains.workspace = true clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } color-eyre.workspace = true @@ -41,11 +41,13 @@ tracing-error = "0.2" tracing-subscriber = { workspace = true, features = ["registry", "env-filter", "fmt"] } tracing.workspace = true yansi = "0.5" +hex.workspace = true +futures = "0.3" [dev-dependencies] tempfile = "3.7" [features] default = ["rustls"] -rustls = ["ethers-providers/rustls", "foundry-wallets/rustls"] -openssl = ["ethers-providers/openssl", "foundry-compilers/openssl", "foundry-wallets/openssl"] +rustls = ["foundry-wallets/rustls"] +openssl = ["foundry-compilers/openssl"] diff --git a/crates/cli/src/opts/transaction.rs b/crates/cli/src/opts/transaction.rs index 84173eaaf4b3..81d6107bdea0 100644 --- a/crates/cli/src/opts/transaction.rs +++ b/crates/cli/src/opts/transaction.rs @@ -1,5 +1,5 @@ use crate::utils::parse_ether_value; -use alloy_primitives::U256; +use alloy_primitives::{U256, U64}; use clap::Parser; use serde::Serialize; @@ -38,7 +38,7 @@ pub struct TransactionOpts { /// Nonce for the transaction. #[arg(long)] - pub nonce: Option, + pub nonce: Option, /// Send a legacy transaction instead of an EIP1559 transaction. /// diff --git a/crates/cli/src/utils/abi.rs b/crates/cli/src/utils/abi.rs new file mode 100644 index 000000000000..634b889ca711 --- /dev/null +++ b/crates/cli/src/utils/abi.rs @@ -0,0 +1,61 @@ +use alloy_chains::Chain; +use alloy_json_abi::Function; +use alloy_primitives::Address; +use alloy_provider::{network::AnyNetwork, Provider}; +use alloy_transport::Transport; +use eyre::{OptionExt, Result}; +use foundry_common::{ + abi::{encode_function_args, get_func, get_func_etherscan}, + ens::NameOrAddress, +}; +use futures::future::join_all; + +async fn resolve_name_args>( + args: &[String], + provider: &P, +) -> Vec { + join_all(args.iter().map(|arg| async { + if arg.contains('.') { + let addr = NameOrAddress::Name(arg.to_string()).resolve(provider).await; + match addr { + Ok(addr) => addr.to_string(), + Err(_) => arg.to_string(), + } + } else { + arg.to_string() + } + })) + .await +} + +pub async fn parse_function_args>( + sig: &str, + args: Vec, + to: Option
, + chain: Chain, + provider: &P, + etherscan_api_key: Option<&str>, +) -> Result<(Vec, Option)> { + if sig.trim().is_empty() { + eyre::bail!("Function signature or calldata must be provided.") + } + + let args = resolve_name_args(&args, provider).await; + + if sig.starts_with("0x") { + return Ok((hex::decode(sig)?, None)); + } + + let func = if sig.contains('(') { + // a regular function signature with parentheses + get_func(sig)? + } else { + let etherscan_api_key = etherscan_api_key.ok_or_eyre( + "If you wish to fetch function data from EtherScan, please provide an API key.", + )?; + let to = to.ok_or_eyre("A 'to' address must be provided to fetch function data.")?; + get_func_etherscan(sig, to, &args, chain, etherscan_api_key).await? + }; + + Ok((encode_function_args(&func, &args)?, Some(func))) +} diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 99fcceb3e5fd..015ce2145af6 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -1,14 +1,12 @@ use alloy_json_abi::JsonAbi; -use alloy_primitives::{utils::format_units, U256}; -use ethers_core::types::TransactionReceipt; -use ethers_providers::Middleware; +use alloy_primitives::U256; +use alloy_provider::{network::AnyNetwork, Provider}; +use alloy_transport::Transport; use eyre::{ContextCompat, Result}; -use foundry_common::types::ToAlloy; use foundry_config::{Chain, Config}; use std::{ ffi::OsStr, future::Future, - ops::Mul, path::{Path, PathBuf}, process::{Command, Output, Stdio}, time::{Duration, SystemTime, UNIX_EPOCH}, @@ -23,6 +21,9 @@ pub use cmd::*; mod suggestions; pub use suggestions::*; +mod abi; +pub use abi::*; + // reexport all `foundry_config::utils` #[doc(hidden)] pub use foundry_config::utils::*; @@ -76,28 +77,26 @@ pub fn subscriber() { } pub fn abi_to_solidity(abi: &JsonAbi, name: &str) -> Result { - let s = abi.to_sol(name); + let s = abi.to_sol(name, None); let s = forge_fmt::format(&s)?; Ok(s) } -/// Returns a [RetryProvider](foundry_common::RetryProvider) instantiated using [Config]'s RPC URL -/// and chain. -/// -/// Defaults to `http://localhost:8545` and `Mainnet`. -pub fn get_provider(config: &Config) -> Result { +/// Returns a [RetryProvider](foundry_common::alloy::RetryProvider) instantiated using [Config]'s +/// RPC +pub fn get_provider(config: &Config) -> Result { get_provider_builder(config)?.build() } -/// Returns a [ProviderBuilder](foundry_common::ProviderBuilder) instantiated using [Config]'s RPC -/// URL and chain. +/// Returns a [ProviderBuilder](foundry_common::provider::alloy::ProviderBuilder) instantiated using +/// [Config] values. /// /// Defaults to `http://localhost:8545` and `Mainnet`. pub fn get_provider_builder( config: &Config, -) -> Result { +) -> Result { let url = config.get_rpc_url_or_localhost_http()?; - let mut builder = foundry_common::provider::ethers::ProviderBuilder::new(url.as_ref()); + let mut builder = foundry_common::provider::alloy::ProviderBuilder::new(url.as_ref()); if let Ok(chain) = config.chain.unwrap_or_default().try_into() { builder = builder.chain(chain); @@ -111,14 +110,14 @@ pub fn get_provider_builder( Ok(builder) } -pub async fn get_chain(chain: Option, provider: M) -> Result +pub async fn get_chain(chain: Option, provider: P) -> Result where - M: Middleware, - M::Error: 'static, + P: Provider, + T: Transport + Clone, { match chain { Some(chain) => Ok(chain), - None => Ok(Chain::from_id(provider.get_chainid().await?.as_u64())), + None => Ok(Chain::from_id(provider.get_chain_id().await?)), } } @@ -221,40 +220,6 @@ pub fn enable_paint() { } } -/// Prints parts of the receipt to stdout -pub fn print_receipt(chain: Chain, receipt: &TransactionReceipt) { - let gas_used = receipt.gas_used.unwrap_or_default(); - let gas_price = receipt.effective_gas_price.unwrap_or_default(); - foundry_common::shell::println(format!( - "\n##### {chain}\n{status}Hash: {tx_hash:?}{caddr}\nBlock: {bn}\n{gas}\n", - status = if receipt.status.map_or(true, |s| s.is_zero()) { - "❌ [Failed]" - } else { - "✅ [Success]" - }, - tx_hash = receipt.transaction_hash, - caddr = if let Some(addr) = &receipt.contract_address { - format!("\nContract Address: {}", addr.to_alloy().to_checksum(None)) - } else { - String::new() - }, - bn = receipt.block_number.unwrap_or_default(), - gas = if gas_price.is_zero() { - format!("Gas Used: {gas_used}") - } else { - let paid = format_units(gas_used.mul(gas_price).to_alloy(), 18) - .unwrap_or_else(|_| "N/A".into()); - let gas_price = format_units(gas_price.to_alloy(), 9).unwrap_or_else(|_| "N/A".into()); - format!( - "Paid: {} ETH ({gas_used} gas * {} gwei)", - paid.trim_end_matches('0'), - gas_price.trim_end_matches('0').trim_end_matches('.') - ) - }, - )) - .expect("could not print receipt"); -} - /// Useful extensions to [`std::process::Command`]. pub trait CommandUtils { /// Returns the command's output if execution is successful, otherwise, throws an error. diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index d458786f97b1..5e7eebf829c5 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -19,21 +19,30 @@ ethers-core.workspace = true ethers-middleware.workspace = true ethers-providers = { workspace = true, features = ["ws", "ipc"] } ethers-signers.workspace = true +# should be removed along with ethers +reqwest_ethers = { package = "reqwest", version = "0.11", default-features = false } alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } alloy-rpc-types.workspace = true alloy-rpc-client.workspace = true -alloy-providers.workspace = true +alloy-provider.workspace = true alloy-transport.workspace = true -alloy-signer.workspace = true -alloy-transport-http.workspace = true +alloy-signer-wallet.workspace = true +alloy-transport-http = { workspace = true, features = ["reqwest", "reqwest-rustls-tls"] } alloy-transport-ws.workspace = true alloy-transport-ipc.workspace = true alloy-json-rpc.workspace = true alloy-pubsub.workspace = true alloy-sol-types.workspace = true +alloy-contract.workspace = true +alloy-consensus.workspace = true tower.workspace = true @@ -47,7 +56,7 @@ globset = "0.4" hex.workspace = true once_cell = "1" rand.workspace = true -reqwest = { version = "0.11", default-features = false } +reqwest = { version = "0.12", default-features = false } semver = "1" serde_json.workspace = true serde.workspace = true diff --git a/crates/common/src/abi.rs b/crates/common/src/abi.rs index b08620d888d1..6b7615b39c9f 100644 --- a/crates/common/src/abi.rs +++ b/crates/common/src/abi.rs @@ -159,7 +159,7 @@ pub fn find_source( } /// Helper function to coerce a value to a [DynSolValue] given a type string -fn coerce_value(ty: &str, arg: &str) -> Result { +pub fn coerce_value(ty: &str, arg: &str) -> Result { let ty = DynSolType::parse(ty)?; Ok(DynSolType::coerce_str(&ty, arg)?) } diff --git a/crates/common/src/constants.rs b/crates/common/src/constants.rs index 23ca0abab43f..0ba0514c2b87 100644 --- a/crates/common/src/constants.rs +++ b/crates/common/src/constants.rs @@ -38,7 +38,7 @@ pub const ARBITRUM_SENDER: Address = address!("000000000000000000000000000000000 pub const OPTIMISM_SYSTEM_ADDRESS: Address = address!("deaddeaddeaddeaddeaddeaddeaddeaddead0001"); /// Transaction identifier of System transaction types -pub const SYSTEM_TRANSACTION_TYPE: u64 = 126u64; +pub const SYSTEM_TRANSACTION_TYPE: u8 = 126; /// Returns whether the sender is a known L2 system sender that is the first tx in every block. /// diff --git a/crates/common/src/ens.rs b/crates/common/src/ens.rs new file mode 100644 index 000000000000..cb1058384131 --- /dev/null +++ b/crates/common/src/ens.rs @@ -0,0 +1,193 @@ +//! ENS Name resolving utilities. +#![allow(missing_docs)] +use alloy_primitives::{address, keccak256, Address, B256}; +use alloy_provider::{Network, Provider}; +use alloy_sol_types::sol; +use alloy_transport::Transport; +use async_trait::async_trait; +use std::str::FromStr; + +use self::EnsResolver::EnsResolverInstance; + +// ENS Registry and Resolver contracts. +sol! { + #[sol(rpc)] + // ENS Registry contract. + contract EnsRegistry { + /// Returns the resolver for the specified node. + function resolver(bytes32 node) view returns (address); + } + + #[sol(rpc)] + // ENS Resolver interface. + contract EnsResolver { + // Returns the address associated with the specified node. + function addr(bytes32 node) view returns (address); + + // Returns the name associated with an ENS node, for reverse records. + function name(bytes32 node) view returns (string); + } +} + +/// ENS registry address (`0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`) +pub const ENS_ADDRESS: Address = address!("00000000000C2E074eC69A0dFb2997BA6C7d2e1e"); + +pub const ENS_REVERSE_REGISTRAR_DOMAIN: &str = "addr.reverse"; + +/// Error type for ENS resolution. +#[derive(Debug, thiserror::Error)] +pub enum EnsResolutionError { + /// Failed to resolve ENS registry. + #[error("Failed to get resolver from ENS registry: {0}")] + EnsRegistryResolutionFailed(String), + /// Failed to resolve ENS name to an address. + #[error("Failed to resolve ENS name to an address: {0}")] + EnsResolutionFailed(String), +} + +/// ENS name or Ethereum Address. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum NameOrAddress { + /// An ENS Name (format does not get checked) + Name(String), + /// An Ethereum Address + Address(Address), +} + +impl NameOrAddress { + /// Resolves the name to an Ethereum Address. + pub async fn resolve>( + &self, + provider: &P, + ) -> Result { + match self { + NameOrAddress::Name(name) => provider.resolve_name(name).await, + NameOrAddress::Address(addr) => Ok(*addr), + } + } +} + +impl From for NameOrAddress { + fn from(name: String) -> Self { + NameOrAddress::Name(name) + } +} + +impl From<&String> for NameOrAddress { + fn from(name: &String) -> Self { + NameOrAddress::Name(name.clone()) + } +} + +impl From
for NameOrAddress { + fn from(addr: Address) -> Self { + NameOrAddress::Address(addr) + } +} + +impl FromStr for NameOrAddress { + type Err =
::Err; + + fn from_str(s: &str) -> Result { + if let Ok(addr) = Address::from_str(s) { + Ok(NameOrAddress::Address(addr)) + } else { + Ok(NameOrAddress::Name(s.to_string())) + } + } +} + +#[async_trait] +pub trait ProviderEnsExt> { + async fn get_resolver(&self) -> Result, EnsResolutionError>; + + async fn resolve_name(&self, name: &str) -> Result { + let node = namehash(name); + let addr = self + .get_resolver() + .await? + .addr(node) + .call() + .await + .map_err(|err| EnsResolutionError::EnsResolutionFailed(err.to_string()))? + ._0; + + Ok(addr) + } + + async fn lookup_address(&self, address: Address) -> Result { + let node = namehash(&reverse_address(address)); + let name = self + .get_resolver() + .await? + .name(node) + .call() + .await + .map_err(|err| EnsResolutionError::EnsResolutionFailed(err.to_string()))? + ._0; + + Ok(name) + } +} + +#[async_trait] +impl ProviderEnsExt for P +where + P: Provider, + N: Network, + T: Transport + Clone, +{ + async fn get_resolver(&self) -> Result, EnsResolutionError> { + let registry = EnsRegistry::new(ENS_ADDRESS, self); + let address = registry + .resolver(namehash("eth")) + .call() + .await + .map_err(|err| EnsResolutionError::EnsRegistryResolutionFailed(err.to_string()))? + ._0; + + Ok(EnsResolverInstance::new(address, self)) + } +} + +/// Returns the ENS namehash as specified in [EIP-137](https://eips.ethereum.org/EIPS/eip-137) +pub fn namehash(name: &str) -> B256 { + if name.is_empty() { + return B256::ZERO + } + + // Remove the variation selector U+FE0F + let name = name.replace('\u{fe0f}', ""); + + // Generate the node starting from the right + name.rsplit('.') + .fold([0u8; 32], |node, label| *keccak256([node, *keccak256(label.as_bytes())].concat())) + .into() +} + +/// Returns the reverse-registrar name of an address. +pub fn reverse_address(addr: Address) -> String { + format!("{addr:?}.{ENS_REVERSE_REGISTRAR_DOMAIN}")[2..].to_string() +} + +#[cfg(test)] +mod test { + use super::*; + + fn assert_hex(hash: B256, val: &str) { + assert_eq!(hash.0.to_vec(), hex::decode(val).unwrap()); + } + + #[test] + fn test_namehash() { + for (name, expected) in &[ + ("", "0000000000000000000000000000000000000000000000000000000000000000"), + ("foo.eth", "de9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f"), + ("eth", "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"), + ("alice.eth", "0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec"), + ("ret↩️rn.eth", "0x3de5f4c02db61b221e7de7f1c40e29b6e2f07eb48d65bf7e304715cd9ed33b24"), + ] { + assert_hex(namehash(name), expected); + } + } +} diff --git a/crates/common/src/fmt/ui.rs b/crates/common/src/fmt/ui.rs index 5549f36abc5f..c5bdf6c130e0 100644 --- a/crates/common/src/fmt/ui.rs +++ b/crates/common/src/fmt/ui.rs @@ -1,8 +1,12 @@ //! Helper trait and functions to format Ethereum types. use crate::TransactionReceiptWithRevertReason; +use alloy_consensus::{AnyReceiptEnvelope, Receipt, ReceiptWithBloom, TxType}; use alloy_primitives::*; -use ethers_core::types::{Block, Log, OtherFields, Transaction, TransactionReceipt, TxHash}; +use alloy_rpc_types::{ + other::OtherFields, AnyTransactionReceipt, Block, BlockTransactions, Log, Transaction, + TransactionReceipt, +}; use serde::Deserialize; /// length of the name column for pretty formatting `{:>20}{value}` @@ -23,6 +27,12 @@ pub trait UIfmt { fn pretty(&self) -> String; } +impl UIfmt for &T { + fn pretty(&self) -> String { + (*self).pretty() + } +} + impl UIfmt for Option { fn pretty(&self) -> String { if let Some(ref inner) = self { @@ -33,7 +43,7 @@ impl UIfmt for Option { } } -impl UIfmt for Vec { +impl UIfmt for [T] { fn pretty(&self) -> String { if !self.is_empty() { let mut s = String::with_capacity(self.len() * 64); @@ -59,13 +69,25 @@ impl UIfmt for String { } } +impl UIfmt for u64 { + fn pretty(&self) -> String { + self.to_string() + } +} + +impl UIfmt for u128 { + fn pretty(&self) -> String { + self.to_string() + } +} + impl UIfmt for bool { fn pretty(&self) -> String { self.to_string() } } -impl UIfmt for U256 { +impl UIfmt for Uint { fn pretty(&self) -> String { self.to_string() } @@ -89,6 +111,12 @@ impl UIfmt for Bloom { } } +impl UIfmt for TxType { + fn pretty(&self) -> String { + (*self as u8).to_string() + } +} + impl UIfmt for Vec { fn pretty(&self) -> String { self[..].pretty() @@ -119,32 +147,34 @@ impl UIfmt for [u8] { } } -impl UIfmt for U64 { - fn pretty(&self) -> String { - self.to_string() - } -} - -impl UIfmt for TransactionReceipt { +impl UIfmt for AnyTransactionReceipt { fn pretty(&self) -> String { let Self { - transaction_hash, - transaction_index, - block_hash, - block_number, - from, - to, - cumulative_gas_used, - gas_used, - contract_address, - logs, - status, - root, - logs_bloom, - transaction_type, - effective_gas_price, + inner: + TransactionReceipt { + transaction_hash, + transaction_index, + block_hash, + block_number, + from, + to, + gas_used, + contract_address, + state_root, + effective_gas_price, + inner: + AnyReceiptEnvelope { + r#type: transaction_type, + inner: + ReceiptWithBloom { + receipt: Receipt { status, cumulative_gas_used, logs }, + logs_bloom, + }, + }, + blob_gas_price, + blob_gas_used, + }, other, - .. } = self; let mut pretty = format!( @@ -162,7 +192,9 @@ root {} status {} transactionHash {} transactionIndex {} -type {}", +type {} +blobGasPrice {} +blobGasUsed {}", block_hash.pretty(), block_number.pretty(), contract_address.pretty(), @@ -170,13 +202,15 @@ type {}", effective_gas_price.pretty(), from.pretty(), gas_used.pretty(), - serde_json::to_string(logs).unwrap(), + serde_json::to_string(&logs).unwrap(), logs_bloom.pretty(), - root.pretty(), + state_root.pretty(), status.pretty(), transaction_hash.pretty(), transaction_index.pretty(), - transaction_type.pretty() + transaction_type, + blob_gas_price.pretty(), + blob_gas_used.pretty(), ); if let Some(to) = to { @@ -205,40 +239,38 @@ removed: {} topics: {} transactionHash: {} transactionIndex: {}", - self.address.pretty(), + self.address().pretty(), self.block_hash.pretty(), self.block_number.pretty(), - self.data.pretty(), + self.data().data.pretty(), self.log_index.pretty(), self.removed.pretty(), - self.topics.pretty(), + self.topics().pretty(), self.transaction_hash.pretty(), self.transaction_index.pretty(), ) } } -impl UIfmt for Block { +impl UIfmt for Block { fn pretty(&self) -> String { format!( " {} -transactions {}", +transactions: {}", pretty_block_basics(self), self.transactions.pretty() ) } } -impl UIfmt for Block { +impl UIfmt for BlockTransactions { fn pretty(&self) -> String { - format!( - " -{} -transactions: {}", - pretty_block_basics(self), - self.transactions.pretty() - ) + match self { + BlockTransactions::Hashes(hashes) => hashes.pretty(), + BlockTransactions::Full(transactions) => transactions.pretty(), + BlockTransactions::Uncle => String::new(), + } } } @@ -285,12 +317,12 @@ value {}{}", self.gas_price.pretty(), self.hash.pretty(), self.input.pretty(), - self.nonce.pretty(), - to_bytes(self.r).pretty(), - to_bytes(self.s).pretty(), + self.nonce, + self.signature.map(|s| s.r.to_be_bytes_vec()).pretty(), + self.signature.map(|s| s.s.to_be_bytes_vec()).pretty(), self.to.pretty(), self.transaction_index.pretty(), - self.v.pretty(), + self.signature.map(|s| s.v).pretty(), self.value.pretty(), self.other.pretty() ) @@ -367,13 +399,6 @@ mod temp_ethers { with_alloy!(Address, Bloom, H64, H256, I256, U256, U64); } -/// Convert a U256 to bytes -pub fn to_bytes(uint: ethers_core::types::U256) -> [u8; 32] { - let mut buffer: [u8; 32] = [0; 32]; - uint.to_big_endian(&mut buffer); - buffer -} - /// Returns the `UiFmt::pretty()` formatted attribute of the transactions pub fn get_pretty_tx_attr(transaction: &Transaction, attr: &str) -> Option { match attr { @@ -384,12 +409,12 @@ pub fn get_pretty_tx_attr(transaction: &Transaction, attr: &str) -> Option Some(transaction.gas_price.pretty()), "hash" => Some(transaction.hash.pretty()), "input" => Some(transaction.input.pretty()), - "nonce" => Some(transaction.nonce.pretty()), - "s" => Some(to_bytes(transaction.s).pretty()), - "r" => Some(to_bytes(transaction.r).pretty()), + "nonce" => Some(transaction.nonce.to_string()), + "s" => transaction.signature.map(|s| B256::from(s.s).pretty()), + "r" => transaction.signature.map(|s| B256::from(s.r).pretty()), "to" => Some(transaction.to.pretty()), "transactionIndex" | "transaction_index" => Some(transaction.transaction_index.pretty()), - "v" => Some(transaction.v.pretty()), + "v" => transaction.signature.map(|s| s.v.pretty()), "value" => Some(transaction.value.pretty()), other => { if let Some(value) = transaction.other.get(other) { @@ -410,49 +435,50 @@ pub fn get_pretty_tx_receipt_attr( "blockNumber" | "block_number" => Some(receipt.receipt.block_number.pretty()), "contractAddress" | "contract_address" => Some(receipt.receipt.contract_address.pretty()), "cumulativeGasUsed" | "cumulative_gas_used" => { - Some(receipt.receipt.cumulative_gas_used.pretty()) + Some(receipt.receipt.inner.inner.inner.receipt.cumulative_gas_used.pretty()) } "effectiveGasPrice" | "effective_gas_price" => { - Some(receipt.receipt.effective_gas_price.pretty()) + Some(receipt.receipt.effective_gas_price.to_string()) + } + "gasUsed" | "gas_used" => Some(receipt.receipt.gas_used.to_string()), + "logs" => Some(receipt.receipt.inner.inner.inner.receipt.logs.as_slice().pretty()), + "logsBloom" | "logs_bloom" => Some(receipt.receipt.inner.inner.inner.logs_bloom.pretty()), + "root" | "stateRoot" | "state_root " => Some(receipt.receipt.state_root.pretty()), + "status" | "statusCode" | "status_code" => { + Some(receipt.receipt.inner.inner.inner.receipt.status.pretty()) } - "gasUsed" | "gas_used" => Some(receipt.receipt.gas_used.pretty()), - "logs" => Some(receipt.receipt.logs.pretty()), - "logsBloom" | "logs_bloom" => Some(receipt.receipt.logs_bloom.pretty()), - "root" => Some(receipt.receipt.root.pretty()), - "status" => Some(receipt.receipt.status.pretty()), "transactionHash" | "transaction_hash" => Some(receipt.receipt.transaction_hash.pretty()), "transactionIndex" | "transaction_index" => { - Some(receipt.receipt.transaction_index.pretty()) + Some(receipt.receipt.transaction_index.to_string()) } - "type" | "transaction_type" => Some(receipt.receipt.transaction_type.pretty()), + "type" | "transaction_type" => Some(receipt.receipt.inner.inner.r#type.to_string()), "revertReason" | "revert_reason" => Some(receipt.revert_reason.pretty()), _ => None, } } /// Returns the `UiFmt::pretty()` formatted attribute of the given block -pub fn get_pretty_block_attr(block: &Block, attr: &str) -> Option { +pub fn get_pretty_block_attr(block: &Block, attr: &str) -> Option { match attr { - "baseFeePerGas" | "base_fee_per_gas" => Some(block.base_fee_per_gas.pretty()), - "difficulty" => Some(block.difficulty.pretty()), - "extraData" | "extra_data" => Some(block.extra_data.pretty()), - "gasLimit" | "gas_limit" => Some(block.gas_limit.pretty()), - "gasUsed" | "gas_used" => Some(block.gas_used.pretty()), - "hash" => Some(block.hash.pretty()), - "logsBloom" | "logs_bloom" => Some(block.logs_bloom.pretty()), - "miner" | "author" => Some(block.author.pretty()), - "mixHash" | "mix_hash" => Some(block.mix_hash.pretty()), - "nonce" => Some(block.nonce.pretty()), - "number" => Some(block.number.pretty()), - "parentHash" | "parent_hash" => Some(block.parent_hash.pretty()), - "transactionsRoot" | "transactions_root" => Some(block.transactions_root.pretty()), - "receiptsRoot" | "receipts_root" => Some(block.receipts_root.pretty()), - "sealFields" | "seal_fields" => Some(block.seal_fields.pretty()), - "sha3Uncles" | "sha_3_uncles" => Some(block.uncles_hash.pretty()), + "baseFeePerGas" | "base_fee_per_gas" => Some(block.header.base_fee_per_gas.pretty()), + "difficulty" => Some(block.header.difficulty.pretty()), + "extraData" | "extra_data" => Some(block.header.extra_data.pretty()), + "gasLimit" | "gas_limit" => Some(block.header.gas_limit.pretty()), + "gasUsed" | "gas_used" => Some(block.header.gas_used.pretty()), + "hash" => Some(block.header.hash.pretty()), + "logsBloom" | "logs_bloom" => Some(block.header.logs_bloom.pretty()), + "miner" | "author" => Some(block.header.miner.pretty()), + "mixHash" | "mix_hash" => Some(block.header.mix_hash.pretty()), + "nonce" => Some(block.header.nonce.pretty()), + "number" => Some(block.header.number.pretty()), + "parentHash" | "parent_hash" => Some(block.header.parent_hash.pretty()), + "transactionsRoot" | "transactions_root" => Some(block.header.transactions_root.pretty()), + "receiptsRoot" | "receipts_root" => Some(block.header.receipts_root.pretty()), + "sha3Uncles" | "sha_3_uncles" => Some(block.header.uncles_hash.pretty()), "size" => Some(block.size.pretty()), - "stateRoot" | "state_root" => Some(block.state_root.pretty()), - "timestamp" => Some(block.timestamp.pretty()), - "totalDifficulty" | "total_difficult" => Some(block.total_difficulty.pretty()), + "stateRoot" | "state_root" => Some(block.header.state_root.pretty()), + "timestamp" => Some(block.header.timestamp.pretty()), + "totalDifficulty" | "total_difficult" => Some(block.header.total_difficulty.pretty()), other => { if let Some(value) = block.other.get(other) { let val = EthValue::from(value.clone()); @@ -463,7 +489,7 @@ pub fn get_pretty_block_attr(block: &Block, attr: &str) -> Option(block: &Block) -> String { +fn pretty_block_basics(block: &Block) -> String { format!( " baseFeePerGas {} @@ -480,34 +506,32 @@ number {} parentHash {} transactionsRoot {} receiptsRoot {} -sealFields {} sha3Uncles {} size {} stateRoot {} timestamp {} withdrawalsRoot {} totalDifficulty {}{}", - block.base_fee_per_gas.pretty(), - block.difficulty.pretty(), - block.extra_data.pretty(), - block.gas_limit.pretty(), - block.gas_used.pretty(), - block.hash.pretty(), - block.logs_bloom.pretty(), - block.author.pretty(), - block.mix_hash.pretty(), - block.nonce.pretty(), - block.number.pretty(), - block.parent_hash.pretty(), - block.transactions_root.pretty(), - block.receipts_root.pretty(), - block.seal_fields.pretty(), - block.uncles_hash.pretty(), + block.header.base_fee_per_gas.pretty(), + block.header.difficulty.pretty(), + block.header.extra_data.pretty(), + block.header.gas_limit.pretty(), + block.header.gas_used.pretty(), + block.header.hash.pretty(), + block.header.logs_bloom.pretty(), + block.header.miner.pretty(), + block.header.mix_hash.pretty(), + block.header.nonce.pretty(), + block.header.number.pretty(), + block.header.parent_hash.pretty(), + block.header.transactions_root.pretty(), + block.header.receipts_root.pretty(), + block.header.uncles_hash.pretty(), block.size.pretty(), - block.state_root.pretty(), - block.timestamp.pretty(), - block.withdrawals_root.pretty(), - block.total_difficulty.pretty(), + block.header.state_root.pretty(), + block.header.timestamp.pretty(), + block.header.withdrawals_root.pretty(), + block.header.total_difficulty.pretty(), block.other.pretty() ) } @@ -594,7 +618,7 @@ txType 0 #[test] fn print_block_w_txs() { let block = r#"{"number":"0x3","hash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","parentHash":"0x689c70c080ca22bc0e681694fa803c1aba16a69c8b6368fed5311d279eb9de90","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x7270c1c4440180f2bd5215809ee3d545df042b67329499e1ab97eb759d31610d","stateRoot":"0x29f32984517a7d25607da485b23cefabfd443751422ca7e603395e1de9bc8a4b","receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","size":"0x3e8","gasLimit":"0x6691b7","gasUsed":"0x5208","timestamp":"0x5ecedbb9","transactions":[{"hash":"0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067","nonce":"0x2","blockHash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","blockNumber":"0x3","transactionIndex":"0x0","from":"0xfdcedc3bfca10ecb0890337fbdd1977aba84807a","to":"0xdca8ce283150ab773bcbeb8d38289bdb5661de1e","value":"0x0","gas":"0x15f90","gasPrice":"0x4a817c800","input":"0x","v":"0x25","r":"0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88","s":"0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e"}],"uncles":[]}"#; - let block: Block = serde_json::from_str(block).unwrap(); + let block: Block = serde_json::from_str(block).unwrap(); let output ="\nblockHash 0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972 blockNumber 3 from 0xFdCeDC3bFca10eCb0890337fbdD1977aba84807a @@ -609,7 +633,11 @@ to 0xdca8ce283150AB773BCbeB8d38289bdB5661dE1e transactionIndex 0 v 37 value 0".to_string(); - let generated = block.transactions[0].pretty(); + let txs = match block.transactions { + BlockTransactions::Full(txs) => txs, + _ => panic!("not full transactions"), + }; + let generated = txs[0].pretty(); assert_eq!(generated.as_str(), output.as_str()); } @@ -657,45 +685,40 @@ value 0".to_string(); #[test] fn test_pretty_tx_attr() { let block = r#"{"number":"0x3","hash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","parentHash":"0x689c70c080ca22bc0e681694fa803c1aba16a69c8b6368fed5311d279eb9de90","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x7270c1c4440180f2bd5215809ee3d545df042b67329499e1ab97eb759d31610d","stateRoot":"0x29f32984517a7d25607da485b23cefabfd443751422ca7e603395e1de9bc8a4b","receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2","miner":"0x0000000000000000000000000000000000000000","difficulty":"0x0","totalDifficulty":"0x0","extraData":"0x","size":"0x3e8","gasLimit":"0x6691b7","gasUsed":"0x5208","timestamp":"0x5ecedbb9","transactions":[{"hash":"0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067","nonce":"0x2","blockHash":"0xda53da08ef6a3cbde84c33e51c04f68c3853b6a3731f10baa2324968eee63972","blockNumber":"0x3","transactionIndex":"0x0","from":"0xfdcedc3bfca10ecb0890337fbdd1977aba84807a","to":"0xdca8ce283150ab773bcbeb8d38289bdb5661de1e","value":"0x0","gas":"0x15f90","gasPrice":"0x4a817c800","input":"0x","v":"0x25","r":"0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88","s":"0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e"}],"uncles":[]}"#; - let block: Block = serde_json::from_str(block).unwrap(); - assert_eq!(None, get_pretty_tx_attr(&block.transactions[0], "")); - assert_eq!( - Some("3".to_string()), - get_pretty_tx_attr(&block.transactions[0], "blockNumber") - ); + let block: Block = serde_json::from_str(block).unwrap(); + let txs = match block.transactions { + BlockTransactions::Full(txes) => txes, + _ => panic!("not full transactions"), + }; + assert_eq!(None, get_pretty_tx_attr(&txs[0], "")); + assert_eq!(Some("3".to_string()), get_pretty_tx_attr(&txs[0], "blockNumber")); assert_eq!( Some("0xFdCeDC3bFca10eCb0890337fbdD1977aba84807a".to_string()), - get_pretty_tx_attr(&block.transactions[0], "from") - ); - assert_eq!(Some("90000".to_string()), get_pretty_tx_attr(&block.transactions[0], "gas")); - assert_eq!( - Some("20000000000".to_string()), - get_pretty_tx_attr(&block.transactions[0], "gasPrice") + get_pretty_tx_attr(&txs[0], "from") ); + assert_eq!(Some("90000".to_string()), get_pretty_tx_attr(&txs[0], "gas")); + assert_eq!(Some("20000000000".to_string()), get_pretty_tx_attr(&txs[0], "gasPrice")); assert_eq!( Some("0xc3c5f700243de37ae986082fd2af88d2a7c2752a0c0f7b9d6ac47c729d45e067".to_string()), - get_pretty_tx_attr(&block.transactions[0], "hash") + get_pretty_tx_attr(&txs[0], "hash") ); - assert_eq!(Some("0x".to_string()), get_pretty_tx_attr(&block.transactions[0], "input")); - assert_eq!(Some("2".to_string()), get_pretty_tx_attr(&block.transactions[0], "nonce")); + assert_eq!(Some("0x".to_string()), get_pretty_tx_attr(&txs[0], "input")); + assert_eq!(Some("2".to_string()), get_pretty_tx_attr(&txs[0], "nonce")); assert_eq!( Some("0x19f2694eb9113656dbea0b925e2e7ceb43df83e601c4116aee9c0dd99130be88".to_string()), - get_pretty_tx_attr(&block.transactions[0], "r") + get_pretty_tx_attr(&txs[0], "r") ); assert_eq!( Some("0x73e5764b324a4f7679d890a198ba658ba1c8cd36983ff9797e10b1b89dbb448e".to_string()), - get_pretty_tx_attr(&block.transactions[0], "s") + get_pretty_tx_attr(&txs[0], "s") ); assert_eq!( Some("0xdca8ce283150AB773BCbeB8d38289bdB5661dE1e".into()), - get_pretty_tx_attr(&block.transactions[0], "to") - ); - assert_eq!( - Some("0".to_string()), - get_pretty_tx_attr(&block.transactions[0], "transactionIndex") + get_pretty_tx_attr(&txs[0], "to") ); - assert_eq!(Some("37".to_string()), get_pretty_tx_attr(&block.transactions[0], "v")); - assert_eq!(Some("0".to_string()), get_pretty_tx_attr(&block.transactions[0], "value")); + assert_eq!(Some("0".to_string()), get_pretty_tx_attr(&txs[0], "transactionIndex")); + assert_eq!(Some("37".to_string()), get_pretty_tx_attr(&txs[0], "v")); + assert_eq!(Some("0".to_string()), get_pretty_tx_attr(&txs[0], "value")); } #[test] @@ -731,7 +754,7 @@ value 0".to_string(); } ); - let block: Block<()> = serde_json::from_value(json).unwrap(); + let block: Block = serde_json::from_value(json).unwrap(); assert_eq!(None, get_pretty_block_attr(&block, "")); assert_eq!(Some("7".to_string()), get_pretty_block_attr(&block, "baseFeePerGas")); diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index c2825ac2f997..36298ae8fe76 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -12,6 +12,7 @@ pub mod calc; pub mod compile; pub mod constants; pub mod contracts; +pub mod ens; pub mod errors; pub mod evm; pub mod fmt; diff --git a/crates/common/src/provider/alloy.rs b/crates/common/src/provider/alloy.rs index 54c9085a15ee..e76a898bd814 100644 --- a/crates/common/src/provider/alloy.rs +++ b/crates/common/src/provider/alloy.rs @@ -3,9 +3,12 @@ use crate::{ provider::runtime_transport::RuntimeTransportBuilder, ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT, }; -use alloy_primitives::U256; -use alloy_providers::tmp::{Provider, TempProvider}; +use alloy_provider::{ + network::AnyNetwork, utils::Eip1559Estimation, Provider, + ProviderBuilder as AlloyProviderBuilder, RootProvider, +}; use alloy_rpc_client::ClientBuilder; +use alloy_transport::Transport; use ethers_middleware::gas_oracle::{GasCategory, GasOracle, Polygon}; use eyre::{Result, WrapErr}; use foundry_common::types::ToAlloy; @@ -25,7 +28,7 @@ use super::{ }; /// Helper type alias for a retry provider -pub type RetryProvider = Provider>; +pub type RetryProvider = RootProvider, N>; /// Helper type alias for a rpc url pub type RpcUrl = String; @@ -239,8 +242,10 @@ impl ProviderBuilder { .build(); let client = ClientBuilder::default().layer(retry_layer).transport(transport, false); - // todo: provider polling interval - Ok(Provider::new_with_client(client)) + let provider = AlloyProviderBuilder::<_, _, AnyNetwork>::default() + .on_provider(RootProvider::new(client)); + + Ok(provider) } } @@ -250,14 +255,14 @@ impl ProviderBuilder { /// - polygon /// /// Fallback is the default [`Provider::estimate_eip1559_fees`] implementation -pub async fn estimate_eip1559_fees( +pub async fn estimate_eip1559_fees, T: Transport + Clone>( provider: &P, chain: Option, -) -> Result<(U256, U256)> { +) -> Result { let chain = if let Some(chain) = chain { chain } else { - provider.get_chain_id().await.wrap_err("Failed to get chain id")?.to() + provider.get_chain_id().await.wrap_err("Failed to get chain id")? }; if let Ok(chain) = NamedChain::try_from(chain) { @@ -272,7 +277,12 @@ pub async fn estimate_eip1559_fees( }; let estimator = Polygon::new(chain)?.category(GasCategory::Standard); let (a, b) = estimator.estimate_eip1559_fees().await?; - return Ok((a.to_alloy(), b.to_alloy())); + + let estimation = Eip1559Estimation { + max_fee_per_gas: a.to_alloy().to(), + max_priority_fee_per_gas: b.to_alloy().to(), + }; + return Ok(estimation) } _ => {} } diff --git a/crates/common/src/provider/ethers.rs b/crates/common/src/provider/ethers.rs index 0d4acd2ce377..7d99763de321 100644 --- a/crates/common/src/provider/ethers.rs +++ b/crates/common/src/provider/ethers.rs @@ -4,8 +4,6 @@ use crate::{ runtime_client::{RuntimeClient, RuntimeClientBuilder}, ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT, }; -use ethers_core::types::U256; -use ethers_middleware::gas_oracle::{GasCategory, GasOracle, Polygon}; use ethers_providers::{is_local_endpoint, Middleware, Provider, DEFAULT_LOCAL_POLL_INTERVAL}; use eyre::{Result, WrapErr}; use foundry_config::NamedChain; @@ -251,44 +249,6 @@ impl ProviderBuilder { } } -/// Estimates EIP1559 fees depending on the chain -/// -/// Uses custom gas oracles for -/// - polygon -/// -/// Fallback is the default [`Provider::estimate_eip1559_fees`] implementation -pub async fn estimate_eip1559_fees( - provider: &M, - chain: Option, -) -> Result<(U256, U256)> -where - M::Error: 'static, -{ - let chain = if let Some(chain) = chain { - chain - } else { - provider.get_chainid().await.wrap_err("Failed to get chain id")?.as_u64() - }; - - if let Ok(chain) = NamedChain::try_from(chain) { - // handle chains that deviate from `eth_feeHistory` and have their own oracle - match chain { - NamedChain::Polygon | NamedChain::PolygonMumbai => { - // TODO: phase this out somehow - let chain = match chain { - NamedChain::Polygon => ethers_core::types::Chain::Polygon, - NamedChain::PolygonMumbai => ethers_core::types::Chain::PolygonMumbai, - _ => unreachable!(), - }; - let estimator = Polygon::new(chain)?.category(GasCategory::Standard); - return Ok(estimator.estimate_eip1559_fees().await?); - } - _ => {} - } - } - provider.estimate_eip1559_fees(None).await.wrap_err("Failed fetch EIP1559 fees") -} - #[cfg(not(windows))] fn resolve_path(path: &Path) -> Result { if path.is_absolute() { diff --git a/crates/common/src/provider/retry.rs b/crates/common/src/provider/retry.rs index e7277bd4b198..b6adfb646be8 100644 --- a/crates/common/src/provider/retry.rs +++ b/crates/common/src/provider/retry.rs @@ -49,6 +49,9 @@ impl RetryPolicy for RateLimitRetryPolicy { false } TransportError::ErrorResp(err) => should_retry_json_rpc_error(err), + TransportError::NullResp => true, + TransportError::UnsupportedFeature(_) => false, + TransportError::LocalUsageError(_) => false, } } diff --git a/crates/common/src/runtime_client.rs b/crates/common/src/runtime_client.rs index ea0fe593830c..3bb1631d5fb7 100644 --- a/crates/common/src/runtime_client.rs +++ b/crates/common/src/runtime_client.rs @@ -7,7 +7,7 @@ use ethers_providers::{ JsonRpcError, JwtAuth, JwtKey, ProviderError, PubsubClient, RetryClient, RetryClientBuilder, RpcError, Ws, }; -use reqwest::{ +use reqwest_ethers::{ header::{HeaderName, HeaderValue}, Url, }; @@ -128,10 +128,10 @@ impl RuntimeClient { async fn connect(&self) -> Result { match self.url.scheme() { "http" | "https" => { - let mut client_builder = reqwest::Client::builder() + let mut client_builder = reqwest_ethers::Client::builder() .timeout(self.timeout) .tls_built_in_root_certs(self.url.scheme() == "https"); - let mut headers = reqwest::header::HeaderMap::new(); + let mut headers = reqwest_ethers::header::HeaderMap::new(); if let Some(jwt) = self.jwt.as_ref() { let auth = build_auth(jwt.clone()).map_err(|err| { @@ -144,7 +144,7 @@ impl RuntimeClient { .expect("Header should be valid string"); auth_value.set_sensitive(true); - headers.insert(reqwest::header::AUTHORIZATION, auth_value); + headers.insert(reqwest_ethers::header::AUTHORIZATION, auth_value); }; for header in self.headers.iter() { diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index b337147d6171..cf3f5a89a75a 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -1,15 +1,16 @@ //! wrappers for transactions -use ethers_core::types::{BlockId, TransactionReceipt}; -use ethers_providers::Middleware; +use alloy_provider::{network::AnyNetwork, Provider}; +use alloy_rpc_types::{AnyTransactionReceipt, BlockId, WithOtherFields}; +use alloy_transport::Transport; use eyre::Result; use serde::{Deserialize, Serialize}; /// Helper type to carry a transaction along with an optional revert reason -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct TransactionReceiptWithRevertReason { /// The underlying transaction receipt #[serde(flatten)] - pub receipt: TransactionReceipt, + pub receipt: AnyTransactionReceipt, /// The revert reason string if the transaction status is failed #[serde(skip_serializing_if = "Option::is_none", rename = "revertReason")] @@ -18,68 +19,67 @@ pub struct TransactionReceiptWithRevertReason { impl TransactionReceiptWithRevertReason { /// Returns if the status of the transaction is 0 (failure) - pub fn is_failure(&self) -> Option { - self.receipt.status.map(|status| status.as_u64() == 0) + pub fn is_failure(&self) -> bool { + !self.receipt.inner.inner.inner.receipt.status } /// Updates the revert reason field using `eth_call` and returns an Err variant if the revert /// reason was not successfully updated - pub async fn update_revert_reason(&mut self, provider: &M) -> Result<()> { + pub async fn update_revert_reason>( + &mut self, + provider: &P, + ) -> Result<()> { self.revert_reason = self.fetch_revert_reason(provider).await?; Ok(()) } - async fn fetch_revert_reason(&self, provider: &M) -> Result> { - if let Some(false) | None = self.is_failure() { + async fn fetch_revert_reason>( + &self, + provider: &P, + ) -> Result> { + if !self.is_failure() { return Ok(None) } - if let Some(ref transaction) = provider - .get_transaction(self.receipt.transaction_hash) + let transaction = provider + .get_transaction_by_hash(self.receipt.transaction_hash) .await - .map_err(|_| eyre::eyre!("unable to fetch transaction"))? - { - if let Some(block_hash) = self.receipt.block_hash { - match provider.call(&transaction.into(), Some(BlockId::Hash(block_hash))).await { - Err(e) => return Ok(extract_revert_reason(e.to_string())), - Ok(_) => eyre::bail!("no revert reason as transaction succeeded"), - } + .map_err(|_| eyre::eyre!("unable to fetch transaction"))?; + + if let Some(block_hash) = self.receipt.block_hash { + match provider + .call( + &WithOtherFields::new(transaction.inner.into()), + Some(BlockId::Hash(block_hash.into())), + ) + .await + { + Err(e) => return Ok(extract_revert_reason(e.to_string())), + Ok(_) => eyre::bail!("no revert reason as transaction succeeded"), } - eyre::bail!("unable to fetch block_hash") } - Err(eyre::eyre!("transaction does not exist")) + eyre::bail!("unable to fetch block_hash") } } -impl From for TransactionReceiptWithRevertReason { - fn from(receipt: TransactionReceipt) -> Self { +impl From for TransactionReceiptWithRevertReason { + fn from(receipt: AnyTransactionReceipt) -> Self { Self { receipt, revert_reason: None } } } -impl From for TransactionReceipt { +impl From for AnyTransactionReceipt { fn from(receipt_with_reason: TransactionReceiptWithRevertReason) -> Self { receipt_with_reason.receipt } } fn extract_revert_reason>(error_string: S) -> Option { - let message_substr = "message: execution reverted: "; - - let mut temp = ""; - + let message_substr = "execution reverted: "; error_string .as_ref() .find(message_substr) - .and_then(|index| { - let (_, rest) = error_string.as_ref().split_at(index + message_substr.len()); - temp = rest; - rest.rfind(", ") - }) - .map(|index| { - let (reason, _) = temp.split_at(index); - reason.to_string() - }) + .map(|index| error_string.as_ref().split_at(index + message_substr.len()).1.to_string()) } #[cfg(test)] @@ -88,16 +88,10 @@ mod tests { #[test] fn test_extract_revert_reason() { - let error_string_1 = "(code: 3, message: execution reverted: Transaction too old, data: Some(String(\"0x08c379a0\")))"; - let error_string_2 = "(code: 3, message: execution reverted: missing data: amountIn, amountOut, data: Some(String(\"0x08c379a0\")))"; - let error_string_3 = - "(code: 4, message: invalid signature, data: Some(String(\"0x08c379a0\")))"; + let error_string_1 = "server returned an error response: error code 3: execution reverted: Transaction too old"; + let error_string_2 = "server returned an error response: error code 3: Invalid signature"; assert_eq!(extract_revert_reason(error_string_1), Some("Transaction too old".to_string())); - assert_eq!( - extract_revert_reason(error_string_2), - Some("missing data: amountIn, amountOut".to_string()) - ); - assert_eq!(extract_revert_reason(error_string_3), None); + assert_eq!(extract_revert_reason(error_string_2), None); } } diff --git a/crates/common/src/types.rs b/crates/common/src/types.rs index 6f4284b32410..bcfed539ff00 100644 --- a/crates/common/src/types.rs +++ b/crates/common/src/types.rs @@ -1,18 +1,14 @@ //! Temporary utility conversion traits between ethers-rs and alloy types. -use alloy_primitives::{Address, Bloom, Bytes, B256, B64, I256, U128, U256, U64}; -use alloy_rpc_types::{ - other::OtherFields, - request::{TransactionInput, TransactionRequest as CallRequest}, - AccessList, AccessListItem, Signature, Transaction, -}; -use alloy_signer::{LocalWallet, Signer}; +use alloy_primitives::{Address, Bloom, Bytes, B256, B64, I256, U256, U64}; +use alloy_rpc_types::{AccessList, AccessListItem, BlockNumberOrTag}; +use alloy_signer_wallet::LocalWallet; use ethers_core::types::{ transaction::eip2930::{ AccessList as EthersAccessList, AccessListItem as EthersAccessListItem, }, - Bloom as EthersBloom, Bytes as EthersBytes, TransactionRequest, H160, H256, H64, - I256 as EthersI256, U256 as EthersU256, U64 as EthersU64, + BlockNumber, Bloom as EthersBloom, Bytes as EthersBytes, H160, H256, H64, I256 as EthersI256, + U256 as EthersU256, U64 as EthersU64, }; /// Conversion trait to easily convert from Ethers types to Alloy types. @@ -105,43 +101,7 @@ impl ToAlloy for u64 { } } -impl ToAlloy for ethers_core::types::Transaction { - type To = Transaction; - - fn to_alloy(self) -> Self::To { - Transaction { - hash: self.hash.to_alloy(), - nonce: U64::from(self.nonce.as_u64()), - block_hash: self.block_hash.map(ToAlloy::to_alloy), - block_number: self.block_number.map(|b| U256::from(b.as_u64())), - transaction_index: self.transaction_index.map(|b| U256::from(b.as_u64())), - from: self.from.to_alloy(), - to: self.to.map(ToAlloy::to_alloy), - value: self.value.to_alloy(), - gas_price: self.gas_price.map(|a| U128::from(a.as_u128())), - gas: self.gas.to_alloy(), - max_fee_per_gas: self.max_fee_per_gas.map(|f| U128::from(f.as_u128())), - max_priority_fee_per_gas: self - .max_priority_fee_per_gas - .map(|f| U128::from(f.as_u128())), - max_fee_per_blob_gas: None, - input: self.input.0.into(), - signature: Some(Signature { - r: self.r.to_alloy(), - s: self.s.to_alloy(), - v: U256::from(self.v.as_u64()), - y_parity: None, - }), - chain_id: self.chain_id.map(|c| U64::from(c.as_u64())), - blob_versioned_hashes: Vec::new(), - access_list: self.access_list.map(|a| a.0.into_iter().map(ToAlloy::to_alloy).collect()), - transaction_type: self.transaction_type.map(|t| t.to_alloy()), - other: Default::default(), - } - } -} - -impl ToEthers for alloy_signer::LocalWallet { +impl ToEthers for alloy_signer_wallet::LocalWallet { type To = ethers_signers::LocalWallet; fn to_ethers(self) -> Self::To { @@ -161,34 +121,6 @@ impl ToEthers for Vec { } } -/// Converts from a [TransactionRequest] to a [CallRequest]. -pub fn to_call_request_from_tx_request(tx: TransactionRequest) -> CallRequest { - CallRequest { - from: tx.from.map(|f| f.to_alloy()), - to: match tx.to { - Some(to) => match to { - ethers_core::types::NameOrAddress::Address(addr) => Some(addr.to_alloy()), - ethers_core::types::NameOrAddress::Name(_) => None, - }, - None => None, - }, - gas_price: tx.gas_price.map(|g| g.to_alloy()), - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - gas: tx.gas.map(|g| g.to_alloy()), - value: tx.value.map(|v| v.to_alloy()), - input: TransactionInput::maybe_input(tx.data.map(|b| b.0.into())), - nonce: tx.nonce.map(|n| U64::from(n.as_u64())), - chain_id: tx.chain_id.map(|c| c.to_alloy()), - access_list: None, - max_fee_per_blob_gas: None, - blob_versioned_hashes: None, - transaction_type: None, - sidecar: None, - other: OtherFields::default(), - } -} - impl ToAlloy for EthersAccessList { type To = AccessList; fn to_alloy(self) -> Self::To { @@ -260,3 +192,19 @@ impl ToEthers for Bytes { EthersBytes(self.0) } } + +impl ToEthers for BlockNumberOrTag { + type To = BlockNumber; + + #[inline(always)] + fn to_ethers(self) -> Self::To { + match self { + BlockNumberOrTag::Number(n) => BlockNumber::Number(n.into()), + BlockNumberOrTag::Earliest => BlockNumber::Earliest, + BlockNumberOrTag::Latest => BlockNumber::Latest, + BlockNumberOrTag::Pending => BlockNumber::Pending, + BlockNumberOrTag::Finalized => BlockNumber::Finalized, + BlockNumberOrTag::Safe => BlockNumber::Safe, + } + } +} diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 7e43c22761d4..da1371c75bd4 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -36,7 +36,7 @@ serde_regex = "1" serde.workspace = true thiserror = "1" toml = { version = "0.8", features = ["preserve_order"] } -toml_edit = "0.21" +toml_edit = "0.22.4" tracing.workspace = true walkdir = "2" diff --git a/crates/config/src/fix.rs b/crates/config/src/fix.rs index a0ce9fbc1b0b..dc43fd25500b 100644 --- a/crates/config/src/fix.rs +++ b/crates/config/src/fix.rs @@ -10,7 +10,7 @@ use std::{ /// A convenience wrapper around a TOML document and the path it was read from struct TomlFile { - doc: toml_edit::Document, + doc: toml_edit::DocumentMut, path: PathBuf, } @@ -21,11 +21,11 @@ impl TomlFile { Ok(Self { doc, path }) } - fn doc(&self) -> &toml_edit::Document { + fn doc(&self) -> &toml_edit::DocumentMut { &self.doc } - fn doc_mut(&mut self) -> &mut toml_edit::Document { + fn doc_mut(&mut self) -> &mut toml_edit::DocumentMut { &mut self.doc } @@ -39,7 +39,7 @@ impl TomlFile { } impl Deref for TomlFile { - type Target = toml_edit::Document; + type Target = toml_edit::DocumentMut; fn deref(&self) -> &Self::Target { self.doc() } diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 88671dce194a..51769ab314a0 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1222,7 +1222,7 @@ impl Config { /// [Self::get_config_path()] and if the closure returns `true`. pub fn update_at(root: impl Into, f: F) -> eyre::Result<()> where - F: FnOnce(&Config, &mut toml_edit::Document) -> bool, + F: FnOnce(&Config, &mut toml_edit::DocumentMut) -> bool, { let config = Self::load_with_root(root).sanitized(); config.update(|doc| f(&config, doc)) @@ -1234,14 +1234,14 @@ impl Config { /// [Self::get_config_path()] and if the closure returns `true` pub fn update(&self, f: F) -> eyre::Result<()> where - F: FnOnce(&mut toml_edit::Document) -> bool, + F: FnOnce(&mut toml_edit::DocumentMut) -> bool, { let file_path = self.get_config_path(); if !file_path.exists() { return Ok(()) } let contents = fs::read_to_string(&file_path)?; - let mut doc = contents.parse::()?; + let mut doc = contents.parse::()?; if f(&mut doc) { fs::write(file_path, doc.to_string())?; } diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index d0d6f8e06663..cb23500088b4 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -14,7 +14,7 @@ use std::{ path::{Path, PathBuf}, str::FromStr, }; -use toml_edit::{Document, Item}; +use toml_edit::{DocumentMut, Item}; /// Loads the config for the current project workspace pub fn load_config() -> Config { @@ -216,9 +216,9 @@ pub fn get_available_profiles(toml_path: impl AsRef) -> eyre::Result) -> eyre::Result { +fn read_toml(path: impl AsRef) -> eyre::Result { let path = path.as_ref().to_owned(); - let doc: Document = std::fs::read_to_string(path)?.parse()?; + let doc: DocumentMut = std::fs::read_to_string(path)?.parse()?; Ok(doc) } diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 7e4ea3c218d2..cf922cbce492 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -19,9 +19,15 @@ foundry-macros.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-primitives = { workspace = true, features = [ + "serde", + "getrandom", + "arbitrary", + "rlp", +] } alloy-genesis.workspace = true -alloy-providers.workspace = true +alloy-provider.workspace = true +alloy-transport.workspace = true alloy-rpc-types.workspace = true alloy-sol-types.workspace = true diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index c0ff88ea40a1..59410541a737 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -18,7 +18,7 @@ use revm::{ }, Database, DatabaseCommit, Inspector, JournaledState, }; -use std::{borrow::Cow, collections::HashMap}; +use std::{borrow::Cow, collections::BTreeMap}; /// A wrapper around `Backend` that ensures only `revm::DatabaseRef` functions are called. /// @@ -159,7 +159,7 @@ impl<'a> DatabaseExt for CowBackend<'a> { fn roll_fork( &mut self, id: Option, - block_number: U256, + block_number: u64, env: &mut Env, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { @@ -213,7 +213,7 @@ impl<'a> DatabaseExt for CowBackend<'a> { fn load_allocs( &mut self, - allocs: &HashMap, + allocs: &BTreeMap, journaled_state: &mut JournaledState, ) -> Result<(), DatabaseError> { self.backend_mut(&Env::default()).load_allocs(allocs, journaled_state) diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index ca6cbb1ef8bd..dbd5071eed60 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -7,8 +7,8 @@ use crate::{ utils::configure_tx_env, }; use alloy_genesis::GenesisAccount; -use alloy_primitives::{b256, keccak256, Address, B256, U256, U64}; -use alloy_rpc_types::{Block, BlockNumberOrTag, BlockTransactions, Transaction}; +use alloy_primitives::{b256, keccak256, Address, B256, U256}; +use alloy_rpc_types::{Block, BlockNumberOrTag, BlockTransactions, Transaction, WithOtherFields}; use eyre::Context; use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; use revm::{ @@ -22,7 +22,7 @@ use revm::{ Database, DatabaseCommit, Inspector, JournaledState, }; use std::{ - collections::{HashMap, HashSet}, + collections::{BTreeMap, HashMap, HashSet}, time::Instant, }; @@ -166,7 +166,7 @@ pub trait DatabaseExt: Database { fn roll_fork( &mut self, id: Option, - block_number: U256, + block_number: u64, env: &mut Env, journaled_state: &mut JournaledState, ) -> eyre::Result<()>; @@ -262,7 +262,7 @@ pub trait DatabaseExt: Database { /// Returns [Ok] if all accounts were successfully inserted into the journal, [Err] otherwise. fn load_allocs( &mut self, - allocs: &HashMap, + allocs: &BTreeMap, journaled_state: &mut JournaledState, ) -> Result<(), DatabaseError>; @@ -857,18 +857,18 @@ impl Backend { &self, id: LocalForkId, transaction: B256, - ) -> eyre::Result<(U64, Block)> { + ) -> eyre::Result<(u64, Block)> { let fork = self.inner.get_fork_by_id(id)?; let tx = fork.db.db.get_transaction(transaction)?; // get the block number we need to fork if let Some(tx_block) = tx.block_number { - let block = fork.db.db.get_full_block(tx_block.to::())?; + let block = fork.db.db.get_full_block(tx_block)?; // we need to subtract 1 here because we want the state before the transaction // was mined - let fork_block = tx_block.to::() - 1; - Ok((U64::from(fork_block), block)) + let fork_block = tx_block - 1; + Ok((fork_block, block)) } else { let block = fork.db.db.get_full_block(BlockNumberOrTag::Latest)?; @@ -877,7 +877,7 @@ impl Backend { .number .ok_or_else(|| DatabaseError::BlockNotFound(BlockNumberOrTag::Latest.into()))?; - Ok((number.to::(), block)) + Ok((number, block)) } } @@ -904,7 +904,7 @@ impl Backend { // System transactions such as on L2s don't contain any pricing info so we skip them // otherwise this would cause reverts if is_known_system_sender(tx.from) || - tx.transaction_type.map(|ty| ty.to::()) == Some(SYSTEM_TRANSACTION_TYPE) + tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE) { trace!(tx=?tx.hash, "skipping system transaction"); continue; @@ -917,7 +917,7 @@ impl Backend { trace!(tx=?tx.hash, "committing transaction"); commit_transaction( - tx, + WithOtherFields::new(tx), env.clone(), journaled_state, fork, @@ -976,7 +976,7 @@ impl DatabaseExt for Backend { // another caller, so we need to ensure the caller account is present in the // journaled state and database let caller = current.tx.caller; - if !journaled_state.state.contains_key(&caller) { + journaled_state.state.entry(caller).or_insert_with(|| { let caller_account = current_state .state .get(&caller) @@ -987,8 +987,8 @@ impl DatabaseExt for Backend { // update the caller account which is required by the evm fork.db.insert_account_info(caller, caller_account.clone()); } - journaled_state.state.insert(caller, caller_account.into()); - } + caller_account.into() + }); self.inner.revert_snapshot(id, fork_id, idx, *fork); self.active_fork_ids = Some((id, idx)) } @@ -1115,7 +1115,7 @@ impl DatabaseExt for Backend { // necessarily the same caller as for the test, however we must always // ensure that fork's state contains the current sender let caller = env.tx.caller; - if !fork.journaled_state.state.contains_key(&caller) { + fork.journaled_state.state.entry(caller).or_insert_with(|| { let caller_account = active_journaled_state .state .get(&env.tx.caller) @@ -1126,8 +1126,8 @@ impl DatabaseExt for Backend { // update the caller account which is required by the evm fork.db.insert_account_info(caller, caller_account.clone()); } - fork.journaled_state.state.insert(caller, caller_account.into()); - } + caller_account.into() + }); self.update_fork_db(active_journaled_state, &mut fork); @@ -1147,14 +1147,14 @@ impl DatabaseExt for Backend { fn roll_fork( &mut self, id: Option, - block_number: U256, + block_number: u64, env: &mut Env, journaled_state: &mut JournaledState, ) -> eyre::Result<()> { trace!(?id, ?block_number, "roll fork"); let id = self.ensure_fork(id)?; let (fork_id, backend, fork_env) = - self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number.to())?; + self.forks.roll_fork(self.inner.ensure_fork_id(id).cloned()?, block_number)?; // this will update the local mapping self.inner.roll_fork(id, fork_id, backend)?; @@ -1216,7 +1216,7 @@ impl DatabaseExt for Backend { self.get_block_number_and_block_for_transaction(id, transaction)?; // roll the fork to the transaction's block or latest if it's pending - self.roll_fork(Some(id), fork_block.to(), env, journaled_state)?; + self.roll_fork(Some(id), fork_block, env, journaled_state)?; update_env_block(env, fork_block, &block); @@ -1332,7 +1332,7 @@ impl DatabaseExt for Backend { /// Returns [Ok] if all accounts were successfully inserted into the journal, [Err] otherwise. fn load_allocs( &mut self, - allocs: &HashMap, + allocs: &BTreeMap, journaled_state: &mut JournaledState, ) -> Result<(), DatabaseError> { // Loop through all of the allocs defined in the map and commit them to the journal. @@ -1855,20 +1855,20 @@ fn is_contract_in_state(journaled_state: &JournaledState, acc: Address) -> bool } /// Updates the env's block with the block's data -fn update_env_block(env: &mut Env, fork_block: U64, block: &Block) { - env.block.timestamp = block.header.timestamp; +fn update_env_block(env: &mut Env, fork_block: u64, block: &Block) { + env.block.timestamp = U256::from(block.header.timestamp); env.block.coinbase = block.header.miner; env.block.difficulty = block.header.difficulty; env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); - env.block.basefee = block.header.base_fee_per_gas.unwrap_or_default(); - env.block.gas_limit = block.header.gas_limit; - env.block.number = block.header.number.map(|n| n.to()).unwrap_or(fork_block.to()); + env.block.basefee = U256::from(block.header.base_fee_per_gas.unwrap_or_default()); + env.block.gas_limit = U256::from(block.header.gas_limit); + env.block.number = U256::from(block.header.number.unwrap_or(fork_block)); } /// Executes the given transaction and commits state changes to the database _and_ the journaled /// state, with an optional inspector fn commit_transaction>( - tx: Transaction, + tx: WithOtherFields, mut env: EnvWithHandlerCfg, journaled_state: &mut JournaledState, fork: &mut Fork, diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index 8e720a2505d2..f6da76fc5c42 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -4,8 +4,9 @@ use crate::{ fork::{cache::FlushJsonBlockCacheDB, BlockchainDb}, }; use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; -use alloy_providers::tmp::TempProvider; -use alloy_rpc_types::{Block, BlockId, Transaction}; +use alloy_provider::{network::AnyNetwork, Provider}; +use alloy_rpc_types::{Block, BlockId, Transaction, WithOtherFields}; +use alloy_transport::Transport; use eyre::WrapErr; use foundry_common::NON_ARCHIVE_NODE_WARNING; use futures::{ @@ -21,6 +22,7 @@ use revm::{ use rustc_hash::FxHashMap; use std::{ collections::{hash_map::Entry, HashMap, VecDeque}, + marker::PhantomData, pin::Pin, sync::{ mpsc::{channel as oneshot_channel, Sender as OneshotSender}, @@ -31,19 +33,23 @@ use std::{ // Various future/request type aliases type AccountFuture = - Pin, Address)> + Send>>; + Pin, Address)> + Send>>; type StorageFuture = Pin, Address, U256)> + Send>>; type BlockHashFuture = Pin, u64)> + Send>>; type FullBlockFuture = Pin, Err>, BlockId)> + Send>>; -type TransactionFuture = - Pin, B256)> + Send>>; +type TransactionFuture = Pin< + Box< + dyn Future, Err>, B256)> + + Send, + >, +>; type AccountInfoSender = OneshotSender>; type StorageSender = OneshotSender>; type BlockHashSender = OneshotSender>; type FullBlockSender = OneshotSender>; -type TransactionSender = OneshotSender>; +type TransactionSender = OneshotSender>>; /// Request variants that are executed by the provider enum ProviderRequest { @@ -76,8 +82,9 @@ enum BackendRequest { /// This handler will remain active as long as it is reachable (request channel still open) and /// requests are in progress. #[must_use = "futures do nothing unless polled"] -pub struct BackendHandler

{ +pub struct BackendHandler { provider: P, + transport: PhantomData, /// Stores all the data. db: BlockchainDb, /// Requests currently in progress @@ -97,9 +104,10 @@ pub struct BackendHandler

{ block_id: Option, } -impl

BackendHandler

+impl BackendHandler where - P: TempProvider + Clone + 'static, + T: Transport + Clone, + P: Provider + Clone + Unpin + 'static, { fn new( provider: P, @@ -117,6 +125,7 @@ where queued_requests: Default::default(), incoming: rx, block_id, + transport: PhantomData, } } @@ -197,7 +206,7 @@ where let fut = Box::pin(async move { let balance = provider.get_balance(address, block_id); let nonce = provider.get_transaction_count(address, block_id); - let code = provider.get_code_at(address, block_id); + let code = provider.get_code_at(address, block_id.unwrap_or_default()); let resp = tokio::try_join!(balance, nonce, code).map_err(Into::into); (resp, address) }); @@ -283,9 +292,10 @@ where } } -impl

Future for BackendHandler

+impl Future for BackendHandler where - P: TempProvider + Clone + Unpin + 'static, + T: Transport + Clone + Unpin, + P: Provider + Clone + Unpin + 'static, { type Output = (); @@ -343,7 +353,7 @@ where // update the cache let acc = AccountInfo { - nonce: nonce.to(), + nonce, balance, code: Some(Bytecode::new_raw(code).to_checked()), code_hash, @@ -512,9 +522,14 @@ impl SharedBackend { /// dropped. /// /// NOTE: this should be called with `Arc` - pub async fn spawn_backend

(provider: P, db: BlockchainDb, pin_block: Option) -> Self + pub async fn spawn_backend( + provider: P, + db: BlockchainDb, + pin_block: Option, + ) -> Self where - P: TempProvider + Unpin + 'static + Clone, + T: Transport + Clone + Unpin, + P: Provider + Unpin + 'static + Clone, { let (shared, handler) = Self::new(provider, db, pin_block); // spawn the provider handler to a task @@ -525,13 +540,14 @@ impl SharedBackend { /// Same as `Self::spawn_backend` but spawns the `BackendHandler` on a separate `std::thread` in /// its own `tokio::Runtime` - pub fn spawn_backend_thread

( + pub fn spawn_backend_thread( provider: P, db: BlockchainDb, pin_block: Option, ) -> Self where - P: TempProvider + Unpin + 'static + Clone, + T: Transport + Clone + Unpin, + P: Provider + Unpin + 'static + Clone, { let (shared, handler) = Self::new(provider, db, pin_block); @@ -554,13 +570,14 @@ impl SharedBackend { } /// Returns a new `SharedBackend` and the `BackendHandler` - pub fn new

( + pub fn new( provider: P, db: BlockchainDb, pin_block: Option, - ) -> (Self, BackendHandler

) + ) -> (Self, BackendHandler) where - P: TempProvider + Clone + 'static, + T: Transport + Clone + Unpin, + P: Provider + Unpin + 'static + Clone, { let (backend, backend_rx) = channel(1); let cache = Arc::new(FlushJsonBlockCacheDB(Arc::clone(db.cache()))); @@ -585,7 +602,7 @@ impl SharedBackend { } /// Returns the transaction for the hash - pub fn get_transaction(&self, tx: B256) -> DatabaseResult { + pub fn get_transaction(&self, tx: B256) -> DatabaseResult> { tokio::task::block_in_place(|| { let (sender, rx) = oneshot_channel(); let req = BackendRequest::Transaction(tx, sender); diff --git a/crates/evm/core/src/fork/init.rs b/crates/evm/core/src/fork/init.rs index abde7cb22dea..b69e02aad9db 100644 --- a/crates/evm/core/src/fork/init.rs +++ b/crates/evm/core/src/fork/init.rs @@ -1,7 +1,8 @@ use crate::utils::apply_chain_and_block_specific_env_changes; use alloy_primitives::{Address, U256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::{Network, Provider}; use alloy_rpc_types::{Block, BlockNumberOrTag}; +use alloy_transport::Transport; use eyre::WrapErr; use foundry_common::NON_ARCHIVE_NODE_WARNING; @@ -10,10 +11,10 @@ use revm::primitives::{BlockEnv, CfgEnv, Env, TxEnv}; /// Initializes a REVM block environment based on a forked /// ethereum provider. // todo(onbjerg): these bounds needed cus of the bounds in `Provider`, can simplify? -pub async fn environment( +pub async fn environment>( provider: &P, memory_limit: u64, - gas_price: Option, + gas_price: Option, override_chain_id: Option, pin_block: Option, origin: Address, @@ -49,7 +50,7 @@ pub async fn environment( }; let mut cfg = CfgEnv::default(); - cfg.chain_id = override_chain_id.unwrap_or(rpc_chain_id.to::()); + cfg.chain_id = override_chain_id.unwrap_or(rpc_chain_id); cfg.memory_limit = memory_limit; cfg.limit_contract_code_size = Some(usize::MAX); // EIP-3607 rejects transactions from senders with deployed code. @@ -61,20 +62,20 @@ pub async fn environment( let mut env = Env { cfg, block: BlockEnv { - number: block.header.number.expect("block number not found"), - timestamp: block.header.timestamp, + number: U256::from(block.header.number.expect("block number not found")), + timestamp: U256::from(block.header.timestamp), coinbase: block.header.miner, difficulty: block.header.difficulty, prevrandao: Some(block.header.mix_hash.unwrap_or_default()), - basefee: block.header.base_fee_per_gas.unwrap_or_default(), - gas_limit: block.header.gas_limit, + basefee: U256::from(block.header.base_fee_per_gas.unwrap_or_default()), + gas_limit: U256::from(block.header.gas_limit), ..Default::default() }, tx: TxEnv { caller: origin, - gas_price: gas_price.map(U256::from).unwrap_or(fork_gas_price), - chain_id: Some(override_chain_id.unwrap_or(rpc_chain_id.to::())), - gas_limit: block.header.gas_limit.to::(), + gas_price: U256::from(gas_price.unwrap_or(fork_gas_price)), + chain_id: Some(override_chain_id.unwrap_or(rpc_chain_id)), + gas_limit: block.header.gas_limit as u64, ..Default::default() }, }; diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index 4e1cb66e5ff4..60096784c60e 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -4,7 +4,11 @@ //! concurrently active pairs at once. use crate::fork::{BackendHandler, BlockchainDb, BlockchainDbMeta, CreateFork, SharedBackend}; -use foundry_common::provider::alloy::{ProviderBuilder, RetryProvider}; +use foundry_common::provider::{ + alloy::{ProviderBuilder, RetryProvider}, + runtime_transport::RuntimeTransport, + tower::RetryBackoffService, +}; use foundry_config::Config; use futures::{ channel::mpsc::{channel, Receiver, Sender}, @@ -167,7 +171,7 @@ impl MultiFork { } } -type Handler = BackendHandler>; +type Handler = BackendHandler, Arc>; type CreateFuture = Pin> + Send>>; @@ -498,7 +502,7 @@ async fn create_fork(mut fork: CreateFork) -> eyre::Result<(ForkId, CreatedFork, // we need to use the block number from the block because the env's number can be different on // some L2s (e.g. Arbitrum). - let number = block.header.number.unwrap_or(meta.block_env.number).to::(); + let number = block.header.number.unwrap_or(meta.block_env.number.to()); // determine the cache path if caching is enabled let cache_path = if fork.enable_caching { diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 89fdcf4e3869..fcd92dd6adf3 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -1,7 +1,7 @@ use super::fork::environment; use crate::fork::CreateFork; use alloy_primitives::{Address, B256, U256}; -use alloy_providers::tmp::TempProvider; +use alloy_provider::Provider; use alloy_rpc_types::Block; use eyre::WrapErr; use foundry_common::{ @@ -95,7 +95,7 @@ impl EvmOpts { environment( &provider, self.memory_limit, - self.env.gas_price, + self.env.gas_price.map(|v| v as u128), self.env.chain_id, self.fork_block_number, self.sender, @@ -205,7 +205,7 @@ impl EvmOpts { .unwrap_or_else(|| panic!("Failed to establish provider to {url}")); if let Ok(id) = RuntimeOrHandle::new().block_on(provider.get_chain_id()) { - return Some(Chain::from(id.to::())); + return Some(Chain::from(id)); } } diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 7ef8bb47f2d4..ee8a8a4d4758 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -25,7 +25,7 @@ pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::primitives::En match chain { NamedChain::Mainnet => { // after merge difficulty is supplanted with prevrandao EIP-4399 - if block_number.to::() >= 15_537_351u64 { + if block_number >= 15_537_351u64 { env.block.difficulty = env.block.prevrandao.unwrap_or_default().into(); } @@ -68,14 +68,15 @@ pub fn get_function( /// Configures the env for the transaction pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) { env.tx.caller = tx.from; - env.tx.gas_limit = tx.gas.to(); - env.tx.gas_price = tx.gas_price.unwrap_or_default().to(); - env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(|g| g.to()); - env.tx.nonce = Some(tx.nonce.to()); + env.tx.gas_limit = tx.gas as u64; + env.tx.gas_price = U256::from(tx.gas_price.unwrap_or_default()); + env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(U256::from); + env.tx.nonce = Some(tx.nonce); env.tx.access_list = tx .access_list .clone() .unwrap_or_default() + .0 .into_iter() .map(|item| { ( diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 6f83bf43f867..a38bf0eeeeaf 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -515,9 +515,7 @@ impl InspectorStack { if let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) { acc_mut.status |= acc.status; for (key, val) in acc.storage { - if !acc_mut.storage.contains_key(&key) { - acc_mut.storage.insert(key, val); - } + acc_mut.storage.entry(key).or_insert(val); } } else { ecx.journaled_state.state.insert(addr, acc); diff --git a/crates/evm/fuzz/src/strategies/state.rs b/crates/evm/fuzz/src/strategies/state.rs index 3a3b17ed641e..f432154b9f9b 100644 --- a/crates/evm/fuzz/src/strategies/state.rs +++ b/crates/evm/fuzz/src/strategies/state.rs @@ -212,7 +212,11 @@ pub fn build_initial_state( let mut values = IndexSet::new(); let mut addresses = IndexSet::new(); - for (address, account) in db.accounts.iter() { + // Sort accounts to ensure deterministic dictionary generation from the same setUp state. + let mut accs = db.accounts.iter().collect::>(); + accs.sort_by_key(|(address, _)| *address); + + for (address, account) in accs { let address: Address = *address; // Insert basic account information values.insert(address.into_word().into()); diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index f5a6b27c7911..b7508ae4b27a 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -28,10 +28,6 @@ foundry-wallets.workspace = true foundry-linking.workspace = true ethers-contract.workspace = true -ethers-core.workspace = true -ethers-middleware.workspace = true -ethers-providers.workspace = true -ethers-signers.workspace = true revm-inspectors.workspace = true @@ -55,6 +51,12 @@ alloy-dyn-abi.workspace = true alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = ["serde"] } alloy-rpc-types.workspace = true +alloy-provider.workspace = true +alloy-network.workspace = true +alloy-transport.workspace = true +alloy-signer.workspace = true +alloy-consensus.workspace = true +alloy-chains.workspace = true async-trait = "0.1" clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } @@ -103,6 +105,9 @@ svm = { package = "svm-rs", version = "0.4", default-features = false, features tempfile = "3" tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } +ethers-core.workspace = true +alloy-signer-wallet.workspace = true + [features] default = ["rustls"] rustls = [ @@ -111,7 +116,7 @@ rustls = [ "reqwest/rustls-tls", "reqwest/rustls-tls-native-roots", ] -openssl = ["foundry-cli/openssl", "reqwest/default-tls", "foundry-wallets/openssl"] +openssl = ["foundry-cli/openssl", "reqwest/default-tls"] asm-keccak = ["alloy-primitives/asm-keccak"] jemalloc = ["dep:tikv-jemallocator"] diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 9dee30675d5f..e42a17057bdb 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -1,17 +1,13 @@ -use alloy_dyn_abi::{DynSolValue, JsonAbiExt, ResolveSolType}; +use alloy_chains::Chain; +use alloy_dyn_abi::{DynSolValue, JsonAbiExt, Specifier}; use alloy_json_abi::{Constructor, JsonAbi}; +use alloy_network::{AnyNetwork, EthereumSigner, TransactionBuilder}; use alloy_primitives::{Address, Bytes}; +use alloy_provider::{Provider, ProviderBuilder}; +use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest, WithOtherFields}; +use alloy_signer::Signer; +use alloy_transport::{Transport, TransportError}; use clap::{Parser, ValueHint}; -use ethers_contract::ContractError; -use ethers_core::{ - abi::InvalidOutputType, - types::{ - transaction::eip2718::TypedTransaction, BlockNumber, Chain, Eip1559TransactionRequest, - TransactionReceipt, TransactionRequest, - }, -}; -use ethers_middleware::SignerMiddleware; -use ethers_providers::Middleware; use eyre::{Context, Result}; use forge_verify::RetryArgs; use foundry_cli::{ @@ -19,10 +15,7 @@ use foundry_cli::{ utils::{self, read_constructor_args_file, remove_contract, LoadConfig}, }; use foundry_common::{ - compile::ProjectCompiler, - fmt::parse_tokens, - provider::ethers::estimate_eip1559_fees, - types::{ToAlloy, ToEthers}, + compile::ProjectCompiler, fmt::parse_tokens, provider::alloy::estimate_eip1559_fees, }; use foundry_compilers::{artifacts::BytecodeObject, info::ContractInfo, utils::canonicalized}; use serde_json::json; @@ -136,18 +129,20 @@ impl CreateArgs { let chain_id = if let Some(chain_id) = self.chain_id() { chain_id } else { - provider.get_chainid().await?.as_u64() + provider.get_chain_id().await? }; if self.unlocked { // Deploy with unlocked account let sender = self.eth.wallet.from.expect("required"); - let provider = provider.with_sender(sender.to_ethers()); - self.deploy(abi, bin, params, provider, chain_id).await + self.deploy(abi, bin, params, provider, chain_id, sender).await } else { // Deploy with signer let signer = self.eth.wallet.signer().await?; - let provider = SignerMiddleware::new_with_provider_chain(provider, signer).await?; - self.deploy(abi, bin, params, provider, chain_id).await + let deployer = signer.address(); + let provider = ProviderBuilder::<_, _, AnyNetwork>::default() + .signer(EthereumSigner::new(signer)) + .on_provider(provider); + self.deploy(abi, bin, params, provider, chain_id, deployer).await } } @@ -206,16 +201,15 @@ impl CreateArgs { } /// Deploys the contract - async fn deploy( + async fn deploy, T: Transport + Clone>( self, abi: JsonAbi, bin: BytecodeObject, args: Vec, - provider: M, + provider: P, chain: u64, + deployer_address: Address, ) -> Result<()> { - let deployer_address = - provider.default_sender().expect("no sender address set for provider"); let bin = bin.into_bytes().unwrap_or_else(|| { panic!("no bytecode found in bin object for {}", self.contract.name) }); @@ -223,7 +217,7 @@ impl CreateArgs { let factory = ContractFactory::new(abi.clone(), bin.clone(), provider.clone()); let is_args_empty = args.is_empty(); - let deployer = + let mut deployer = factory.deploy_tokens(args.clone()).context("failed to deploy contract").map_err(|e| { if is_args_empty { e.wrap_err("no arguments provided for contract constructor; consider --constructor-args or --constructor-args-path") @@ -231,57 +225,52 @@ impl CreateArgs { e } })?; - let is_legacy = self.tx.legacy || - Chain::try_from(chain).map(|x| Chain::is_legacy(&x)).unwrap_or_default(); - let mut deployer = if is_legacy { deployer.legacy() } else { deployer }; + let is_legacy = self.tx.legacy || Chain::from(chain).is_legacy(); + + deployer.tx.set_from(deployer_address); + deployer.tx.set_chain_id(chain); + + deployer.tx.set_nonce(if let Some(nonce) = self.tx.nonce { + Ok(nonce.to()) + } else { + provider.get_transaction_count(deployer_address, None).await + }?); + + deployer.tx.set_gas_limit(if let Some(gas_limit) = self.tx.gas_limit { + Ok(gas_limit.to()) + } else { + provider.estimate_gas(&deployer.tx, None).await + }?); // set tx value if specified if let Some(value) = self.tx.value { - deployer.tx.set_value(value.to_ethers()); + deployer.tx.set_value(value); } - // fill tx first because if you target a lower gas than current base, eth_estimateGas - // will fail and create will fail - provider.fill_transaction(&mut deployer.tx, None).await?; - - // the max - let mut priority_fee = self.tx.priority_gas_price; - - // set gas price if specified - if let Some(gas_price) = self.tx.gas_price { - deployer.tx.set_gas_price(gas_price.to_ethers()); - } else if !is_legacy { - // estimate EIP1559 fees - let (max_fee, max_priority_fee) = estimate_eip1559_fees(&provider, Some(chain)) + if is_legacy { + let gas_price = if let Some(gas_price) = self.tx.gas_price { + gas_price.to() + } else { + provider.get_gas_price().await? + }; + deployer.tx.set_gas_price(gas_price); + } else { + let estimate = estimate_eip1559_fees(&provider, Some(chain)) .await .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; - deployer.tx.set_gas_price(max_fee); - if priority_fee.is_none() { - priority_fee = Some(max_priority_fee.to_alloy()); - } - } - - // set gas limit if specified - if let Some(gas_limit) = self.tx.gas_limit { - deployer.tx.set_gas(gas_limit.to_ethers()); - } - - // set nonce if specified - if let Some(nonce) = self.tx.nonce { - deployer.tx.set_nonce(nonce.to_ethers()); - } - - // set priority fee if specified - if let Some(priority_fee) = priority_fee { - if is_legacy { - eyre::bail!("there is no priority fee for legacy txs"); - } - deployer.tx = match deployer.tx { - TypedTransaction::Eip1559(eip1559_tx_request) => TypedTransaction::Eip1559( - eip1559_tx_request.max_priority_fee_per_gas(priority_fee.to_ethers()), - ), - _ => deployer.tx, + let priority_fee = if let Some(priority_fee) = self.tx.priority_gas_price { + priority_fee.to() + } else { + estimate.max_priority_fee_per_gas }; + let max_fee = if let Some(max_fee) = self.tx.gas_price { + max_fee.to() + } else { + estimate.max_fee_per_gas + }; + + deployer.tx.set_max_fee_per_gas(max_fee); + deployer.tx.set_max_priority_fee_per_gas(priority_fee); } // Before we actually deploy the contract we try check if the verify settings are valid @@ -304,13 +293,13 @@ impl CreateArgs { let address = deployed_contract; if self.json { let output = json!({ - "deployer": deployer_address.to_alloy().to_string(), + "deployer": deployer_address.to_string(), "deployedTo": address.to_string(), "transactionHash": receipt.transaction_hash }); println!("{output}"); } else { - println!("Deployer: {}", deployer_address.to_alloy()); + println!("Deployer: {deployer_address}"); println!("Deployed to: {address}"); println!("Transaction hash: {:?}", receipt.transaction_hash); }; @@ -376,7 +365,7 @@ impl CreateArgs { /// compatibility with less-abstract Contracts. /// /// For full usage docs, see [`DeploymentTxFactory`]. -pub type ContractFactory = DeploymentTxFactory, M>; +pub type ContractFactory = DeploymentTxFactory, P, T>; /// Helper which manages the deployment transaction of a smart contract. It /// wraps a deployment transaction, and retrieves the contract address output @@ -385,16 +374,16 @@ pub type ContractFactory = DeploymentTxFactory, M>; /// Currently, we recommend using the [`ContractDeployer`] type alias. #[derive(Debug)] #[must_use = "ContractDeploymentTx does nothing unless you `send` it"] -pub struct ContractDeploymentTx { +pub struct ContractDeploymentTx { /// the actual deployer, exposed for overriding the defaults - pub deployer: Deployer, + pub deployer: Deployer, /// marker for the `Contract` type to create afterwards /// /// this type will be used to construct it via `From::from(Contract)` _contract: PhantomData, } -impl Clone for ContractDeploymentTx +impl Clone for ContractDeploymentTx where B: Clone, { @@ -403,8 +392,8 @@ where } } -impl From> for ContractDeploymentTx { - fn from(deployer: Deployer) -> Self { +impl From> for ContractDeploymentTx { + fn from(deployer: Deployer) -> Self { Self { deployer, _contract: PhantomData } } } @@ -412,17 +401,17 @@ impl From> for ContractDeploymentTx { /// Helper which manages the deployment transaction of a smart contract #[derive(Debug)] #[must_use = "Deployer does nothing unless you `send` it"] -pub struct Deployer { +pub struct Deployer { /// The deployer's transaction, exposed for overriding the defaults - pub tx: TypedTransaction, + pub tx: WithOtherFields, abi: JsonAbi, client: B, confs: usize, - block: BlockNumber, - _m: PhantomData, + _p: PhantomData

, + _t: PhantomData, } -impl Clone for Deployer +impl Clone for Deployer where B: Clone, { @@ -432,53 +421,38 @@ where abi: self.abi.clone(), client: self.client.clone(), confs: self.confs, - block: self.block, - _m: PhantomData, + _p: PhantomData, + _t: PhantomData, } } } -impl Deployer +impl Deployer where - B: Borrow + Clone, - M: Middleware, + B: Borrow

+ Clone, + P: Provider, + T: Transport + Clone, { - /// Uses a Legacy transaction instead of an EIP-1559 one to do the deployment - pub fn legacy(mut self) -> Self { - self.tx = match self.tx { - TypedTransaction::Eip1559(inner) => { - let tx: TransactionRequest = inner.into(); - TypedTransaction::Legacy(tx) - } - other => other, - }; - self - } - /// Broadcasts the contract deployment transaction and after waiting for it to /// be sufficiently confirmed (default: 1), it returns a tuple with /// the [`Contract`](crate::Contract) struct at the deployed contract's address - /// and the corresponding [`TransactionReceipt`]. + /// and the corresponding [`AnyReceipt`]. pub async fn send_with_receipt( self, - ) -> Result<(Address, TransactionReceipt), ContractError> { - let pending_tx = self + ) -> Result<(Address, AnyTransactionReceipt), ContractDeploymentError> { + let receipt = self .client .borrow() - .send_transaction(self.tx, Some(self.block.into())) - .await - .map_err(ContractError::from_middleware_error)?; - - // TODO: Should this be calculated "optimistically" by address/nonce? - let receipt = pending_tx - .confirmations(self.confs) - .await - .ok() - .flatten() - .ok_or(ContractError::ContractNotDeployed)?; - let address = receipt.contract_address.ok_or(ContractError::ContractNotDeployed)?; - - Ok((address.to_alloy(), receipt)) + .send_transaction(self.tx) + .await? + .with_required_confirmations(self.confs as u64) + .get_receipt() + .await?; + + let address = + receipt.contract_address.ok_or(ContractDeploymentError::ContractNotDeployed)?; + + Ok((address, receipt)) } } @@ -519,14 +493,15 @@ where /// # Ok(()) /// # } #[derive(Debug)] -pub struct DeploymentTxFactory { +pub struct DeploymentTxFactory { client: B, abi: JsonAbi, bytecode: Bytes, - _m: PhantomData, + _p: PhantomData

, + _t: PhantomData, } -impl Clone for DeploymentTxFactory +impl Clone for DeploymentTxFactory where B: Clone, { @@ -535,39 +510,42 @@ where client: self.client.clone(), abi: self.abi.clone(), bytecode: self.bytecode.clone(), - _m: PhantomData, + _p: PhantomData, + _t: PhantomData, } } } -impl DeploymentTxFactory +impl DeploymentTxFactory where - B: Borrow + Clone, - M: Middleware, + B: Borrow

+ Clone, + P: Provider, + T: Transport + Clone, { /// Creates a factory for deployment of the Contract with bytecode, and the /// constructor defined in the abi. The client will be used to send any deployment /// transaction. pub fn new(abi: JsonAbi, bytecode: Bytes, client: B) -> Self { - Self { client, abi, bytecode, _m: PhantomData } + Self { client, abi, bytecode, _p: PhantomData, _t: PhantomData } } /// Create a deployment tx using the provided tokens as constructor /// arguments - pub fn deploy_tokens(self, params: Vec) -> Result, ContractError> + pub fn deploy_tokens( + self, + params: Vec, + ) -> Result, ContractDeploymentError> where B: Clone, { // Encode the constructor args & concatenate with the bytecode if necessary let data: Bytes = match (self.abi.constructor(), params.is_empty()) { - (None, false) => return Err(ContractError::ConstructorError), + (None, false) => return Err(ContractDeploymentError::ConstructorError), (None, true) => self.bytecode.clone(), (Some(constructor), _) => { let input: Bytes = constructor .abi_encode_input(¶ms) - .map_err(|f| { - ContractError::DetokenizationError(InvalidOutputType(f.to_string())) - })? + .map_err(ContractDeploymentError::DetokenizationError)? .into(); // Concatenate the bytecode and abi-encoded constructor call. self.bytecode.iter().copied().chain(input).collect() @@ -575,27 +553,32 @@ where }; // create the tx object. Since we're deploying a contract, `to` is `None` - // We default to EIP1559 transactions, but the sender can convert it back - // to a legacy one. - let tx = Eip1559TransactionRequest { - to: None, - data: Some(data.to_ethers()), - ..Default::default() - }; - - let tx = tx.into(); + let tx = WithOtherFields::new(TransactionRequest::default().input(data.into()).to(None)); Ok(Deployer { client: self.client.clone(), abi: self.abi, tx, confs: 1, - block: BlockNumber::Latest, - _m: PhantomData, + _p: PhantomData, + _t: PhantomData, }) } } +#[derive(thiserror::Error, Debug)] +/// An Error which is thrown when interacting with a smart contract +pub enum ContractDeploymentError { + #[error("constructor is not defined in the ABI")] + ConstructorError, + #[error(transparent)] + DetokenizationError(#[from] alloy_dyn_abi::Error), + #[error("contract was not deployed")] + ContractNotDeployed, + #[error(transparent)] + RpcError(#[from] TransportError), +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/forge/tests/cli/utils.rs b/crates/forge/tests/cli/utils.rs index 12b0fe13470f..e8e79fa68872 100644 --- a/crates/forge/tests/cli/utils.rs +++ b/crates/forge/tests/cli/utils.rs @@ -1,7 +1,8 @@ //! Various helper functions +use alloy_signer_wallet::LocalWallet; use ethers_core::types::{Address, Chain}; -use ethers_signers::{LocalWallet, Signer}; +use foundry_common::types::ToEthers; /// Returns the current millis since unix epoch. /// @@ -46,7 +47,7 @@ pub struct EnvExternalities { impl EnvExternalities { pub fn address(&self) -> Option

{ let pk: LocalWallet = self.pk.parse().ok()?; - Some(pk.address()) + Some(pk.address().to_ethers()) } pub fn goerli() -> Option { diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 49cbce5db7f2..efbd95d7103e 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -255,14 +255,14 @@ async fn test_invariant_shrink() { let create_fren_sequence = sequence[0].clone(); assert_eq!( create_fren_sequence.contract_name.unwrap(), - "fuzz/invariant/common/InvariantInnerContract.t.sol:Jesus" + "default/fuzz/invariant/common/InvariantInnerContract.t.sol:Jesus" ); assert_eq!(create_fren_sequence.signature.unwrap(), "create_fren()"); let betray_sequence = sequence[1].clone(); assert_eq!( betray_sequence.contract_name.unwrap(), - "fuzz/invariant/common/InvariantInnerContract.t.sol:Judas" + "default/fuzz/invariant/common/InvariantInnerContract.t.sol:Judas" ); assert_eq!(betray_sequence.signature.unwrap(), "betray()"); } diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 02f1aa15eea2..b1f8efc49162 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -4,10 +4,10 @@ use crate::{ config::*, test_helpers::{ForgeTestData, TEST_DATA_DEFAULT}, }; -use alloy_primitives::{address, Address}; -use ethers_core::abi::{Event, EventParam, Log, LogParam, ParamType, RawLog, Token}; +use alloy_dyn_abi::{DecodedEvent, DynSolValue, EventExt}; +use alloy_json_abi::Event; +use alloy_primitives::{address, Address, U256}; use forge::result::TestStatus; -use foundry_common::types::ToEthers; use foundry_config::{fs_permissions::PathPermission, FsPermissions}; use foundry_evm::{ constants::HARDHAT_CONSOLE_ADDRESS, @@ -125,25 +125,15 @@ test_repro!(3347, false, None, |res| { let mut res = res.remove("default/repros/Issue3347.t.sol:Issue3347Test").unwrap(); let test = res.test_results.remove("test()").unwrap(); assert_eq!(test.logs.len(), 1); - let event = Event { - name: "log2".to_string(), - inputs: vec![ - EventParam { name: "x".to_string(), kind: ParamType::Uint(256), indexed: false }, - EventParam { name: "y".to_string(), kind: ParamType::Uint(256), indexed: false }, - ], - anonymous: false, - }; - let raw_log = RawLog { - topics: test.logs[0].data.topics().iter().map(|t| t.to_ethers()).collect(), - data: test.logs[0].data.data.clone().to_vec(), - }; - let log = event.parse_log(raw_log).unwrap(); + let event = Event::parse("event log2(uint256, uint256)").unwrap(); + let decoded = event.decode_log(&test.logs[0].data, false).unwrap(); assert_eq!( - log, - Log { - params: vec![ - LogParam { name: "x".to_string(), value: Token::Uint(1u64.into()) }, - LogParam { name: "y".to_string(), value: Token::Uint(2u64.into()) } + decoded, + DecodedEvent { + indexed: vec![], + body: vec![ + DynSolValue::Uint(U256::from(1), 256), + DynSolValue::Uint(U256::from(2), 256) ] } ); diff --git a/crates/script/Cargo.toml b/crates/script/Cargo.toml index 5ae57482200e..e62e054bfadb 100644 --- a/crates/script/Cargo.toml +++ b/crates/script/Cargo.toml @@ -32,19 +32,24 @@ clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } semver = "1" futures = "0.3" async-recursion = "1.0.5" -alloy-primitives.workspace = true -alloy-dyn-abi.workspace = true + itertools.workspace = true parking_lot = "0.12" yansi = "0.5" -ethers-core.workspace = true -ethers-providers.workspace = true -ethers-signers.workspace = true revm-inspectors.workspace = true alloy-rpc-types.workspace = true alloy-json-abi.workspace = true dialoguer = { version = "0.11", default-features = false } indicatif = "0.17" +alloy-signer.workspace = true +alloy-network.workspace = true +alloy-provider.workspace = true +alloy-chains.workspace = true +alloy-dyn-abi.workspace = true +alloy-primitives.workspace = true +alloy-eips.workspace = true +alloy-transport.workspace = true + [dev-dependencies] tempfile = "3" \ No newline at end of file diff --git a/crates/script/src/broadcast.rs b/crates/script/src/broadcast.rs index 983c02374fb6..0cb5b0c9c529 100644 --- a/crates/script/src/broadcast.rs +++ b/crates/script/src/broadcast.rs @@ -1,13 +1,15 @@ +use super::receipts; use crate::{ build::LinkedBuildData, sequence::ScriptSequenceKind, verify::BroadcastedState, ScriptArgs, ScriptConfig, }; - -use super::receipts; -use alloy_primitives::{utils::format_units, Address, TxHash, U256}; -use ethers_core::types::{transaction::eip2718::TypedTransaction, BlockId}; -use ethers_providers::{JsonRpcClient, Middleware, Provider}; -use ethers_signers::Signer; +use alloy_chains::Chain; +use alloy_eips::eip2718::Encodable2718; +use alloy_network::{AnyNetwork, EthereumSigner, TransactionBuilder}; +use alloy_primitives::{utils::format_units, Address, TxHash}; +use alloy_provider::{utils::Eip1559Estimation, Provider}; +use alloy_rpc_types::{TransactionRequest, WithOtherFields}; +use alloy_transport::Transport; use eyre::{bail, Context, Result}; use forge_verify::provider::VerificationProviderType; use foundry_cheatcodes::ScriptWallets; @@ -16,14 +18,12 @@ use foundry_cli::{ utils::{has_batch_support, has_different_gas_calc}, }; use foundry_common::{ - provider::ethers::{ + provider::alloy::{ estimate_eip1559_fees, get_http_provider, try_get_http_provider, RetryProvider, }, shell, - types::{ToAlloy, ToEthers}, }; use foundry_config::Config; -use foundry_wallets::WalletSigner; use futures::{future::join_all, StreamExt}; use itertools::Itertools; use std::{ @@ -31,56 +31,49 @@ use std::{ sync::Arc, }; -pub async fn estimate_gas( - tx: &mut TypedTransaction, - provider: &Provider, +pub async fn estimate_gas( + tx: &mut WithOtherFields, + provider: &P, estimate_multiplier: u64, ) -> Result<()> where - T: JsonRpcClient, + P: Provider, + T: Transport + Clone, { // if already set, some RPC endpoints might simply return the gas value that is already // set in the request and omit the estimate altogether, so we remove it here - let _ = tx.gas_mut().take(); - - tx.set_gas( - provider - .estimate_gas(tx, None) - .await - .wrap_err_with(|| format!("Failed to estimate gas for tx: {:?}", tx.sighash()))? * - estimate_multiplier / + tx.gas = None; + + tx.set_gas_limit( + provider.estimate_gas(tx, None).await.wrap_err("Failed to estimate gas for tx")? * + estimate_multiplier as u128 / 100, ); Ok(()) } -pub async fn next_nonce( - caller: Address, - provider_url: &str, - block: Option, -) -> eyre::Result { - let provider = Provider::try_from(provider_url) +pub async fn next_nonce(caller: Address, provider_url: &str) -> eyre::Result { + let provider = try_get_http_provider(provider_url) .wrap_err_with(|| format!("bad fork_url provider: {provider_url}"))?; - let res = provider.get_transaction_count(caller.to_ethers(), block).await?.to_alloy(); - res.try_into().map_err(Into::into) + Ok(provider.get_transaction_count(caller, None).await?) } pub async fn send_transaction( provider: Arc, - mut tx: TypedTransaction, + mut tx: WithOtherFields, kind: SendTransactionKind<'_>, sequential_broadcast: bool, is_fixed_gas_limit: bool, estimate_via_rpc: bool, estimate_multiplier: u64, ) -> Result { - let from = tx.from().expect("no sender"); + let from = tx.from.expect("no sender"); if sequential_broadcast { - let nonce = provider.get_transaction_count(*from, None).await?; + let nonce = provider.get_transaction_count(from, None).await?; - let tx_nonce = tx.nonce().expect("no nonce"); - if nonce != *tx_nonce { + let tx_nonce = tx.nonce.expect("no nonce"); + if nonce != tx_nonce { bail!("EOA nonce changed unexpectedly while sending transactions. Expected {tx_nonce} got {nonce} from provider.") } } @@ -96,29 +89,26 @@ pub async fn send_transaction( debug!("sending transaction from unlocked account {:?}: {:?}", addr, tx); // Submit the transaction - provider.send_transaction(tx, None).await? + provider.send_transaction(tx).await? } SendTransactionKind::Raw(signer) => { debug!("sending transaction: {:?}", tx); - // Signing manually so we skip `fill_transaction` and its `eth_createAccessList` - // request. - let signature = - signer.sign_transaction(&tx).await.wrap_err("Failed to sign transaction")?; + let signed = tx.build(signer).await?; // Submit the raw transaction - provider.send_raw_transaction(tx.rlp_signed(&signature)).await? + provider.send_raw_transaction(signed.encoded_2718().as_ref()).await? } }; - Ok(pending.tx_hash().to_alloy()) + Ok(*pending.tx_hash()) } /// How to send a single transaction #[derive(Clone)] pub enum SendTransactionKind<'a> { Unlocked(Address), - Raw(&'a WalletSigner), + Raw(&'a EthereumSigner), } /// Represents how to send _all_ transactions @@ -126,7 +116,7 @@ pub enum SendTransactionsKind { /// Send via `eth_sendTransaction` and rely on the `from` address being unlocked. Unlocked(HashSet
), /// Send a signed transaction via `eth_sendRawTransaction` - Raw(HashMap), + Raw(HashMap), } impl SendTransactionsKind { @@ -202,8 +192,8 @@ impl BundledState { .iter() .flat_map(|sequence| { sequence - .typed_transactions() - .map(|tx| (*tx.from().expect("No sender for onchain transaction!")).to_alloy()) + .transactions() + .map(|tx| (tx.from().expect("No sender for onchain transaction!"))) }) .collect::>(); @@ -233,6 +223,11 @@ impl BundledState { ); } + let signers = signers + .into_iter() + .map(|(addr, signer)| (addr, EthereumSigner::new(signer))) + .collect(); + SendTransactionsKind::Raw(signers) }; @@ -243,23 +238,37 @@ impl BundledState { let already_broadcasted = sequence.receipts.len(); if already_broadcasted < sequence.transactions.len() { + let is_legacy = Chain::from(sequence.chain).is_legacy() || self.args.legacy; // Make a one-time gas price estimation - let (gas_price, eip1559_fees) = match self.args.with_gas_price { - None => match sequence.transactions.front().unwrap().typed_tx() { - TypedTransaction::Eip1559(_) => { - let mut fees = estimate_eip1559_fees(&provider, Some(sequence.chain)) + let (gas_price, eip1559_fees) = match ( + is_legacy, + self.args.with_gas_price, + self.args.priority_gas_price, + ) { + (true, Some(gas_price), _) => (Some(gas_price.to()), None), + (true, None, _) => (Some(provider.get_gas_price().await?), None), + (false, Some(max_fee_per_gas), Some(max_priority_fee_per_gas)) => ( + None, + Some(Eip1559Estimation { + max_fee_per_gas: max_fee_per_gas.to(), + max_priority_fee_per_gas: max_priority_fee_per_gas.to(), + }), + ), + (false, _, _) => { + let mut fees = estimate_eip1559_fees(&provider, Some(sequence.chain)) .await - .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; + .wrap_err("Failed to estimate EIP1559 fees. This chain might not support EIP1559, try adding --legacy to your command.")?; - if let Some(priority_gas_price) = self.args.priority_gas_price { - fees.1 = priority_gas_price.to_ethers(); - } + if let Some(gas_price) = self.args.with_gas_price { + fees.max_fee_per_gas = gas_price.to(); + } - (None, Some(fees)) + if let Some(priority_gas_price) = self.args.priority_gas_price { + fees.max_priority_fee_per_gas = priority_gas_price.to(); } - _ => (provider.get_gas_price().await.ok(), None), - }, - Some(gas_price) => (Some(gas_price.to_ethers()), None), + + (None, Some(fees)) + } }; // Iterate through transactions, matching the `from` field with the associated @@ -269,35 +278,21 @@ impl BundledState { .iter() .skip(already_broadcasted) .map(|tx_with_metadata| { - let tx = tx_with_metadata.typed_tx(); - let from = - (*tx.from().expect("No sender for onchain transaction!")).to_alloy(); + let tx = tx_with_metadata.tx(); + let from = tx.from().expect("No sender for onchain transaction!"); let kind = send_kind.for_sender(&from)?; let is_fixed_gas_limit = tx_with_metadata.is_fixed_gas_limit; let mut tx = tx.clone(); - tx.set_chain_id(sequence.chain); if let Some(gas_price) = gas_price { tx.set_gas_price(gas_price); } else { let eip1559_fees = eip1559_fees.expect("was set above"); - // fill gas price - match tx { - TypedTransaction::Eip1559(ref mut inner) => { - inner.max_priority_fee_per_gas = Some(eip1559_fees.1); - inner.max_fee_per_gas = Some(eip1559_fees.0); - } - _ => { - // If we're here, it means that first transaction of the - // sequence was EIP1559 transaction (see match statement above), - // however, we can only have transactions of the same type in - // the sequence. - unreachable!() - } - } + tx.set_max_priority_fee_per_gas(eip1559_fees.max_priority_fee_per_gas); + tx.set_max_fee_per_gas(eip1559_fees.max_fee_per_gas); } Ok((tx, kind, is_fixed_gas_limit)) @@ -375,18 +370,15 @@ impl BundledState { shell::println("\n\n==========================")?; shell::println("\nONCHAIN EXECUTION COMPLETE & SUCCESSFUL.")?; - let (total_gas, total_gas_price, total_paid) = sequence.receipts.iter().fold( - (U256::ZERO, U256::ZERO, U256::ZERO), - |acc, receipt| { - let gas_used = receipt.gas_used.unwrap_or_default().to_alloy(); - let gas_price = receipt.effective_gas_price.unwrap_or_default().to_alloy(); + let (total_gas, total_gas_price, total_paid) = + sequence.receipts.iter().fold((0, 0, 0), |acc, receipt| { + let gas_used = receipt.gas_used; + let gas_price = receipt.effective_gas_price; (acc.0 + gas_used, acc.1 + gas_price, acc.2 + gas_used * gas_price) - }, - ); + }); let paid = format_units(total_paid, 18).unwrap_or_else(|_| "N/A".to_string()); - let avg_gas_price = - format_units(total_gas_price / U256::from(sequence.receipts.len()), 9) - .unwrap_or_else(|_| "N/A".to_string()); + let avg_gas_price = format_units(total_gas_price / sequence.receipts.len() as u128, 9) + .unwrap_or_else(|_| "N/A".to_string()); shell::println(format!( "Total Paid: {} ETH ({} gas * avg {} gwei)", diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index ab17a95a9b93..51fde33adafd 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -7,14 +7,13 @@ use crate::{ }; use alloy_primitives::{Address, Bytes}; -use ethers_providers::Middleware; +use alloy_provider::Provider; use eyre::{Context, OptionExt, Result}; use foundry_cheatcodes::ScriptWallets; use foundry_cli::utils::get_cached_entry_by_name; use foundry_common::{ compile::{self, ContractSources, ProjectCompiler}, - provider::ethers::try_get_http_provider, - types::ToAlloy, + provider::alloy::try_get_http_provider, ContractsByArtifact, }; use foundry_compilers::{ @@ -290,7 +289,7 @@ impl CompiledState { } else { let fork_url = self.script_config.evm_opts.fork_url.clone().ok_or_eyre("Missing --fork-url field, if you were trying to broadcast a multi-chain sequence, please use --multi flag")?; let provider = Arc::new(try_get_http_provider(fork_url)?); - Some(provider.get_chainid().await?.as_u64()) + Some(provider.get_chain_id().await?) }; let sequence = match self.try_load_sequence(chain, false) { @@ -318,7 +317,7 @@ impl CompiledState { s.transactions .iter() .skip(s.receipts.len()) - .map(|t| t.transaction.from().expect("from is missing in script artifact")) + .map(|t| t.transaction.from.expect("from is missing in script artifact")) }); let available_signers = self @@ -326,7 +325,7 @@ impl CompiledState { .signers() .map_err(|e| eyre::eyre!("Failed to get available signers: {}", e))?; - if !froms.all(|from| available_signers.contains(&from.to_alloy())) { + if !froms.all(|from| available_signers.contains(&from)) { // IF we are missing required signers, execute script as we might need to collect // private keys from the execution. let executed = self.link()?.prepare_execution().await?.execute().await?; diff --git a/crates/script/src/execute.rs b/crates/script/src/execute.rs index 7b811a67cd91..03ac8bc9e44d 100644 --- a/crates/script/src/execute.rs +++ b/crates/script/src/execute.rs @@ -7,16 +7,16 @@ use crate::{ use super::{runner::ScriptRunner, JsonResult, NestedValue, ScriptResult}; use alloy_dyn_abi::FunctionExt; use alloy_json_abi::{Function, InternalType, JsonAbi}; -use alloy_primitives::{Address, Bytes, U64}; +use alloy_primitives::{Address, Bytes}; +use alloy_provider::Provider; use alloy_rpc_types::request::TransactionRequest; use async_recursion::async_recursion; -use ethers_providers::Middleware; use eyre::Result; use foundry_cheatcodes::ScriptWallets; use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; use foundry_common::{ fmt::{format_token, format_token_raw}, - provider::ethers::{get_http_provider, RpcUrl}, + provider::alloy::{get_http_provider, RpcUrl}, shell, ContractsByArtifact, }; use foundry_compilers::artifacts::ContractBytecodeSome; @@ -135,7 +135,7 @@ impl PreExecutionState { transaction: TransactionRequest { from: Some(self.script_config.evm_opts.sender), input: Some(bytes.clone()).into(), - nonce: Some(U64::from(self.script_config.sender_nonce + i as u64)), + nonce: Some(self.script_config.sender_nonce + i as u64), ..Default::default() }, }) @@ -253,9 +253,8 @@ impl RpcData { async fn check_shanghai_support(&self) -> Result<()> { let chain_ids = self.total_rpcs.iter().map(|rpc| async move { let provider = get_http_provider(rpc); - let id = provider.get_chainid().await.ok()?; - let id_u64: u64 = id.try_into().ok()?; - NamedChain::try_from(id_u64).ok() + let id = provider.get_chain_id().await.ok()?; + NamedChain::try_from(id).ok() }); let chains = join_all(chain_ids).await; diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 530c84d36c7b..8973dc1260c3 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -7,11 +7,11 @@ use self::transaction::AdditionalContract; use crate::runner::ScriptRunner; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes, Log, U256}; +use alloy_signer::Signer; use broadcast::next_nonce; use build::PreprocessedState; use clap::{Parser, ValueHint}; use dialoguer::Confirm; -use ethers_signers::Signer; use eyre::{ContextCompat, Result}; use forge_verify::RetryArgs; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; @@ -20,10 +20,8 @@ use foundry_common::{ compile::SkipBuildFilter, errors::UnlinkedByteCode, evm::{Breakpoints, EvmArgs}, - provider::ethers::RpcUrl, - shell, - types::ToAlloy, - CONTRACT_MAX_SIZE, SELECTOR_LEN, + provider::alloy::RpcUrl, + shell, CONTRACT_MAX_SIZE, SELECTOR_LEN, }; use foundry_compilers::{artifacts::ContractBytecodeSome, ArtifactId}; use foundry_config::{ @@ -305,7 +303,7 @@ impl ScriptArgs { .wallets .private_keys()? .filter(|pks| pks.len() == 1) - .map(|pks| pks.first().unwrap().address().to_alloy()); + .map(|pks| pks.first().unwrap().address()); Ok(maybe_sender) } @@ -531,7 +529,7 @@ pub struct ScriptConfig { impl ScriptConfig { pub async fn new(config: Config, evm_opts: EvmOpts) -> Result { let sender_nonce = if let Some(fork_url) = evm_opts.fork_url.as_ref() { - next_nonce(evm_opts.sender, fork_url, None).await? + next_nonce(evm_opts.sender, fork_url).await? } else { // dapptools compatibility 1 @@ -541,7 +539,7 @@ impl ScriptConfig { pub async fn update_sender(&mut self, sender: Address) -> Result<()> { self.sender_nonce = if let Some(fork_url) = self.evm_opts.fork_url.as_ref() { - next_nonce(sender, fork_url, None).await? + next_nonce(sender, fork_url).await? } else { // dapptools compatibility 1 diff --git a/crates/script/src/providers.rs b/crates/script/src/providers.rs index f29a72629320..ab2ee99112f9 100644 --- a/crates/script/src/providers.rs +++ b/crates/script/src/providers.rs @@ -1,11 +1,6 @@ -use alloy_primitives::U256; -use ethers_providers::{Middleware, Provider}; +use alloy_provider::{utils::Eip1559Estimation, Provider}; use eyre::{Result, WrapErr}; -use foundry_common::{ - provider::ethers::{get_http_provider, RpcUrl}, - runtime_client::RuntimeClient, - types::ToAlloy, -}; +use foundry_common::provider::alloy::{get_http_provider, RetryProvider, RpcUrl}; use foundry_config::Chain; use std::{ collections::{hash_map::Entry, HashMap}, @@ -47,23 +42,22 @@ impl Deref for ProvidersManager { /// Holds related metadata to each provider RPC. #[derive(Debug)] pub struct ProviderInfo { - pub provider: Arc>, + pub provider: Arc, pub chain: u64, pub gas_price: GasPrice, - pub is_legacy: bool, } /// Represents the outcome of a gas price request #[derive(Debug)] pub enum GasPrice { - Legacy(Result), - EIP1559(Result<(U256, U256)>), + Legacy(Result), + EIP1559(Result), } impl ProviderInfo { pub async fn new(rpc: &str, mut is_legacy: bool) -> Result { let provider = Arc::new(get_http_provider(rpc)); - let chain = provider.get_chainid().await?.as_u64(); + let chain = provider.get_chain_id().await?; if let Some(chain) = Chain::from(chain).named() { is_legacy |= chain.is_legacy(); @@ -71,30 +65,22 @@ impl ProviderInfo { let gas_price = if is_legacy { GasPrice::Legacy( - provider - .get_gas_price() - .await - .wrap_err("Failed to get legacy gas price") - .map(|p| p.to_alloy()), + provider.get_gas_price().await.wrap_err("Failed to get legacy gas price"), ) } else { GasPrice::EIP1559( - provider - .estimate_eip1559_fees(None) - .await - .wrap_err("Failed to get EIP-1559 fees") - .map(|p| (p.0.to_alloy(), p.1.to_alloy())), + provider.estimate_eip1559_fees(None).await.wrap_err("Failed to get EIP-1559 fees"), ) }; - Ok(ProviderInfo { provider, chain, gas_price, is_legacy }) + Ok(ProviderInfo { provider, chain, gas_price }) } /// Returns the gas price to use - pub fn gas_price(&self) -> Result { + pub fn gas_price(&self) -> Result { let res = match &self.gas_price { GasPrice::Legacy(res) => res.as_ref(), - GasPrice::EIP1559(res) => res.as_ref().map(|res| &res.0), + GasPrice::EIP1559(res) => res.as_ref().map(|res| &res.max_fee_per_gas), }; match res { Ok(val) => Ok(*val), diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index 29848aad3564..3bdc228f0575 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -1,27 +1,24 @@ use super::sequence::ScriptSequence; -use alloy_primitives::TxHash; -use ethers_core::types::TransactionReceipt; -use ethers_providers::{Middleware, PendingTransaction}; +use alloy_chains::Chain; +use alloy_primitives::{utils::format_units, TxHash, U256}; +use alloy_provider::{PendingTransactionBuilder, Provider}; +use alloy_rpc_types::AnyTransactionReceipt; use eyre::Result; -use foundry_cli::{init_progress, update_progress, utils::print_receipt}; -use foundry_common::{ - provider::ethers::RetryProvider, - types::{ToAlloy, ToEthers}, -}; +use foundry_cli::{init_progress, update_progress}; +use foundry_common::provider::alloy::RetryProvider; use futures::StreamExt; use std::sync::Arc; /// Convenience enum for internal signalling of transaction status enum TxStatus { Dropped, - Success(TransactionReceipt), - Revert(TransactionReceipt), + Success(AnyTransactionReceipt), + Revert(AnyTransactionReceipt), } -impl From for TxStatus { - fn from(receipt: TransactionReceipt) -> Self { - let status = receipt.status.expect("receipt is from an ancient, pre-EIP658 block"); - if status.is_zero() { +impl From for TxStatus { + fn from(receipt: AnyTransactionReceipt) -> Self { + if !receipt.inner.inner.inner.receipt.status { TxStatus::Revert(receipt) } else { TxStatus::Success(receipt) @@ -68,7 +65,7 @@ pub async fn clear_pendings( let mut tasks = futures::stream::iter(futs).buffer_unordered(10); let mut errors: Vec = vec![]; - let mut receipts = Vec::::with_capacity(count); + let mut receipts = Vec::::with_capacity(count); // set up progress bar let mut pos = 0; @@ -87,7 +84,7 @@ pub async fn clear_pendings( } Ok(TxStatus::Success(receipt)) => { trace!(tx_hash=?tx_hash, "received tx receipt"); - deployment_sequence.remove_pending(receipt.transaction_hash.to_alloy()); + deployment_sequence.remove_pending(receipt.transaction_hash); receipts.push(receipt); } Ok(TxStatus::Revert(receipt)) => { @@ -95,7 +92,7 @@ pub async fn clear_pendings( // if this is not removed from pending, then the script becomes // un-resumable. Is this desirable on reverts? warn!(tx_hash=?tx_hash, "Transaction Failure"); - deployment_sequence.remove_pending(receipt.transaction_hash.to_alloy()); + deployment_sequence.remove_pending(receipt.transaction_hash); errors.push(format!("Transaction Failure: {:?}", receipt.transaction_hash)); } } @@ -105,7 +102,7 @@ pub async fn clear_pendings( } // sort receipts by blocks asc and index - receipts.sort_unstable(); + receipts.sort_by_key(|r| (r.block_number, r.transaction_index)); // print all receipts for receipt in receipts { @@ -136,20 +133,53 @@ async fn check_tx_status( // still neatly return the tuple let result = async move { // First check if there's a receipt - let receipt_opt = provider.get_transaction_receipt(hash.to_ethers()).await?; + let receipt_opt = provider.get_transaction_receipt(hash).await?; if let Some(receipt) = receipt_opt { return Ok(receipt.into()); } // If the tx is present in the mempool, run the pending tx future, and // assume the next drop is really really real - let pending_res = PendingTransaction::new(hash.to_ethers(), provider).await?; - match pending_res { - Some(receipt) => Ok(receipt.into()), - None => Ok(TxStatus::Dropped), - } + Ok(PendingTransactionBuilder::new(provider, hash) + .get_receipt() + .await + .map_or(TxStatus::Dropped, |r| r.into())) } .await; (hash, result) } + +/// Prints parts of the receipt to stdout +pub fn print_receipt(chain: Chain, receipt: &AnyTransactionReceipt) { + let gas_used = receipt.gas_used; + let gas_price = receipt.effective_gas_price; + foundry_common::shell::println(format!( + "\n##### {chain}\n{status}Hash: {tx_hash:?}{caddr}\nBlock: {bn}\n{gas}\n", + status = if !receipt.inner.inner.inner.receipt.status { + "❌ [Failed]" + } else { + "✅ [Success]" + }, + tx_hash = receipt.transaction_hash, + caddr = if let Some(addr) = &receipt.contract_address { + format!("\nContract Address: {}", addr.to_checksum(None)) + } else { + String::new() + }, + bn = receipt.block_number.unwrap_or_default(), + gas = if gas_price == 0 { + format!("Gas Used: {gas_used}") + } else { + let paid = format_units(gas_used.saturating_mul(gas_price), 18) + .unwrap_or_else(|_| "N/A".into()); + let gas_price = format_units(U256::from(gas_price), 9).unwrap_or_else(|_| "N/A".into()); + format!( + "Paid: {} ETH ({gas_used} gas * {} gwei)", + paid.trim_end_matches('0'), + gas_price.trim_end_matches('0').trim_end_matches('.') + ) + }, + )) + .expect("could not print receipt"); +} diff --git a/crates/script/src/sequence.rs b/crates/script/src/sequence.rs index 9fdee7b021bf..2d39c12923db 100644 --- a/crates/script/src/sequence.rs +++ b/crates/script/src/sequence.rs @@ -1,18 +1,14 @@ use super::{multi_sequence::MultiChainSequence, NestedValue}; use crate::{ - transaction::{wrapper, AdditionalContract, TransactionWithMetadata}, + transaction::{AdditionalContract, TransactionWithMetadata}, verify::VerifyBundle, }; use alloy_primitives::{Address, TxHash}; -use ethers_core::types::{transaction::eip2718::TypedTransaction, TransactionReceipt}; +use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest, WithOtherFields}; use eyre::{ContextCompat, Result, WrapErr}; use forge_verify::provider::VerificationProviderType; use foundry_cli::utils::{now, Git}; -use foundry_common::{ - fs, shell, - types::{ToAlloy, ToEthers}, - SELECTOR_LEN, -}; +use foundry_common::{fs, shell, SELECTOR_LEN}; use foundry_compilers::ArtifactId; use foundry_config::Config; use serde::{Deserialize, Serialize}; @@ -91,8 +87,7 @@ pub const DRY_RUN_DIR: &str = "dry-run"; #[derive(Clone, Default, Serialize, Deserialize)] pub struct ScriptSequence { pub transactions: VecDeque, - #[serde(serialize_with = "wrapper::serialize_receipts")] - pub receipts: Vec, + pub receipts: Vec, pub libraries: Vec, pub pending: Vec, #[serde(skip)] @@ -201,13 +196,13 @@ impl ScriptSequence { Ok(()) } - pub fn add_receipt(&mut self, receipt: TransactionReceipt) { + pub fn add_receipt(&mut self, receipt: AnyTransactionReceipt) { self.receipts.push(receipt); } /// Sorts all receipts with ascending transaction index pub fn sort_receipts(&mut self) { - self.receipts.sort_unstable() + self.receipts.sort_by_key(|r| (r.block_number, r.transaction_index)); } pub fn add_pending(&mut self, index: usize, tx_hash: TxHash) { @@ -284,13 +279,13 @@ impl ScriptSequence { let mut offset = 0; if tx.is_create2() { - receipt.contract_address = tx.contract_address.map(|a| a.to_ethers()); + receipt.contract_address = tx.contract_address; offset = 32; } // Verify contract created directly from the transaction if let (Some(address), Some(data)) = - (receipt.contract_address.map(|h| h.to_alloy()), tx.typed_tx().data()) + (receipt.contract_address, tx.tx().input.input()) { match verify.get_verify_args(address, offset, &data.0, &self.libraries) { Some(verify) => future_verifications.push(verify.run()), @@ -300,7 +295,7 @@ impl ScriptSequence { // Verify potential contracts created during the transaction execution for AdditionalContract { address, init_code, .. } in &tx.additional_contracts { - match verify.get_verify_args(*address, 0, init_code, &self.libraries) { + match verify.get_verify_args(*address, 0, init_code.as_ref(), &self.libraries) { Some(verify) => future_verifications.push(verify.run()), None => unverifiable_contracts.push(*address), }; @@ -357,8 +352,8 @@ impl ScriptSequence { } /// Returns the list of the transactions without the metadata. - pub fn typed_transactions(&self) -> impl Iterator { - self.transactions.iter().map(|tx| tx.typed_tx()) + pub fn transactions(&self) -> impl Iterator> { + self.transactions.iter().map(|tx| tx.tx()) } pub fn fill_sensitive(&mut self, sensitive: &SensitiveScriptSequence) { diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index 96b1eaefe2f0..c740e59a4735 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -13,13 +13,12 @@ use crate::{ sequence::get_commit_hash, ScriptArgs, ScriptConfig, ScriptResult, }; +use alloy_network::TransactionBuilder; use alloy_primitives::{utils::format_units, Address, U256}; use eyre::{Context, Result}; use foundry_cheatcodes::{BroadcastableTransactions, ScriptWallets}; use foundry_cli::utils::{has_different_gas_calc, now}; -use foundry_common::{ - get_contract_name, provider::ethers::RpcUrl, shell, types::ToAlloy, ContractsByArtifact, -}; +use foundry_common::{get_contract_name, provider::alloy::RpcUrl, shell, ContractsByArtifact}; use foundry_evm::traces::render_trace_arena; use futures::future::{join_all, try_join_all}; use parking_lot::RwLock; @@ -132,9 +131,8 @@ impl PreSimulationState { } // We inflate the gas used by the user specified percentage None => { - let gas = - U256::from(result.gas_used * self.args.gas_estimate_multiplier / 100); - tx.gas = Some(gas); + let gas = result.gas_used * self.args.gas_estimate_multiplier / 100; + tx.gas = Some(gas as u128); } } let tx = TransactionWithMetadata::new( @@ -266,7 +264,7 @@ impl FilledTransactionsState { eyre::bail!("Multi-chain deployment is not supported with libraries."); } - let mut total_gas_per_rpc: HashMap = HashMap::new(); + let mut total_gas_per_rpc: HashMap = HashMap::new(); // Batches sequence of transactions from different rpcs. let mut new_sequence = VecDeque::new(); @@ -282,15 +280,14 @@ impl FilledTransactionsState { let provider_info = manager.get_or_init_provider(&tx.rpc, self.args.legacy).await?; // Handles chain specific requirements. - tx.change_type(provider_info.is_legacy); tx.transaction.set_chain_id(provider_info.chain); if !self.args.skip_simulation { - let typed_tx = tx.typed_tx_mut(); + let tx = tx.tx_mut(); if has_different_gas_calc(provider_info.chain) { trace!("estimating with different gas calculation"); - let gas = *typed_tx.gas().expect("gas is set by simulation."); + let gas = tx.gas.expect("gas is set by simulation."); // We are trying to show the user an estimation of the total gas usage. // @@ -303,22 +300,19 @@ impl FilledTransactionsState { // for chains where `has_different_gas_calc` returns true, // we await each transaction before broadcasting the next // one. - if let Err(err) = estimate_gas( - typed_tx, - &provider_info.provider, - self.args.gas_estimate_multiplier, - ) - .await + if let Err(err) = + estimate_gas(tx, &provider_info.provider, self.args.gas_estimate_multiplier) + .await { trace!("gas estimation failed: {err}"); // Restore gas value, since `estimate_gas` will remove it. - typed_tx.set_gas(gas); + tx.set_gas_limit(gas); } } - let total_gas = total_gas_per_rpc.entry(tx_rpc.clone()).or_insert(U256::ZERO); - *total_gas += (*typed_tx.gas().expect("gas is set")).to_alloy(); + let total_gas = total_gas_per_rpc.entry(tx_rpc.clone()).or_insert(0); + *total_gas += tx.gas.expect("gas is set"); } new_sequence.push_back(tx); @@ -346,7 +340,7 @@ impl FilledTransactionsState { // We don't store it in the transactions, since we want the most updated value. // Right before broadcasting. let per_gas = if let Some(gas_price) = self.args.with_gas_price { - gas_price + gas_price.to() } else { provider_info.gas_price()? }; diff --git a/crates/script/src/transaction.rs b/crates/script/src/transaction.rs index 80a2814207aa..e74699e74913 100644 --- a/crates/script/src/transaction.rs +++ b/crates/script/src/transaction.rs @@ -1,18 +1,9 @@ use super::{artifacts::ArtifactInfo, ScriptResult}; use alloy_dyn_abi::JsonAbiExt; use alloy_primitives::{Address, Bytes, B256}; -use alloy_rpc_types::request::TransactionRequest; -use ethers_core::types::{ - transaction::eip2718::TypedTransaction, NameOrAddress, - TransactionRequest as EthersTransactionRequest, -}; +use alloy_rpc_types::{request::TransactionRequest, WithOtherFields}; use eyre::{ContextCompat, Result, WrapErr}; -use foundry_common::{ - fmt::format_token_raw, - provider::ethers::RpcUrl, - types::{ToAlloy, ToEthers}, - SELECTOR_LEN, -}; +use foundry_common::{fmt::format_token_raw, provider::alloy::RpcUrl, SELECTOR_LEN}; use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, traces::CallTraceDecoder}; use itertools::Itertools; use revm_inspectors::tracing::types::CallKind; @@ -24,7 +15,6 @@ use std::collections::BTreeMap; pub struct AdditionalContract { #[serde(rename = "transactionType")] pub opcode: CallKind, - #[serde(serialize_with = "wrapper::serialize_addr")] pub address: Address, pub init_code: Bytes, } @@ -37,7 +27,7 @@ pub struct TransactionWithMetadata { pub opcode: CallKind, #[serde(default = "default_string")] pub contract_name: Option, - #[serde(default = "default_address", serialize_with = "wrapper::serialize_opt_addr")] + #[serde(default = "default_address")] pub contract_address: Option
, #[serde(default = "default_string")] pub function: Option, @@ -45,7 +35,7 @@ pub struct TransactionWithMetadata { pub arguments: Option>, #[serde(skip)] pub rpc: RpcUrl, - pub transaction: TypedTransaction, + pub transaction: WithOtherFields, pub additional_contracts: Vec, pub is_fixed_gas_limit: bool, } @@ -64,18 +54,7 @@ fn default_vec_of_strings() -> Option> { impl TransactionWithMetadata { pub fn from_tx_request(transaction: TransactionRequest) -> Self { - Self { - transaction: TypedTransaction::Legacy(EthersTransactionRequest { - from: transaction.from.map(ToEthers::to_ethers), - to: transaction.to.map(ToEthers::to_ethers).map(Into::into), - value: transaction.value.map(ToEthers::to_ethers), - data: transaction.input.into_input().map(ToEthers::to_ethers), - nonce: transaction.nonce.map(|n| n.to::().into()), - gas: transaction.gas.map(ToEthers::to_ethers), - ..Default::default() - }), - ..Default::default() - } + Self { transaction: WithOtherFields::new(transaction), ..Default::default() } } pub fn new( @@ -92,8 +71,8 @@ impl TransactionWithMetadata { metadata.is_fixed_gas_limit = is_fixed_gas_limit; // Specify if any contract was directly created with this transaction - if let Some(NameOrAddress::Address(to)) = metadata.transaction.to().cloned() { - if to.to_alloy() == DEFAULT_CREATE2_DEPLOYER { + if let Some(to) = metadata.transaction.to { + if to == DEFAULT_CREATE2_DEPLOYER { metadata.set_create( true, Address::from_slice(&result.returned), @@ -101,10 +80,10 @@ impl TransactionWithMetadata { )?; } else { metadata - .set_call(to.to_alloy(), local_contracts, decoder) + .set_call(to, local_contracts, decoder) .wrap_err("Could not decode transaction type.")?; } - } else if metadata.transaction.to().is_none() { + } else { metadata.set_create( false, result.address.wrap_err("There should be a contract address from CREATE.")?, @@ -150,7 +129,7 @@ impl TransactionWithMetadata { self.contract_name = info.map(|info| info.contract_name.clone()); self.contract_address = Some(address); - let Some(data) = self.transaction.data() else { return Ok(()) }; + let Some(data) = self.transaction.input.input() else { return Ok(()) }; let Some(info) = info else { return Ok(()) }; // `create2` transactions are prefixed by a 32 byte salt. @@ -197,7 +176,7 @@ impl TransactionWithMetadata { self.opcode = CallKind::Call; self.contract_address = Some(target); - let Some(data) = self.transaction.data() else { return Ok(()) }; + let Some(data) = self.transaction.input.input() else { return Ok(()) }; if data.len() < SELECTOR_LEN { return Ok(()); } @@ -229,19 +208,11 @@ impl TransactionWithMetadata { Ok(()) } - pub fn change_type(&mut self, is_legacy: bool) { - self.transaction = if is_legacy { - TypedTransaction::Legacy(self.transaction.clone().into()) - } else { - TypedTransaction::Eip1559(self.transaction.clone().into()) - }; - } - - pub fn typed_tx(&self) -> &TypedTransaction { + pub fn tx(&self) -> &WithOtherFields { &self.transaction } - pub fn typed_tx_mut(&mut self) -> &mut TypedTransaction { + pub fn tx_mut(&mut self) -> &mut WithOtherFields { &mut self.transaction } @@ -249,208 +220,3 @@ impl TransactionWithMetadata { self.opcode == CallKind::Create2 } } - -// wrapper for modifying ethers-rs type serialization -pub mod wrapper { - pub use super::*; - use ethers_core::{ - types::{Bloom, Bytes, Log, TransactionReceipt, H256, U256, U64}, - utils::to_checksum, - }; - - pub fn serialize_addr(addr: &Address, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&to_checksum(&addr.to_ethers(), None)) - } - - pub fn serialize_opt_addr(opt: &Option
, serializer: S) -> Result - where - S: serde::Serializer, - { - match opt { - Some(addr) => serialize_addr(addr, serializer), - None => serializer.serialize_none(), - } - } - - pub fn serialize_vec_with_wrapped( - vec: &[T], - serializer: S, - ) -> Result - where - S: serde::Serializer, - T: Clone, - WrappedType: serde::Serialize + From, - { - serializer.collect_seq(vec.iter().cloned().map(WrappedType::from)) - } - - // copied from https://github.com/gakonst/ethers-rs - #[derive(Serialize, Deserialize)] - struct WrappedLog { - /// The contract address that emitted the log. - #[serde(serialize_with = "serialize_addr")] - pub address: Address, - - /// Array of 0 to 4 32 Bytes of indexed log arguments. - /// - /// (In solidity: The first topic is the hash of the signature of the event - /// (e.g. `Deposit(address,bytes32,uint256)`), except you declared the event - /// with the anonymous specifier.) - pub topics: Vec, - - /// Data - pub data: Bytes, - - /// Block Hash - #[serde(rename = "blockHash")] - #[serde(skip_serializing_if = "Option::is_none")] - pub block_hash: Option, - - /// Block Number - #[serde(rename = "blockNumber")] - #[serde(skip_serializing_if = "Option::is_none")] - pub block_number: Option, - - /// Transaction Hash - #[serde(rename = "transactionHash")] - #[serde(skip_serializing_if = "Option::is_none")] - pub transaction_hash: Option, - - /// Transaction Index - #[serde(rename = "transactionIndex")] - #[serde(skip_serializing_if = "Option::is_none")] - pub transaction_index: Option, - - /// Integer of the log index position in the block. None if it's a pending log. - #[serde(rename = "logIndex")] - #[serde(skip_serializing_if = "Option::is_none")] - pub log_index: Option, - - /// Integer of the transactions index position log was created from. - /// None when it's a pending log. - #[serde(rename = "transactionLogIndex")] - #[serde(skip_serializing_if = "Option::is_none")] - pub transaction_log_index: Option, - - /// Log Type - #[serde(rename = "logType")] - #[serde(skip_serializing_if = "Option::is_none")] - pub log_type: Option, - - /// True when the log was removed, due to a chain reorganization. - /// false if it's a valid log. - #[serde(skip_serializing_if = "Option::is_none")] - pub removed: Option, - } - impl From for WrappedLog { - fn from(log: Log) -> Self { - Self { - address: log.address.to_alloy(), - topics: log.topics, - data: log.data, - block_hash: log.block_hash, - block_number: log.block_number, - transaction_hash: log.transaction_hash, - transaction_index: log.transaction_index, - log_index: log.log_index, - transaction_log_index: log.transaction_log_index, - log_type: log.log_type, - removed: log.removed, - } - } - } - - fn serialize_logs( - logs: &[Log], - serializer: S, - ) -> Result { - serialize_vec_with_wrapped::(logs, serializer) - } - - // "Receipt" of an executed transaction: details of its execution. - // copied from https://github.com/gakonst/ethers-rs - #[derive(Clone, Default, Serialize, Deserialize)] - pub struct WrappedTransactionReceipt { - /// Transaction hash. - #[serde(rename = "transactionHash")] - pub transaction_hash: H256, - /// Index within the block. - #[serde(rename = "transactionIndex")] - pub transaction_index: U64, - /// Hash of the block this transaction was included within. - #[serde(rename = "blockHash")] - pub block_hash: Option, - /// Number of the block this transaction was included within. - #[serde(rename = "blockNumber")] - pub block_number: Option, - /// The address of the sender. - #[serde(serialize_with = "serialize_addr")] - pub from: Address, - // The address of the receiver. `None` when its a contract creation transaction. - #[serde(serialize_with = "serialize_opt_addr")] - pub to: Option
, - /// Cumulative gas used within the block after this was executed. - #[serde(rename = "cumulativeGasUsed")] - pub cumulative_gas_used: U256, - /// Gas used by this transaction alone. - /// - /// Gas used is `None` if the client is running in light client mode. - #[serde(rename = "gasUsed")] - pub gas_used: Option, - /// Contract address created, or `None` if not a deployment. - #[serde(rename = "contractAddress", serialize_with = "serialize_opt_addr")] - pub contract_address: Option
, - /// Logs generated within this transaction. - #[serde(serialize_with = "serialize_logs")] - pub logs: Vec, - /// Status: either 1 (success) or 0 (failure). Only present after activation of [EIP-658](https://eips.ethereum.org/EIPS/eip-658) - pub status: Option, - /// State root. Only present before activation of [EIP-658](https://eips.ethereum.org/EIPS/eip-658) - #[serde(default, skip_serializing_if = "Option::is_none")] - pub root: Option, - /// Logs bloom - #[serde(rename = "logsBloom")] - pub logs_bloom: Bloom, - /// Transaction type, Some(1) for AccessList transaction, None for Legacy - #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, - /// The price paid post-execution by the transaction (i.e. base fee + priority fee). - /// Both fields in 1559-style transactions are *maximums* (max fee + max priority fee), the - /// amount that's actually paid by users can only be determined post-execution - #[serde(rename = "effectiveGasPrice", default, skip_serializing_if = "Option::is_none")] - pub effective_gas_price: Option, - } - impl From for WrappedTransactionReceipt { - fn from(receipt: TransactionReceipt) -> Self { - Self { - transaction_hash: receipt.transaction_hash, - transaction_index: receipt.transaction_index, - block_hash: receipt.block_hash, - block_number: receipt.block_number, - from: receipt.from.to_alloy(), - to: receipt.to.map(|addr| addr.to_alloy()), - cumulative_gas_used: receipt.cumulative_gas_used, - gas_used: receipt.gas_used, - contract_address: receipt.contract_address.map(|addr| addr.to_alloy()), - logs: receipt.logs, - status: receipt.status, - root: receipt.root, - logs_bloom: receipt.logs_bloom, - transaction_type: receipt.transaction_type, - effective_gas_price: receipt.effective_gas_price, - } - } - } - - pub fn serialize_receipts( - receipts: &[TransactionReceipt], - serializer: S, - ) -> Result { - serialize_vec_with_wrapped::( - receipts, serializer, - ) - } -} diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 8e2b75931734..09025e27c6f3 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -17,9 +17,7 @@ foundry-compilers = { workspace = true, features = ["project-util"] } foundry-config.workspace = true alloy-primitives.workspace = true - -ethers-core.workspace = true -ethers-providers.workspace = true +alloy-provider.workspace = true eyre.workspace = true fd-lock = "4.0.0" diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index b60723d9d8d2..fecbc9f5f07f 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -1,12 +1,8 @@ use crate::{init_tracing, TestCommand}; -use alloy_primitives::{Address, U256}; -use ethers_core::types::NameOrAddress; -use ethers_providers::Middleware; +use alloy_primitives::Address; +use alloy_provider::Provider; use eyre::Result; -use foundry_common::{ - provider::ethers::{get_http_provider, RetryProvider}, - types::{ToAlloy, ToEthers}, -}; +use foundry_common::provider::alloy::{get_http_provider, RetryProvider}; use std::{collections::BTreeMap, fs, path::Path, str::FromStr}; const BROADCAST_TEST_PATH: &str = "src/Broadcast.t.sol"; @@ -17,8 +13,8 @@ pub struct ScriptTester { pub accounts_pub: Vec
, pub accounts_priv: Vec, pub provider: Option, - pub nonces: BTreeMap, - pub address_nonces: BTreeMap, + pub nonces: BTreeMap, + pub address_nonces: BTreeMap, pub cmd: TestCommand, } @@ -121,13 +117,10 @@ impl ScriptTester { if let Some(provider) = &self.provider { let nonce = provider - .get_transaction_count( - NameOrAddress::Address(self.accounts_pub[index as usize].to_ethers()), - None, - ) + .get_transaction_count(self.accounts_pub[index as usize], None) .await .unwrap(); - self.nonces.insert(index, nonce.to_alloy()); + self.nonces.insert(index, nonce); } } self @@ -135,14 +128,9 @@ impl ScriptTester { pub async fn load_addresses(&mut self, addresses: &[Address]) -> &mut Self { for &address in addresses { - let nonce = self - .provider - .as_ref() - .unwrap() - .get_transaction_count(NameOrAddress::Address(address.to_ethers()), None) - .await - .unwrap(); - self.address_nonces.insert(address, nonce.to_alloy()); + let nonce = + self.provider.as_ref().unwrap().get_transaction_count(address, None).await.unwrap(); + self.address_nonces.insert(address, nonce); } self } @@ -181,18 +169,13 @@ impl ScriptTester { pub async fn assert_nonce_increment(&mut self, keys_indexes: &[(u32, u32)]) -> &mut Self { for &(private_key_slot, expected_increment) in keys_indexes { let addr = self.accounts_pub[private_key_slot as usize]; - let nonce = self - .provider - .as_ref() - .unwrap() - .get_transaction_count(NameOrAddress::Address(addr.to_ethers()), None) - .await - .unwrap(); + let nonce = + self.provider.as_ref().unwrap().get_transaction_count(addr, None).await.unwrap(); let prev_nonce = self.nonces.get(&private_key_slot).unwrap(); assert_eq!( nonce, - (prev_nonce + U256::from(expected_increment)).to_ethers(), + (*prev_nonce + expected_increment as u64), "nonce not incremented correctly for {addr}: \ {prev_nonce} + {expected_increment} != {nonce}" ); @@ -210,12 +193,12 @@ impl ScriptTester { .provider .as_ref() .unwrap() - .get_transaction_count(NameOrAddress::Address(address.to_ethers()), None) + .get_transaction_count(*address, None) .await .unwrap(); let prev_nonce = self.address_nonces.get(address).unwrap(); - assert_eq!(nonce, (prev_nonce + U256::from(*expected_increment)).to_ethers()); + assert_eq!(nonce, *prev_nonce + *expected_increment as u64); } self } diff --git a/crates/verify/Cargo.toml b/crates/verify/Cargo.toml index a735b39af57b..832cf1c6b39c 100644 --- a/crates/verify/Cargo.toml +++ b/crates/verify/Cargo.toml @@ -22,7 +22,7 @@ alloy-json-abi.workspace = true alloy-primitives.workspace = true serde.workspace = true eyre.workspace = true -ethers-providers.workspace = true +alloy-provider.workspace = true tracing.workspace = true foundry-compilers = { workspace = true, features = ["full"] } foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } diff --git a/crates/verify/src/etherscan/mod.rs b/crates/verify/src/etherscan/mod.rs index 5922f64add1a..ec218e714e90 100644 --- a/crates/verify/src/etherscan/mod.rs +++ b/crates/verify/src/etherscan/mod.rs @@ -1,7 +1,7 @@ use super::{provider::VerificationProvider, VerifyArgs, VerifyCheckArgs}; use crate::retry::RETRY_CHECK_ON_VERIFY; use alloy_json_abi::Function; -use ethers_providers::Middleware; +use alloy_provider::Provider; use eyre::{eyre, Context, OptionExt, Result}; use foundry_block_explorers::{ errors::EtherscanError, @@ -10,7 +10,7 @@ use foundry_block_explorers::{ Client, }; use foundry_cli::utils::{self, get_cached_entry_by_name, read_constructor_args_file, LoadConfig}; -use foundry_common::{abi::encode_function_args, retry::Retry, types::ToEthers}; +use foundry_common::{abi::encode_function_args, retry::Retry}; use foundry_compilers::{ artifacts::{BytecodeObject, CompactContract}, cache::CacheEntry, @@ -498,20 +498,17 @@ impl EtherscanVerificationProvider { )?; let creation_data = client.contract_creation_data(args.address).await?; - let transaction = provider - .get_transaction(creation_data.transaction_hash.to_ethers()) - .await? - .ok_or_eyre("Couldn't fetch transaction data from RPC")?; + let transaction = provider.get_transaction_by_hash(creation_data.transaction_hash).await?; let receipt = provider - .get_transaction_receipt(creation_data.transaction_hash.to_ethers()) + .get_transaction_receipt(creation_data.transaction_hash) .await? .ok_or_eyre("Couldn't fetch transaction receipt from RPC")?; let maybe_creation_code: &[u8]; - if receipt.contract_address == Some(args.address.to_ethers()) { + if receipt.contract_address == Some(args.address) { maybe_creation_code = &transaction.input; - } else if transaction.to == Some(DEFAULT_CREATE2_DEPLOYER.to_ethers()) { + } else if transaction.to == Some(DEFAULT_CREATE2_DEPLOYER) { maybe_creation_code = &transaction.input[32..]; } else { eyre::bail!("Fetching of constructor arguments is not supported for contracts created by contracts") diff --git a/crates/wallets/Cargo.toml b/crates/wallets/Cargo.toml index 1ef26972dbd4..31ea5607ce96 100644 --- a/crates/wallets/Cargo.toml +++ b/crates/wallets/Cargo.toml @@ -11,13 +11,18 @@ repository.workspace = true [dependencies] alloy-primitives.workspace = true - -ethers-core.workspace = true -ethers-providers.workspace = true -ethers-signers = { workspace = true, features = ["aws", "ledger", "trezor"] } - -rusoto_core = { version = "0.48", default-features = false } -rusoto_kms = { version = "0.48", default-features = false } +alloy-signer = { workspace = true, features = ["eip712"] } +alloy-signer-wallet = { workspace = true, features = ["mnemonic", "keystore"] } +alloy-signer-aws.workspace = true +alloy-signer-ledger.workspace = true +alloy-signer-trezor.workspace = true +alloy-network.workspace = true +alloy-consensus.workspace = true +alloy-sol-types.workspace = true +alloy-dyn-abi.workspace = true + +aws-sdk-kms = { version = "1", default-features = false } +aws-config = "1" foundry-config.workspace = true foundry-common.workspace = true @@ -38,5 +43,4 @@ tokio = { version = "1", features = ["macros"] } [features] default = ["rustls"] -rustls = ["ethers-providers/rustls", "rusoto_core/rustls"] -openssl = ["ethers-providers/openssl"] +rustls = ["aws-sdk-kms/rustls"] diff --git a/crates/wallets/src/error.rs b/crates/wallets/src/error.rs index 6588f5e22cc1..b9a5e34f592a 100644 --- a/crates/wallets/src/error.rs +++ b/crates/wallets/src/error.rs @@ -1,4 +1,8 @@ -use ethers_signers::{AwsSignerError, LedgerError, TrezorError, WalletError}; +use alloy_signer::k256::ecdsa; +use alloy_signer_aws::AwsSignerError; +use alloy_signer_ledger::LedgerError; +use alloy_signer_trezor::TrezorError; +use alloy_signer_wallet::WalletError; use hex::FromHexError; #[derive(Debug, thiserror::Error)] @@ -23,6 +27,8 @@ pub enum WalletSignerError { Io(#[from] std::io::Error), #[error(transparent)] InvalidHex(#[from] FromHexError), + #[error(transparent)] + Ecdsa(#[from] ecdsa::Error), #[error("{0} cannot sign raw hashes")] CannotSignRawHash(&'static str), } diff --git a/crates/wallets/src/multi_wallet.rs b/crates/wallets/src/multi_wallet.rs index d9673985a11f..c95bb8d0e2ae 100644 --- a/crates/wallets/src/multi_wallet.rs +++ b/crates/wallets/src/multi_wallet.rs @@ -3,11 +3,10 @@ use crate::{ wallet_signer::{PendingSigner, WalletSigner}, }; use alloy_primitives::Address; +use alloy_signer::Signer; use clap::Parser; use derive_builder::Builder; -use ethers_signers::Signer; use eyre::Result; -use foundry_common::types::ToAlloy; use foundry_config::Config; use serde::Serialize; use std::{collections::HashMap, iter::repeat, path::PathBuf}; @@ -24,15 +23,14 @@ pub struct MultiWallet { impl MultiWallet { pub fn new(pending_signers: Vec, signers: Vec) -> Self { - let signers = - signers.into_iter().map(|signer| (signer.address().to_alloy(), signer)).collect(); + let signers = signers.into_iter().map(|signer| (signer.address(), signer)).collect(); Self { pending_signers, signers } } fn maybe_unlock_pending(&mut self) -> Result<()> { for pending in self.pending_signers.drain(..) { let signer = pending.unlock()?; - self.signers.insert(signer.address().to_alloy(), signer); + self.signers.insert(signer.address(), signer); } Ok(()) } @@ -48,7 +46,7 @@ impl MultiWallet { } pub fn add_signer(&mut self, signer: WalletSigner) { - self.signers.insert(signer.address().to_alloy(), signer); + self.signers.insert(signer.address(), signer); } } @@ -386,7 +384,7 @@ impl MultiWalletOpts { .collect::>(); for key in aws_keys { - let aws_signer = WalletSigner::from_aws(&key).await?; + let aws_signer = WalletSigner::from_aws(key).await?; wallets.push(aws_signer) } @@ -399,7 +397,7 @@ impl MultiWalletOpts { #[cfg(test)] mod tests { use super::*; - use std::path::Path; + use std::{path::Path, str::FromStr}; #[test] fn parse_keystore_args() { @@ -439,7 +437,7 @@ mod tests { assert_eq!(unlocked.len(), 1); assert_eq!( unlocked[0].address(), - "ec554aeafe75601aaab43bd4621a22284db566c2".parse().unwrap() + Address::from_str("0xec554aeafe75601aaab43bd4621a22284db566c2").unwrap() ); } diff --git a/crates/wallets/src/utils.rs b/crates/wallets/src/utils.rs index a10903313034..08c95242af33 100644 --- a/crates/wallets/src/utils.rs +++ b/crates/wallets/src/utils.rs @@ -1,39 +1,35 @@ use crate::{error::PrivateKeyError, PendingSigner, WalletSigner}; -use ethers_signers::{HDPath as LedgerHDPath, LocalWallet, TrezorHDPath, WalletError}; +use alloy_primitives::B256; +use alloy_signer_ledger::HDPath as LedgerHDPath; +use alloy_signer_trezor::HDPath as TrezorHDPath; +use alloy_signer_wallet::LocalWallet; use eyre::{Context, Result}; use foundry_config::Config; use std::{ fs, path::{Path, PathBuf}, - str::FromStr, }; +fn ensure_pk_not_env(pk: &str) -> Result<()> { + if !pk.starts_with("0x") && std::env::var(pk).is_ok() { + return Err(PrivateKeyError::ExistsAsEnvVar(pk.to_string()).into()); + } + Ok(()) +} + /// Validates and sanitizes user inputs, returning configured [WalletSigner]. pub fn create_private_key_signer(private_key: &str) -> Result { let privk = private_key.trim().strip_prefix("0x").unwrap_or(private_key); - match LocalWallet::from_str(privk) { + + let Ok(private_key) = hex::decode(privk) else { + ensure_pk_not_env(privk)?; + eyre::bail!("Failed to decode private key") + }; + + match LocalWallet::from_bytes(&B256::from_slice(&private_key)) { Ok(pk) => Ok(WalletSigner::Local(pk)), Err(err) => { - // helper closure to check if pk was meant to be an env var, this usually happens if - // `$` is missing - let ensure_not_env = |pk: &str| { - // check if pk was meant to be an env var - if !pk.starts_with("0x") && std::env::var(pk).is_ok() { - // SAFETY: at this point we know the user actually wanted to use an env var - // and most likely forgot the `$` anchor, so the - // `private_key` here is an unresolved env var - return Err(PrivateKeyError::ExistsAsEnvVar(pk.to_string())) - } - Ok(()) - }; - match err { - WalletError::HexError(err) => { - ensure_not_env(private_key)?; - return Err(PrivateKeyError::InvalidHex(err).into()); - } - WalletError::EcdsaError(_) => ensure_not_env(private_key)?, - _ => {} - }; + ensure_pk_not_env(privk)?; eyre::bail!("Failed to create wallet from private key: {err}") } } diff --git a/crates/wallets/src/wallet.rs b/crates/wallets/src/wallet.rs index cd7359f2e2ce..5773e57d80b6 100644 --- a/crates/wallets/src/wallet.rs +++ b/crates/wallets/src/wallet.rs @@ -1,9 +1,8 @@ use crate::{raw_wallet::RawWalletOpts, utils, wallet_signer::WalletSigner}; use alloy_primitives::Address; +use alloy_signer::Signer; use clap::Parser; -use ethers_signers::Signer; use eyre::Result; -use foundry_common::types::ToAlloy; use serde::Serialize; /// The wallet options can either be: @@ -95,7 +94,7 @@ impl WalletOpts { .await? } else if self.aws { let key_id = std::env::var("AWS_KMS_KEY_ID")?; - WalletSigner::from_aws(&key_id).await? + WalletSigner::from_aws(key_id).await? } else if let Some(raw_wallet) = self.raw.signer()? { raw_wallet } else if let Some(path) = utils::maybe_get_keystore_path( @@ -139,7 +138,7 @@ of the unlocked account you want to use, or provide the --from flag with the add if let Some(from) = self.from { from } else if let Ok(signer) = self.signer().await { - signer.address().to_alloy() + signer.address() } else { Address::ZERO } @@ -176,7 +175,7 @@ mod tests { ]); let signer = wallet.signer().await.unwrap(); assert_eq!( - signer.address().to_alloy(), + signer.address(), Address::from_str("ec554aeafe75601aaab43bd4621a22284db566c2").unwrap() ); } @@ -207,7 +206,7 @@ mod tests { } Err(x) => { assert!( - x.to_string().contains("Failed to create wallet"), + x.to_string().contains("Failed to decode private key"), "Error message is not user-friendly" ); } diff --git a/crates/wallets/src/wallet_signer.rs b/crates/wallets/src/wallet_signer.rs index d71fbe1af7ff..9cf4478e2d8b 100644 --- a/crates/wallets/src/wallet_signer.rs +++ b/crates/wallets/src/wallet_signer.rs @@ -1,19 +1,17 @@ use crate::error::WalletSignerError; -use alloy_primitives::B256; +use alloy_consensus::SignableTransaction; +use alloy_dyn_abi::TypedData; +use alloy_network::TxSigner; +use alloy_primitives::{Address, ChainId, B256}; +use alloy_signer::{Signature, Signer}; +use alloy_signer_aws::AwsSigner; +use alloy_signer_ledger::{HDPath as LedgerHDPath, LedgerSigner}; +use alloy_signer_trezor::{HDPath as TrezorHDPath, TrezorSigner}; +use alloy_signer_wallet::{coins_bip39::English, LocalWallet, MnemonicBuilder}; +use alloy_sol_types::{Eip712Domain, SolStruct}; use async_trait::async_trait; -use ethers_core::types::{ - transaction::{eip2718::TypedTransaction, eip712::Eip712}, - Signature, -}; -use ethers_signers::{ - coins_bip39::English, AwsSigner, HDPath as LedgerHDPath, Ledger, LocalWallet, MnemonicBuilder, - Signer, Trezor, TrezorHDPath, -}; -use rusoto_core::{ - credential::ChainProvider as AwsChainProvider, region::Region as AwsRegion, - request::HttpClient as AwsHttpClient, Client as AwsClient, -}; -use rusoto_kms::KmsClient; +use aws_config::BehaviorVersion; +use aws_sdk_kms::Client as AwsClient; use std::path::PathBuf; pub type Result = std::result::Result; @@ -24,36 +22,34 @@ pub enum WalletSigner { /// Wrapper around local wallet. e.g. private key, mnemonic Local(LocalWallet), /// Wrapper around Ledger signer. - Ledger(Ledger), + Ledger(LedgerSigner), /// Wrapper around Trezor signer. - Trezor(Trezor), + Trezor(TrezorSigner), /// Wrapper around AWS KMS signer. Aws(AwsSigner), } impl WalletSigner { pub async fn from_ledger_path(path: LedgerHDPath) -> Result { - let ledger = Ledger::new(path, 1).await?; + let ledger = LedgerSigner::new(path, None).await?; Ok(Self::Ledger(ledger)) } pub async fn from_trezor_path(path: TrezorHDPath) -> Result { // cached to ~/.ethers-rs/trezor/cache/trezor.session - let trezor = Trezor::new(path, 1, None).await?; + let trezor = TrezorSigner::new(path, None).await?; Ok(Self::Trezor(trezor)) } - pub async fn from_aws(key_id: &str) -> Result { - let client = - AwsClient::new_with(AwsChainProvider::default(), AwsHttpClient::new().unwrap()); + pub async fn from_aws(key_id: String) -> Result { + let config = aws_config::load_defaults(BehaviorVersion::latest()).await; + let client = AwsClient::new(&config); - let kms = KmsClient::new_with_client(client, AwsRegion::default()); - - Ok(Self::Aws(AwsSigner::new(kms, key_id, 1).await?)) + Ok(Self::Aws(AwsSigner::new(client, key_id, None).await?)) } pub fn from_private_key(private_key: impl AsRef<[u8]>) -> Result { - let wallet = LocalWallet::from_bytes(private_key.as_ref())?; + let wallet = LocalWallet::from_bytes(&B256::from_slice(private_key.as_ref()))?; Ok(Self::Local(wallet)) } @@ -63,7 +59,7 @@ impl WalletSigner { /// - the result for Ledger signers includes addresses available for both LedgerLive and Legacy /// derivation paths /// - for Local and AWS signers the result contains a single address - pub async fn available_senders(&self, max: usize) -> Result> { + pub async fn available_senders(&self, max: usize) -> Result> { let mut senders = Vec::new(); match self { WalletSigner::Local(local) => { @@ -136,78 +132,53 @@ macro_rules! delegate { #[async_trait] impl Signer for WalletSigner { - type Error = WalletSignerError; - - async fn sign_message>(&self, message: S) -> Result { - delegate!(self, inner => inner.sign_message(message).await.map_err(Into::into)) - } - - async fn sign_transaction(&self, message: &TypedTransaction) -> Result { - delegate!(self, inner => inner.sign_transaction(message).await.map_err(Into::into)) + /// Signs the given hash. + async fn sign_hash(&self, hash: &B256) -> alloy_signer::Result { + delegate!(self, inner => inner.sign_hash(hash)).await } - async fn sign_typed_data(&self, payload: &T) -> Result { - delegate!(self, inner => inner.sign_typed_data(payload).await.map_err(Into::into)) + async fn sign_message(&self, message: &[u8]) -> alloy_signer::Result { + delegate!(self, inner => inner.sign_message(message)).await } - fn address(&self) -> ethers_core::types::Address { + fn address(&self) -> Address { delegate!(self, inner => inner.address()) } - fn chain_id(&self) -> u64 { + fn chain_id(&self) -> Option { delegate!(self, inner => inner.chain_id()) } - fn with_chain_id>(self, chain_id: T) -> Self { - match self { - Self::Local(inner) => Self::Local(inner.with_chain_id(chain_id)), - Self::Ledger(inner) => Self::Ledger(inner.with_chain_id(chain_id)), - Self::Trezor(inner) => Self::Trezor(inner.with_chain_id(chain_id)), - Self::Aws(inner) => Self::Aws(inner.with_chain_id(chain_id)), - } - } -} - -#[async_trait] -impl Signer for &WalletSigner { - type Error = WalletSignerError; - - async fn sign_message>(&self, message: S) -> Result { - (*self).sign_message(message).await + fn set_chain_id(&mut self, chain_id: Option) { + delegate!(self, inner => inner.set_chain_id(chain_id)) } - async fn sign_transaction(&self, message: &TypedTransaction) -> Result { - (*self).sign_transaction(message).await + async fn sign_typed_data( + &self, + payload: &T, + domain: &Eip712Domain, + ) -> alloy_signer::Result + where + Self: Sized, + { + delegate!(self, inner => inner.sign_typed_data(payload, domain)).await } - async fn sign_typed_data(&self, payload: &T) -> Result { - (*self).sign_typed_data(payload).await - } - - fn address(&self) -> ethers_core::types::Address { - (*self).address() - } - - fn chain_id(&self) -> u64 { - (*self).chain_id() - } - - fn with_chain_id>(self, chain_id: T) -> Self { - let _ = chain_id; - self + async fn sign_dynamic_typed_data( + &self, + payload: &TypedData, + ) -> alloy_signer::Result { + delegate!(self, inner => inner.sign_dynamic_typed_data(payload)).await } } -impl WalletSigner { - pub async fn sign_hash(&self, hash: &B256) -> Result { - match self { - // TODO: AWS can sign hashes but utilities aren't exposed in ethers-signers. - // TODO: Implement with alloy-signer. - Self::Aws(_aws) => Err(WalletSignerError::CannotSignRawHash("AWS")), - Self::Ledger(_) => Err(WalletSignerError::CannotSignRawHash("Ledger")), - Self::Local(wallet) => wallet.sign_hash(hash.0.into()).map_err(Into::into), - Self::Trezor(_) => Err(WalletSignerError::CannotSignRawHash("Trezor")), - } +#[async_trait] +impl TxSigner for WalletSigner { + async fn sign_transaction( + &self, + tx: &mut dyn SignableTransaction, + ) -> alloy_signer::Result { + delegate!(self, inner => inner.sign_transaction(tx)).await } }