diff --git a/Cargo.lock b/Cargo.lock index 5c3655fc..4985af4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,17 +26,487 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloy-consensus" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f021a55afd68ff2364ccfddaa364fc9a38a72200cdc74fcfb8dc3231d38f2c" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-trie", + "alloy-tx-macros", + "auto_impl", + "c-kzg", + "derive_more 2.0.1", + "either", + "k256", + "once_cell", + "rand 0.8.5", + "secp256k1", + "serde", + "serde_with", + "thiserror 2.0.16", +] + +[[package]] +name = "alloy-consensus-any" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0ecca7a71b1f88e63d19e2d9397ce56949d3dd3484fd73c73d0077dc5c93d4" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-eip2124" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "741bdd7499908b3aa0b159bba11e71c8cddd009a2c2eb7a06e825f1ec87900a5" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "crc", + "serde", + "thiserror 2.0.16", +] + +[[package]] +name = "alloy-eip2930" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b82752a889170df67bbb36d42ca63c531eb16274f0d7299ae2a680facba17bd" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d4769c6ffddca380b0070d71c8b7f30bed375543fe76bb2f74ec0acf4b7cd16" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", + "thiserror 2.0.16", +] + +[[package]] +name = "alloy-eips" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7473a19f02b25f8e1e8c69d35f02c07245694d11bd91bfe00e9190ac106b3838" +dependencies = [ + "alloy-eip2124", + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "auto_impl", + "c-kzg", + "derive_more 2.0.1", + "either", + "serde", + "sha2", +] + +[[package]] +name = "alloy-json-abi" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "125a1c373261b252e53e04d6e92c37d881833afc1315fceab53fd46045695640" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-network-primitives" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d540d962ddbc3e95153bafe56ccefeb16dfbffa52c5f7bdd66cd29ec8f52259" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9485c56de23438127a731a6b4c87803d49faf1a7068dcd1d8768aca3a9edb9" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 2.0.1", + "foldhash", + "hashbrown 0.15.5", + "indexmap 2.10.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.2", + "ruint", + "rustc-hash", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" +dependencies = [ + "alloy-rlp-derive", + "arrayvec 0.7.6", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "alloy-rpc-types" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d4fe522f6fc749c8afce721bdc8f73b715d317c3c02fcb9b51f7a143e4401dd" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1a77a23d609bca2e4a60f992dde5f987475cb064da355fa4dbd7cda2e1bb48" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "derive_more 2.0.1", + "jsonwebtoken", + "rand 0.8.5", + "serde", + "strum 0.27.2", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781d4d5020bea8f020e164f5593101c2e2f790d66d04a0727839d03bc4411ed7" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "itertools 0.14.0", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.16", +] + +[[package]] +name = "alloy-serde" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30be84f45d4f687b00efaba1e6290cbf53ccc8f6b8fbb54e4c2f9d2a0474ce95" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-sol-macro" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d20d867dcf42019d4779519a1ceb55eba8d7f3d0e4f0a89bcba82b8f9eb01e48" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74e91b0b553c115d14bd0ed41898309356dc85d0e3d4b9014c4e7715e48c8ad" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap 2.10.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.106", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84194d31220803f5f62d0a00f583fd3a062b36382e2bea446f1af96727754565" +dependencies = [ + "const-hex", + "dunce", + "heck", + "macro-string", + "proc-macro2", + "quote", + "syn 2.0.106", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe8c27b3cf6b2bb8361904732f955bc7c05e00be5f469cec7e2280b6167f3ff0" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5383d34ea00079e6dd89c652bcbdb764db160cef84e6250926961a0b2295d04" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "serde", +] + +[[package]] +name = "alloy-trie" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3412d52bb97c6c6cc27ccc28d4e6e8cf605469101193b50b0bd5813b1f990b5" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arrayvec 0.7.6", + "derive_more 2.0.1", + "nybbles", + "serde", + "smallvec", + "tracing", +] + +[[package]] +name = "alloy-tx-macros" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e29436068f836727d4e7c819ae6bf6f9c9e19a32e96fc23e814709a277f23a" +dependencies = [ + "alloy-primitives", + "darling", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] [[package]] name = "arrayvec" @@ -49,6 +519,9 @@ name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] [[package]] name = "ascii-canvas" @@ -65,6 +538,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -79,7 +563,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -197,11 +681,27 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] + [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" [[package]] name = "bitvec" @@ -224,6 +724,18 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blst" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fd49896f12ac9b6dcd7a5998466b9b58263a695a3dd1ecc1aaca2e12a90b080" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + [[package]] name = "bumpalo" version = "3.19.0" @@ -251,6 +763,21 @@ dependencies = [ "serde", ] +[[package]] +name = "c-kzg" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7318cfa722931cb5fe0838b98d3ce5621e75f6a6408abc21721d80de9223f2e4" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + [[package]] name = "camino" version = "1.1.11" @@ -292,7 +819,7 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -332,7 +859,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "thiserror 2.0.12", + "thiserror 2.0.16", "tower", "tower-layer", ] @@ -361,7 +888,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -381,25 +908,25 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.26", "serde", "serde_json", ] [[package]] name = "cc" -version = "1.2.32" +version = "1.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" dependencies = [ "shlex", ] [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -413,7 +940,11 @@ version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ + "android-tzdata", + "iana-time-zone", "num-traits", + "serde", + "windows-link", ] [[package]] @@ -455,9 +986,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.14.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e22e0ed40b96a48d3db274f72fd365bd78f67af39b6bbd47e8a15e1c6207ff" +checksum = "dccd746bf9b1038c0507b7cec21eb2b11222db96a2902c96e8c185d6d20fb9c4" dependencies = [ "cfg-if", "cpufeatures", @@ -526,6 +1057,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.5.0" @@ -599,7 +1145,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -610,7 +1156,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -636,6 +1182,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -664,7 +1222,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -675,7 +1233,17 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", ] [[package]] @@ -719,9 +1287,15 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -749,9 +1323,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", + "serdect", "signature", "spki", ] @@ -761,6 +1336,9 @@ name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] [[package]] name = "elliptic-curve" @@ -770,13 +1348,14 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", "pkcs8", "rand_core 0.6.4", "sec1", + "serdect", "subtle", "zeroize", ] @@ -817,12 +1396,11 @@ dependencies = [ [[package]] name = "escargot" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f351750780493fc33fa0ce8ba3c7d61f9736cfa3b3bb9ee2342643ffe40211" +checksum = "11c3aea32bc97b500c9ca6a72b768a26e558264303d101d3409cf6d57a9ed0cf" dependencies = [ "log", - "once_cell", "serde", "serde_json", ] @@ -947,7 +1525,7 @@ dependencies = [ "serde_bytes", "serde_json", "sha2", - "thiserror 2.0.12", + "thiserror 2.0.16", "thousands", "tower", "tower-http", @@ -955,10 +1533,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "evm_rpc_client" +version = "1.4.0" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types", + "async-trait", + "candid", + "evm_rpc_types", + "ic-cdk", + "ic-error-types", + "serde", + "strum 0.27.2", + "tokio", +] + [[package]] name = "evm_rpc_types" version = "2.0.0" dependencies = [ + "alloy-primitives", + "alloy-rpc-types", "candid", "canlog", "hex", @@ -967,8 +1563,9 @@ dependencies = [ "num-bigint", "proptest", "serde", + "serde_json", "strum 0.27.2", - "thiserror 2.0.12", + "thiserror 2.0.16", "url", ] @@ -978,6 +1575,28 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec 0.7.6", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec 0.7.6", + "auto_impl", + "bytes", +] + [[package]] name = "ff" version = "0.13.1" @@ -1022,11 +1641,17 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -1067,7 +1692,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1143,6 +1768,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "group" version = "0.13.0" @@ -1166,7 +1797,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -1189,11 +1820,21 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", + "serde", +] [[package]] name = "heck" @@ -1201,6 +1842,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hex" version = "0.4.3" @@ -1210,13 +1857,22 @@ dependencies = [ "serde", ] +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec 0.7.6", +] + [[package]] name = "hmac" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -1261,19 +1917,21 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", "httparse", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -1321,6 +1979,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ic-canister-log" version = "0.2.0" @@ -1370,7 +2052,7 @@ dependencies = [ "quote", "serde", "serde_tokenstream", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1442,9 +2124,9 @@ dependencies = [ [[package]] name = "ic-management-canister-types" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95f3af3543f6d0cbdecd2dcdfd4737ada2bd42d935cc787eec22090c96492c76" +checksum = "ea7e5b8a0f7c3b320d9450ac950547db4f24a31601b5d398f9680b64427455d2" dependencies = [ "candid", "serde", @@ -1499,7 +2181,7 @@ dependencies = [ "serde_cbor", "serde_repr", "sha2", - "thiserror 2.0.12", + "thiserror 2.0.16", ] [[package]] @@ -1622,9 +2304,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -1676,7 +2358,18 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", ] [[package]] @@ -1686,7 +2379,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.5", + "serde", ] [[package]] @@ -1725,6 +2419,15 @@ dependencies = [ "serde", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.11.0" @@ -1734,6 +2437,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -1750,6 +2462,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "k256" version = "0.13.4" @@ -1760,6 +2487,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", + "serdect", "sha2", ] @@ -1772,6 +2500,16 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + [[package]] name = "lalrpop" version = "0.20.2" @@ -1781,7 +2519,7 @@ dependencies = [ "ascii-canvas", "bit-set 0.5.3", "ena", - "itertools", + "itertools 0.11.0", "lalrpop-util", "petgraph", "pico-args", @@ -1819,7 +2557,13 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" name = "libc" version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" @@ -1879,7 +2623,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.6.29", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1897,6 +2641,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "maplit" version = "1.0.2" @@ -1971,7 +2726,7 @@ checksum = "512ce2c37128698ea15c99b3518936c78a8b112b92468e7b95b9fa045666ebd8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2043,6 +2798,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", ] [[package]] @@ -2064,7 +2830,21 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", +] + +[[package]] +name = "nybbles" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63cb50036b1ad148038105af40aaa70ff24d8a14fbc44ae5c914e1348533d12e" +dependencies = [ + "alloy-rlp", + "cfg-if", + "proptest", + "ruint", + "serde", + "smallvec", ] [[package]] @@ -2144,7 +2924,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2176,11 +2956,32 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pest" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +dependencies = [ + "memchr", + "thiserror 2.0.16", + "ucd-trie", +] [[package]] name = "petgraph" @@ -2189,7 +2990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.10.0", ] [[package]] @@ -2224,7 +3025,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2264,7 +3065,7 @@ dependencies = [ "ic-management-canister-types", "ic-transport-types", "reqwest", - "schemars", + "schemars 0.8.22", "serde", "serde_bytes", "serde_cbor", @@ -2274,7 +3075,7 @@ dependencies = [ "strum 0.26.3", "strum_macros 0.26.4", "tempfile", - "thiserror 2.0.12", + "thiserror 2.0.16", "tokio", "tracing", "tracing-appender", @@ -2346,11 +3147,33 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "proc-macro2" -version = "1.0.96" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beef09f85ae72cea1ef96ba6870c51e6382ebfa4f0e85b643459331f3daa5be0" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -2404,7 +3227,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.5.10", - "thiserror 2.0.12", + "thiserror 2.0.16", "tokio", "tracing", "web-time", @@ -2425,7 +3248,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror 2.0.16", "tinyvec", "tracing", "web-time", @@ -2475,6 +3298,7 @@ dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", + "serde", ] [[package]] @@ -2485,6 +3309,7 @@ checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", + "serde", ] [[package]] @@ -2523,6 +3348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom 0.3.3", + "serde", ] [[package]] @@ -2554,6 +3380,26 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "regex" version = "1.11.1" @@ -2600,9 +3446,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64 0.22.1", "bytes", @@ -2689,6 +3535,39 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ruint" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecb38f82477f20c5c3d62ef52d7c4e536e38ea9b73fb570a20c5cae0e14bcf6" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand 0.8.5", + "rand 0.9.2", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -2707,6 +3586,24 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.26", +] + [[package]] name = "rustix" version = "1.0.8" @@ -2821,7 +3718,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2845,6 +3742,30 @@ dependencies = [ "serde_json", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "schemars_derive" version = "0.8.22" @@ -2854,7 +3775,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2873,10 +3794,32 @@ dependencies = [ "der", "generic-array", "pkcs8", + "serdect", "subtle", "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + [[package]] name = "security-framework" version = "3.3.0" @@ -2900,6 +3843,15 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.26" @@ -2909,6 +3861,15 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.219" @@ -2945,7 +3906,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2956,14 +3917,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -2979,7 +3940,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2991,7 +3952,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3006,6 +3967,48 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.10.0", + "schemars 0.9.0", + "schemars 1.0.4", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha2" version = "0.10.9" @@ -3014,7 +4017,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -3023,10 +4026,20 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest", + "digest 0.10.7", "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -3057,10 +4070,22 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.16", + "time", +] + [[package]] name = "siphasher" version = "1.0.1" @@ -3087,6 +4112,9 @@ name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -3189,7 +4217,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3201,7 +4229,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3223,15 +4251,27 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b198d366dbec045acfcd97295eb653a7a2b40e4dc764ef1e79aafcad439d3c" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -3249,7 +4289,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3260,15 +4300,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3302,11 +4342,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.16", ] [[package]] @@ -3317,18 +4357,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3346,6 +4386,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "time" version = "0.3.41" @@ -3398,9 +4447,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -3439,7 +4488,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3477,7 +4526,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap", + "indexmap 2.10.0", "toml_datetime", "winnow", ] @@ -3558,7 +4607,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3632,6 +4681,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "uint" version = "0.9.5" @@ -3688,9 +4743,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.4" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "ec961601b32b6f5d14ae8dabd35ff2ff2e2c6cb4c0e6641845ff105abe96d958" dependencies = [ "form_urlencoded", "idna", @@ -3780,7 +4835,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -3815,7 +4870,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3889,11 +4944,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3902,12 +4957,65 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "windows-link" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -4123,7 +5231,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -4144,7 +5252,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4164,7 +5272,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -4185,7 +5293,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4218,5 +5326,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] diff --git a/Cargo.toml b/Cargo.toml index 5f1afff9..dc06a54e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ zeroize = { version = "1.8", features = ["zeroize_derive"] } regex = "1.11" [dev-dependencies] -assert_matches = "1.5" +assert_matches = { workspace = true } candid_parser = { workspace = true } ic-crypto-test-utils-reproducible-rng = { git = "https://github.com/dfinity/ic", rev = "release-2024-09-26_01-31-base" } ic-management-canister-types = { workspace = true } @@ -61,7 +61,10 @@ proptest = { workspace = true } rand = "0.8" [workspace.dependencies] +alloy-primitives = "1.3.0" +alloy-rpc-types = "1.0.23" assert_matches = "1.5.0" +async-trait = "0.1.88" candid = { version = "0.10.13" } canlog = { version = "0.2.0", features = ["derive"] } candid_parser = { version = "0.1.4" } @@ -103,4 +106,4 @@ thiserror = "2.0.12" url = "2.5" [workspace] -members = ["e2e/rust", "evm_rpc_types"] +members = ["e2e/rust", "evm_rpc_types", "evm_rpc_client"] diff --git a/evm_rpc_client/CHANGELOG.md b/evm_rpc_client/CHANGELOG.md new file mode 100644 index 00000000..7b307443 --- /dev/null +++ b/evm_rpc_client/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] \ No newline at end of file diff --git a/evm_rpc_client/Cargo.toml b/evm_rpc_client/Cargo.toml new file mode 100644 index 00000000..2fc713e3 --- /dev/null +++ b/evm_rpc_client/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "evm_rpc_client" +version = "1.4.0" +description = "Rust client for interacting with the EVM RPC canister" +license = "Apache-2.0" +readme = "README.md" +authors = ["DFINITY Foundation"] +edition = "2021" +include = ["src", "Cargo.toml", "CHANGELOG.md", "LICENSE", "README.md"] +repository = "https://github.com/dfinity/evm-rpc-canister" +documentation = "https://docs.rs/evm_rpc_client" + +[dependencies] +alloy-primitives = { workspace = true } +alloy-rpc-types = { workspace = true } +async-trait = { workspace = true } +candid = { workspace = true } +evm_rpc_types = { path = "../evm_rpc_types", features = ["alloy"] } +ic-cdk = { workspace = true } +ic-error-types = { workspace = true } +serde = { workspace = true } +strum = { workspace = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["full"] } \ No newline at end of file diff --git a/evm_rpc_client/LICENSE b/evm_rpc_client/LICENSE new file mode 120000 index 00000000..ea5b6064 --- /dev/null +++ b/evm_rpc_client/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/evm_rpc_client/NOTICE b/evm_rpc_client/NOTICE new file mode 120000 index 00000000..7e1b82f6 --- /dev/null +++ b/evm_rpc_client/NOTICE @@ -0,0 +1 @@ +../NOTICE \ No newline at end of file diff --git a/evm_rpc_client/README.md b/evm_rpc_client/README.md new file mode 100644 index 00000000..577d4cce --- /dev/null +++ b/evm_rpc_client/README.md @@ -0,0 +1,3 @@ +# EVM RPC Client + +This crate defines a client for interacting with the EVM RPC canister. \ No newline at end of file diff --git a/evm_rpc_client/src/fixtures/mod.rs b/evm_rpc_client/src/fixtures/mod.rs new file mode 100644 index 00000000..612624d1 --- /dev/null +++ b/evm_rpc_client/src/fixtures/mod.rs @@ -0,0 +1,132 @@ +//! Simple types to create basic unit tests for the [`crate::EvmRpcClient`]. +//! +//! Types and methods for this module are only available for non-canister architecture (non `wasm32`). + +use crate::{ClientBuilder, Runtime}; +use async_trait::async_trait; +use candid::{utils::ArgumentEncoder, CandidType, Decode, Encode, Principal}; +use ic_error_types::RejectCode; +use serde::de::DeserializeOwned; +use std::collections::BTreeMap; + +impl ClientBuilder { + /// Set the runtime to a [`StubRuntime`]. + pub fn with_stub_responses(self) -> ClientBuilder { + self.with_runtime(|_runtime| StubRuntime::default()) + } + + /// Change the runtime to return the given stub response for all calls. + pub fn with_default_stub_response( + self, + stub_response: Out, + ) -> ClientBuilder { + self.with_stub_responses() + .with_default_response(stub_response) + } +} + +impl ClientBuilder { + /// Change the runtime to return the given stub response for all calls. + pub fn with_default_response( + self, + stub_response: Out, + ) -> ClientBuilder { + self.with_runtime(|runtime| runtime.with_default_response(stub_response)) + } + + /// Change the runtime to return the given stub response for calls to the given method. + pub fn with_response_for_method( + self, + method_name: &str, + stub_response: Out, + ) -> ClientBuilder { + self.with_runtime(|runtime| runtime.with_response_for_method(method_name, stub_response)) + } +} + +/// An implementation of [`Runtime`] that always returns the same candid-encoded response +/// for a given method. +/// +/// Implement your own [`Runtime`] in case a more refined approach is needed. +pub struct StubRuntime { + default_call_result: Option>, + method_to_call_result_map: BTreeMap>, +} + +impl StubRuntime { + /// Create a new [`StubRuntime`] with the given default stub response. + pub fn new() -> Self { + Self { + default_call_result: None, + method_to_call_result_map: BTreeMap::new(), + } + } + + /// Create a new [`StubRuntime`] with the given default stub response. + pub fn with_default_response(mut self, stub_response: Out) -> Self { + let result = Encode!(&stub_response).expect("Failed to encode Candid stub response"); + self.default_call_result = Some(result); + self + } + + /// Modify a [`StubRuntime`] to return the given response for the given method + pub fn with_response_for_method( + mut self, + method: &str, + stub_response: Out, + ) -> Self { + self.method_to_call_result_map.insert( + method.to_string(), + Encode!(&stub_response).expect("Failed to encode Candid stub response"), + ); + self + } + + fn call(&self, method: &str) -> Result + where + Out: CandidType + DeserializeOwned, + { + let bytes = self + .method_to_call_result_map + .get(method) + .or(self.default_call_result.as_ref()) + .unwrap_or_else(|| panic!("No available call response value for method `{method}`")); + Ok(Decode!(bytes, Out).expect("Failed to decode Candid stub response")) + } +} + +impl Default for StubRuntime { + fn default() -> Self { + Self::new() + } +} + +#[async_trait] +impl Runtime for StubRuntime { + async fn update_call( + &self, + _id: Principal, + method: &str, + _args: In, + _cycles: u128, + ) -> Result + where + In: ArgumentEncoder + Send, + Out: CandidType + DeserializeOwned, + { + self.call(method) + } + + async fn query_call( + &self, + _id: Principal, + method: &str, + _args: In, + ) -> Result + where + In: ArgumentEncoder + Send, + Out: CandidType + DeserializeOwned, + { + self.call(method) + } +} diff --git a/evm_rpc_client/src/lib.rs b/evm_rpc_client/src/lib.rs new file mode 100644 index 00000000..a9fc55db --- /dev/null +++ b/evm_rpc_client/src/lib.rs @@ -0,0 +1,507 @@ +//! Client to interact with the EVM RPC canister +//! +//! # Examples +//! +//! ## Configuring the client +//! +//! By default, any RPC endpoint supported by the EVM RPC canister will call 3 providers and require +//! equality between their results. It is possible to customize the client so that another strategy, +//! such as 2-out-of-3 in the example below, is used for all following calls. +//! +//! ```rust +//! use evm_rpc_client::EvmRpcClient; +//! use evm_rpc_types::{ConsensusStrategy, RpcConfig, RpcServices}; +//! +//! let client = EvmRpcClient::builder_for_ic() +//! .with_rpc_sources(RpcServices::EthMainnet(None)) +//! .with_consensus_strategy(ConsensusStrategy::Threshold { +//! total: Some(3), +//! min: 2, +//! }) +//! .build(); +//! ``` +//! +//! ## Specifying the amount of cycles to send +//! +//! Every call made to the EVM RPC canister that triggers HTTPs outcalls (e.g., `eth_getLogs`) +//! needs to attach some cycles to pay for the call. +//! By default, the client will attach some amount of cycles that should be sufficient for most cases. +//! +//! If this is not the case, the amount of cycles to be sent can be overridden. It's advisable to +//! actually send *more* cycles than required, since *unused cycles will be refunded*. +//! +//! ```rust +//! # // TODO XC-412: Use simpler example e.g. `eth_getBalance` +//! use alloy_primitives::{address, b256, bytes}; +//! use evm_rpc_client::EvmRpcClient; +//! +//! # use evm_rpc_types::{Hex, Hex20, Hex32, MultiRpcResult}; +//! # use std::str::FromStr; +//! # #[tokio::main] +//! # async fn main() -> Result<(), Box> { +//! let client = EvmRpcClient::builder_for_ic() +//! # .with_default_stub_response(MultiRpcResult::Consistent(Ok(vec![ +//! # evm_rpc_types::LogEntry { +//! # address: Hex20::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(), +//! # topics: vec![ +//! # Hex32::from_str("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef").unwrap(), +//! # Hex32::from_str("0x000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90").unwrap(), +//! # Hex32::from_str("0x0000000000000000000000000000000aa232009084bd71a5797d089aa4edfad4").unwrap(), +//! # ], +//! # data: Hex::from_str("0x00000000000000000000000000000000000000000000000000000000cd566ae8").unwrap(), +//! # block_number: Some(0x161bd70_u64.into()), +//! # transaction_hash: Some(Hex32::from_str("0xfe5bc88d0818b66a67b0619b1b4d81bfe38029e3799c7f0eb86b33ca7dc4c811").unwrap()), +//! # transaction_index: Some(0x0_u64.into()), +//! # block_hash: Some(Hex32::from_str("0x0bbd9b12140e674cdd55e63539a25df8280a70cee3676c94d8e05fa5f868a914").unwrap()), +//! # log_index: Some(0x0_u64.into()), +//! # removed: false, +//! # } +//! # ]))) +//! .build(); +//! +//! let result = client +//! .get_logs(vec![address!("0xdac17f958d2ee523a2206206994597c13d831ec7")]) +//! .with_cycles(10_000_000_000) +//! .send() +//! .await +//! .expect_consistent(); +//! +//! assert_eq!(result.unwrap().first(), Some( +//! &alloy_rpc_types::Log { +//! inner: alloy_primitives::Log { +//! address: address!("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), +//! data: alloy_primitives::LogData::new( +//! vec![ +//! b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), +//! b256!("0x000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90"), +//! b256!("0x0000000000000000000000000000000aa232009084bd71a5797d089aa4edfad4"), +//! ], +//! bytes!("0x00000000000000000000000000000000000000000000000000000000cd566ae8"), +//! ).unwrap(), +//! }, +//! block_hash: Some(b256!("0x0bbd9b12140e674cdd55e63539a25df8280a70cee3676c94d8e05fa5f868a914")), +//! block_number: Some(0x161bd70_u64), +//! block_timestamp: None, +//! transaction_hash: Some(b256!("0xfe5bc88d0818b66a67b0619b1b4d81bfe38029e3799c7f0eb86b33ca7dc4c811")), +//! transaction_index: Some(0x0_u64), +//! log_index: Some(0x0_u64), +//! removed: false, +//! }, +//! )); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Overriding client configuration for a specific call +//! +//! Besides changing the amount of cycles for a particular call as described above, +//! it is sometimes desirable to have a custom configuration for a specific +//! call that is different from the one used by the client for all the other calls. +//! +//! For example, maybe for most calls, a 2 out-of 3 strategy is good enough, but for `eth_getLogs` +//! your application requires a higher threshold and more robustness with a 3-out-of-5 : +//! +//! ```rust +//! # // TODO XC-412: Use simpler example e.g. `eth_getBalance` +//! use alloy_primitives::{address, b256, bytes}; +//! use evm_rpc_client::EvmRpcClient; +//! use evm_rpc_types::{ConsensusStrategy, GetLogsRpcConfig , RpcServices}; +//! +//! # use evm_rpc_types::{Hex, Hex20, Hex32, MultiRpcResult}; +//! # use std::str::FromStr; +//! # #[tokio::main] +//! # async fn main() -> Result<(), Box> { +//! let client = EvmRpcClient::builder_for_ic() +//! # .with_default_stub_response(MultiRpcResult::Consistent(Ok(vec![ +//! # evm_rpc_types::LogEntry { +//! # address: Hex20::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(), +//! # topics: vec![ +//! # Hex32::from_str("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef").unwrap(), +//! # Hex32::from_str("0x000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90").unwrap(), +//! # Hex32::from_str("0x0000000000000000000000000000000aa232009084bd71a5797d089aa4edfad4").unwrap(), +//! # ], +//! # data: Hex::from_str("0x00000000000000000000000000000000000000000000000000000000cd566ae8").unwrap(), +//! # block_number: Some(0x161bd70_u64.into()), +//! # transaction_hash: Some(Hex32::from_str("0xfe5bc88d0818b66a67b0619b1b4d81bfe38029e3799c7f0eb86b33ca7dc4c811").unwrap()), +//! # transaction_index: Some(0x0_u64.into()), +//! # block_hash: Some(Hex32::from_str("0x0bbd9b12140e674cdd55e63539a25df8280a70cee3676c94d8e05fa5f868a914").unwrap()), +//! # log_index: Some(0x0_u64.into()), +//! # removed: false, +//! # } +//! # ]))) +//! .with_rpc_sources(RpcServices::EthMainnet(None)) +//! .with_consensus_strategy(ConsensusStrategy::Threshold { +//! total: Some(3), +//! min: 2, +//! }) +//! .build(); +//! +//! let result = client +//! .get_logs(vec![address!("0xdac17f958d2ee523a2206206994597c13d831ec7")]) +//! .with_rpc_config(GetLogsRpcConfig { +//! response_consensus: Some(ConsensusStrategy::Threshold { +//! total: Some(5), +//! min: 3, +//! }), +//! ..Default::default() +//! }) +//! .send() +//! .await +//! .expect_consistent(); +//! +//! assert_eq!(result.unwrap().first(), Some( +//! &alloy_rpc_types::Log { +//! inner: alloy_primitives::Log { +//! address: address!("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), +//! data: alloy_primitives::LogData::new( +//! vec![ +//! b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), +//! b256!("0x000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90"), +//! b256!("0x0000000000000000000000000000000aa232009084bd71a5797d089aa4edfad4"), +//! ], +//! bytes!("0x00000000000000000000000000000000000000000000000000000000cd566ae8"), +//! ).unwrap(), +//! }, +//! block_hash: Some(b256!("0x0bbd9b12140e674cdd55e63539a25df8280a70cee3676c94d8e05fa5f868a914")), +//! block_number: Some(0x161bd70_u64), +//! block_timestamp: None, +//! transaction_hash: Some(b256!("0xfe5bc88d0818b66a67b0619b1b4d81bfe38029e3799c7f0eb86b33ca7dc4c811")), +//! transaction_index: Some(0x0_u64), +//! log_index: Some(0x0_u64), +//! removed: false, +//! }, +//! )); +//! # Ok(()) +//! # } +//! ``` + +#![forbid(unsafe_code)] +#![forbid(missing_docs)] + +#[cfg(not(target_arch = "wasm32"))] +pub mod fixtures; +mod request; + +use crate::request::{Request, RequestBuilder}; +use async_trait::async_trait; +use candid::utils::ArgumentEncoder; +use candid::{CandidType, Principal}; +use evm_rpc_types::{ConsensusStrategy, GetLogsArgs, RpcConfig, RpcServices}; +use ic_cdk::api::call::RejectionCode as IcCdkRejectionCode; +use ic_error_types::RejectCode; +use request::{GetLogsRequest, GetLogsRequestBuilder}; +use serde::de::DeserializeOwned; +use std::sync::Arc; + +/// The principal identifying the productive EVM RPC canister under NNS control. +/// +/// ```rust +/// use candid::Principal; +/// use evm_rpc_client::EVM_RPC_CANISTER; +/// +/// assert_eq!(EVM_RPC_CANISTER, Principal::from_text("7hfb6-caaaa-aaaar-qadga-cai").unwrap()) +/// ``` +pub const EVM_RPC_CANISTER: Principal = Principal::from_slice(&[0, 0, 0, 0, 2, 48, 0, 204, 1, 1]); + +/// Abstract the canister runtime so that the client code can be reused: +/// * in production using `ic_cdk`, +/// * in unit tests by mocking this trait, +/// * in integration tests by implementing this trait for `PocketIc`. +#[async_trait] +pub trait Runtime { + /// Defines how asynchronous inter-canister update calls are made. + async fn update_call( + &self, + id: Principal, + method: &str, + args: In, + cycles: u128, + ) -> Result + where + In: ArgumentEncoder + Send, + Out: CandidType + DeserializeOwned; + + /// Defines how asynchronous inter-canister query calls are made. + async fn query_call( + &self, + id: Principal, + method: &str, + args: In, + ) -> Result + where + In: ArgumentEncoder + Send, + Out: CandidType + DeserializeOwned; +} + +/// Client to interact with the EVM RPC canister. +#[derive(Debug)] +pub struct EvmRpcClient { + config: Arc>, +} + +impl Clone for EvmRpcClient { + fn clone(&self) -> Self { + Self { + config: self.config.clone(), + } + } +} + +impl EvmRpcClient { + /// Creates a [`ClientBuilder`] to configure a [`EvmRpcClient`] targeting [`EVM_RPC_CANISTER`] + /// running on the Internet Computer. + pub fn builder_for_ic() -> ClientBuilder { + ClientBuilder::new(IcRuntime, EVM_RPC_CANISTER) + } +} + +/// Configuration for the EVM RPC canister client. +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct ClientConfig { + runtime: R, + evm_rpc_canister: Principal, + rpc_config: Option, + rpc_services: RpcServices, +} + +/// A [`ClientBuilder`] to create a [`EvmRpcClient`] with custom configuration. +#[must_use] +pub struct ClientBuilder { + config: ClientConfig, +} + +impl ClientBuilder { + fn new(runtime: R, evm_rpc_canister: Principal) -> Self { + Self { + config: ClientConfig { + runtime, + evm_rpc_canister, + rpc_config: None, + rpc_services: RpcServices::EthMainnet(None), + }, + } + } + + /// Modify the existing runtime by applying a transformation function. + /// + /// The transformation does not necessarily produce a runtime of the same type. + pub fn with_runtime S>(self, other_runtime: F) -> ClientBuilder { + ClientBuilder { + config: ClientConfig { + runtime: other_runtime(self.config.runtime), + evm_rpc_canister: self.config.evm_rpc_canister, + rpc_config: self.config.rpc_config, + rpc_services: self.config.rpc_services, + }, + } + } + + /// Mutates the builder to use the given [`RpcServices`]. + pub fn with_rpc_sources(mut self, rpc_services: RpcServices) -> Self { + self.config.rpc_services = rpc_services; + self + } + + /// Mutates the builder to use the given [`RpcConfig`]. + pub fn with_rpc_config(mut self, rpc_config: RpcConfig) -> Self { + self.config.rpc_config = Some(rpc_config); + self + } + + /// Mutates the builder to use the given [`ConsensusStrategy`] in the [`RpcConfig`]. + pub fn with_consensus_strategy(mut self, consensus_strategy: ConsensusStrategy) -> Self { + self.config.rpc_config = Some(RpcConfig { + response_consensus: Some(consensus_strategy), + ..self.config.rpc_config.unwrap_or_default() + }); + self + } + + /// Mutates the builder to use the given `response_size_estimate` in the [`RpcConfig`]. + pub fn with_response_size_estimate(mut self, response_size_estimate: u64) -> Self { + self.config.rpc_config = Some(RpcConfig { + response_size_estimate: Some(response_size_estimate), + ..self.config.rpc_config.unwrap_or_default() + }); + self + } + + /// Creates a [`EvmRpcClient`] from the configuration specified in the [`ClientBuilder`]. + pub fn build(self) -> EvmRpcClient { + EvmRpcClient { + config: Arc::new(self.config), + } + } +} + +impl EvmRpcClient { + /// Call `eth_getLogs` on the EVM RPC canister. + /// + /// # Examples + /// + /// ```rust + /// use alloy_primitives::{address, b256, bytes}; + /// use evm_rpc_client::EvmRpcClient; + /// + /// # use evm_rpc_types::{Hex, Hex20, Hex32, MultiRpcResult}; + /// # use std::str::FromStr; + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// let client = EvmRpcClient::builder_for_ic() + /// # .with_default_stub_response(MultiRpcResult::Consistent(Ok(vec![ + /// # evm_rpc_types::LogEntry { + /// # address: Hex20::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(), + /// # topics: vec![ + /// # Hex32::from_str("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef").unwrap(), + /// # Hex32::from_str("0x000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90").unwrap(), + /// # Hex32::from_str("0x0000000000000000000000000000000aa232009084bd71a5797d089aa4edfad4").unwrap(), + /// # ], + /// # data: Hex::from_str("0x00000000000000000000000000000000000000000000000000000000cd566ae8").unwrap(), + /// # block_number: Some(0x161bd70_u64.into()), + /// # transaction_hash: Some(Hex32::from_str("0xfe5bc88d0818b66a67b0619b1b4d81bfe38029e3799c7f0eb86b33ca7dc4c811").unwrap()), + /// # transaction_index: Some(0x0_u64.into()), + /// # block_hash: Some(Hex32::from_str("0x0bbd9b12140e674cdd55e63539a25df8280a70cee3676c94d8e05fa5f868a914").unwrap()), + /// # log_index: Some(0x0_u64.into()), + /// # removed: false, + /// # } + /// # ]))) + /// .build(); + /// + /// let result = client + /// .get_logs(vec![address!("0xdac17f958d2ee523a2206206994597c13d831ec7")]) + /// .with_cycles(10_000_000_000) + /// .send() + /// .await + /// .expect_consistent(); + /// + /// assert_eq!(result.unwrap().first(), Some( + /// &alloy_rpc_types::Log { + /// inner: alloy_primitives::Log { + /// address: address!("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), + /// data: alloy_primitives::LogData::new( + /// vec![ + /// b256!("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), + /// b256!("0x000000000000000000000000000000000004444c5dc75cb358380d2e3de08a90"), + /// b256!("0x0000000000000000000000000000000aa232009084bd71a5797d089aa4edfad4"), + /// ], + /// bytes!("0x00000000000000000000000000000000000000000000000000000000cd566ae8"), + /// ).unwrap(), + /// }, + /// block_hash: Some(b256!("0x0bbd9b12140e674cdd55e63539a25df8280a70cee3676c94d8e05fa5f868a914")), + /// block_number: Some(0x161bd70_u64), + /// block_timestamp: None, + /// transaction_hash: Some(b256!("0xfe5bc88d0818b66a67b0619b1b4d81bfe38029e3799c7f0eb86b33ca7dc4c811")), + /// transaction_index: Some(0x0_u64), + /// log_index: Some(0x0_u64), + /// removed: false, + /// }, + /// )); + /// # Ok(()) + /// # } + /// ``` + pub fn get_logs(&self, params: impl Into) -> GetLogsRequestBuilder { + RequestBuilder::new( + self.clone(), + GetLogsRequest::new(params.into()), + 10_000_000_000, + ) + } +} + +impl EvmRpcClient { + async fn execute_request( + &self, + request: Request, + ) -> Output + where + Config: CandidType + Send, + Params: CandidType + Send, + CandidOutput: Into + CandidType + DeserializeOwned, + { + let rpc_method = request.endpoint.rpc_method(); + self.try_execute_request(request) + .await + .unwrap_or_else(|e| panic!("Client error: failed to call `{}`: {e:?}", rpc_method)) + } + + async fn try_execute_request( + &self, + request: Request, + ) -> Result + where + Config: CandidType + Send, + Params: CandidType + Send, + CandidOutput: Into + CandidType + DeserializeOwned, + { + self.config + .runtime + .update_call::<(RpcServices, Option, Params), CandidOutput>( + self.config.evm_rpc_canister, + request.endpoint.rpc_method(), + (request.rpc_services, request.rpc_config, request.params), + request.cycles, + ) + .await + .map(Into::into) + } +} + +/// Runtime when interacting with a canister running on the Internet Computer. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct IcRuntime; + +#[async_trait] +impl Runtime for IcRuntime { + async fn update_call( + &self, + id: Principal, + method: &str, + args: In, + cycles: u128, + ) -> Result + where + In: ArgumentEncoder + Send, + Out: CandidType + DeserializeOwned, + { + ic_cdk::api::call::call_with_payment128(id, method, args, cycles) + .await + .map(|(res,)| res) + .map_err(|(code, message)| (convert_reject_code(code), message)) + } + + async fn query_call( + &self, + id: Principal, + method: &str, + args: In, + ) -> Result + where + In: ArgumentEncoder + Send, + Out: CandidType + DeserializeOwned, + { + ic_cdk::api::call::call(id, method, args) + .await + .map(|(res,)| res) + .map_err(|(code, message)| (convert_reject_code(code), message)) + } +} + +fn convert_reject_code(code: IcCdkRejectionCode) -> RejectCode { + match code { + IcCdkRejectionCode::SysFatal => RejectCode::SysFatal, + IcCdkRejectionCode::SysTransient => RejectCode::SysTransient, + IcCdkRejectionCode::DestinationInvalid => RejectCode::DestinationInvalid, + IcCdkRejectionCode::CanisterReject => RejectCode::CanisterReject, + IcCdkRejectionCode::CanisterError => RejectCode::CanisterError, + IcCdkRejectionCode::Unknown => { + // This can only happen if there is a new error code on ICP that the CDK is not aware of. + // We map it to SysFatal since none of the other error codes apply. + // In particular, note that RejectCode::SysUnknown is only applicable to inter-canister + // calls that used ic0.call_with_best_effort_response. + RejectCode::SysFatal + } + IcCdkRejectionCode::NoError => { + unreachable!("inter-canister calls should never produce a RejectionCode::NoError error") + } + } +} diff --git a/evm_rpc_client/src/request/mod.rs b/evm_rpc_client/src/request/mod.rs new file mode 100644 index 00000000..47c36a1f --- /dev/null +++ b/evm_rpc_client/src/request/mod.rs @@ -0,0 +1,333 @@ +use crate::{EvmRpcClient, Runtime}; +use candid::CandidType; +use evm_rpc_types::{ + BlockTag, GetLogsArgs, GetLogsRpcConfig, Hex20, Hex32, MultiRpcResult, RpcConfig, RpcServices, +}; +use ic_error_types::RejectCode; +use serde::de::DeserializeOwned; +use std::fmt::{Debug, Formatter}; +use strum::EnumIter; + +#[derive(Debug, Clone)] +pub struct GetLogsRequest(GetLogsArgs); + +impl GetLogsRequest { + pub fn new(params: GetLogsArgs) -> Self { + Self(params) + } +} + +impl EvmRpcRequest for GetLogsRequest { + type Config = GetLogsRpcConfig; + type Params = GetLogsArgs; + type CandidOutput = MultiRpcResult>; + type Output = MultiRpcResult>; + + fn endpoint(&self) -> EvmRpcEndpoint { + EvmRpcEndpoint::GetLogs + } + + fn params(self) -> Self::Params { + self.0 + } +} + +pub type GetLogsRequestBuilder = RequestBuilder< + R, + GetLogsRpcConfig, + GetLogsArgs, + MultiRpcResult>, + MultiRpcResult>, +>; + +impl GetLogsRequestBuilder { + /// Change the `from_block` parameter for an `eth_getLogs` request. + pub fn with_from_block(mut self, from_block: BlockTag) -> Self { + self.request.params.from_block = Some(from_block); + self + } + + /// Change the `to_block` parameter for an `eth_getLogs` request. + pub fn with_to_block(mut self, to_block: BlockTag) -> Self { + self.request.params.to_block = Some(to_block); + self + } + + /// Change the `addresses` parameter for an `eth_getLogs` request. + pub fn with_addresses(mut self, addresses: Vec) -> Self { + self.request.params.addresses = addresses; + self + } + + /// Change the `topics` parameter for an `eth_getLogs` request. + pub fn with_topics(mut self, topics: Vec>) -> Self { + self.request.params.topics = Some(topics); + self + } +} + +/// Ethereum RPC endpoint supported by the EVM RPC canister. +pub trait EvmRpcRequest { + /// Type of RPC config for that request. + type Config; + /// The type of parameters taken by this endpoint. + type Params; + /// The Candid type returned when executing this request which is then converted to [`Self::Output`]. + type CandidOutput; + /// The type returned by this endpoint. + type Output; + + /// The name of the endpoint on the EVM RPC canister. + fn endpoint(&self) -> EvmRpcEndpoint; + + /// Return the request parameters. + fn params(self) -> Self::Params; +} + +/// Endpoint on the EVM RPC canister triggering a call to EVM providers. +#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, EnumIter)] +pub enum EvmRpcEndpoint { + /// `eth_getLogs` endpoint. + GetLogs, +} + +impl EvmRpcEndpoint { + /// Method name on the EVM RPC canister + pub fn rpc_method(&self) -> &'static str { + match &self { + Self::GetLogs => "eth_getLogs", + } + } +} + +/// A builder to construct a [`Request`]. +/// +/// To construct a [`RequestBuilder`], refer to the [`EvmRpcClient`] documentation. +#[must_use = "RequestBuilder does nothing until you 'send' it"] +pub struct RequestBuilder { + client: EvmRpcClient, + request: Request, +} + +impl Clone + for RequestBuilder +{ + fn clone(&self) -> Self { + Self { + client: self.client.clone(), + request: self.request.clone(), + } + } +} + +impl Debug + for RequestBuilder +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let RequestBuilder { client, request } = &self; + f.debug_struct("RequestBuilder") + .field("client", client) + .field("request", request) + .finish() + } +} + +impl + RequestBuilder +{ + pub(super) fn new( + client: EvmRpcClient, + rpc_request: RpcRequest, + cycles: u128, + ) -> Self + where + RpcRequest: EvmRpcRequest< + Config = Config, + Params = Params, + CandidOutput = CandidOutput, + Output = Output, + >, + Config: From, + { + let endpoint = rpc_request.endpoint(); + let params = rpc_request.params(); + let request = Request { + endpoint, + rpc_services: client.config.rpc_services.clone(), + rpc_config: client.config.rpc_config.clone().map(Config::from), + params, + cycles, + _candid_marker: Default::default(), + _output_marker: Default::default(), + }; + RequestBuilder:: { client, request } + } + + /// Change the amount of cycles to send for that request. + pub fn with_cycles(mut self, cycles: u128) -> Self { + *self.request.cycles_mut() = cycles; + self + } + + /// Change the parameters to send for that request. + pub fn with_params(mut self, params: impl Into) -> Self { + *self.request.params_mut() = params.into(); + self + } + + /// Modify current parameters to send for that request. + pub fn modify_params(mut self, mutator: F) -> Self + where + F: FnOnce(&mut Params), + { + mutator(self.request.params_mut()); + self + } + + /// Change the RPC configuration to use for that request. + pub fn with_rpc_config(mut self, rpc_config: impl Into) -> Self { + *self.request.rpc_config_mut() = Some(rpc_config.into()); + self + } +} + +impl + RequestBuilder +{ + /// Constructs the [`Request`] and sends it using the [`EvmRpcClient`] returning the response. + /// + /// # Panics + /// + /// If the request was not successful. + pub async fn send(self) -> Output + where + Config: CandidType + Send, + Params: CandidType + Send, + CandidOutput: Into + CandidType + DeserializeOwned, + { + self.client + .execute_request::(self.request) + .await + } + + /// Constructs the [`Request`] and sends it using the [`EvmRpcClient`]. This method returns + /// either the request response or any error that occurs while sending the request. + pub async fn try_send(self) -> Result + where + Config: CandidType + Send, + Params: CandidType + Send, + CandidOutput: Into + CandidType + DeserializeOwned, + { + self.client + .try_execute_request::(self.request) + .await + } +} + +impl + RequestBuilder +{ + /// Change the max block range error for `eth_getLogs` request. + pub fn with_max_block_range(mut self, max_block_range: u32) -> Self { + let config = self.request.rpc_config_mut().get_or_insert_default(); + config.max_block_range = Some(max_block_range); + self + } +} + +/// A request which can be executed with `EvmRpcClient::execute_request` or `EvmRpcClient::execute_query_request`. +pub struct Request { + pub(super) endpoint: EvmRpcEndpoint, + pub(super) rpc_services: RpcServices, + pub(super) rpc_config: Option, + pub(super) params: Params, + pub(super) cycles: u128, + pub(super) _candid_marker: std::marker::PhantomData, + pub(super) _output_marker: std::marker::PhantomData, +} + +impl Debug + for Request +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let Request { + endpoint, + rpc_services, + rpc_config, + params, + cycles, + _candid_marker, + _output_marker, + } = &self; + f.debug_struct("Request") + .field("endpoint", endpoint) + .field("rpc_services", rpc_services) + .field("rpc_config", rpc_config) + .field("params", params) + .field("cycles", cycles) + .field("_candid_marker", _candid_marker) + .field("_output_marker", _output_marker) + .finish() + } +} + +impl PartialEq + for Request +{ + fn eq( + &self, + Request { + endpoint, + rpc_services, + rpc_config, + params, + cycles, + _candid_marker, + _output_marker, + }: &Self, + ) -> bool { + &self.endpoint == endpoint + && &self.rpc_services == rpc_services + && &self.rpc_config == rpc_config + && &self.params == params + && &self.cycles == cycles + && &self._candid_marker == _candid_marker + && &self._output_marker == _output_marker + } +} + +impl Clone + for Request +{ + fn clone(&self) -> Self { + Self { + endpoint: self.endpoint.clone(), + rpc_services: self.rpc_services.clone(), + rpc_config: self.rpc_config.clone(), + params: self.params.clone(), + cycles: self.cycles, + _candid_marker: self._candid_marker, + _output_marker: self._output_marker, + } + } +} + +impl Request { + /// Get a mutable reference to the cycles. + #[inline] + pub fn cycles_mut(&mut self) -> &mut u128 { + &mut self.cycles + } + + /// Get a mutable reference to the RPC configuration. + #[inline] + pub fn rpc_config_mut(&mut self) -> &mut Option { + &mut self.rpc_config + } + + /// Get a mutable reference to the request parameters. + #[inline] + pub fn params_mut(&mut self) -> &mut Params { + &mut self.params + } +} diff --git a/evm_rpc_types/Cargo.toml b/evm_rpc_types/Cargo.toml index 7d6ff179..879e96b9 100644 --- a/evm_rpc_types/Cargo.toml +++ b/evm_rpc_types/Cargo.toml @@ -11,6 +11,8 @@ repository = "https://github.com/dfinity/evm-rpc-canister" documentation = "https://docs.rs/evm_rpc_types" [dependencies] +alloy-primitives = { workspace = true, optional = true } +alloy-rpc-types = { workspace = true, optional = true } candid = { workspace = true } canlog = { workspace = true } hex = { workspace = true } @@ -23,4 +25,9 @@ thiserror = { workspace = true } url = { workspace = true } [dev-dependencies] -proptest = { workspace = true } \ No newline at end of file +proptest = { workspace = true } +serde_json = { workspace = true } + +[features] +default = ["alloy"] +alloy = ["dep:alloy-primitives", "dep:alloy-rpc-types"] \ No newline at end of file diff --git a/evm_rpc_types/src/alloy.rs b/evm_rpc_types/src/alloy.rs new file mode 100644 index 00000000..f6e09a1f --- /dev/null +++ b/evm_rpc_types/src/alloy.rs @@ -0,0 +1,37 @@ +use crate::{Hex, Hex20, Hex32}; + +impl From for alloy_primitives::Address { + fn from(value: Hex20) -> Self { + Self::from(<[u8; 20]>::from(value)) + } +} + +impl From for Hex20 { + fn from(value: alloy_primitives::Address) -> Self { + Self::from(value.into_array()) + } +} + +impl From for alloy_primitives::B256 { + fn from(value: Hex32) -> Self { + Self::from(<[u8; 32]>::from(value)) + } +} + +impl From for Hex32 { + fn from(value: alloy_primitives::B256) -> Self { + Self::from(value.0) + } +} + +impl From for alloy_primitives::Bytes { + fn from(value: Hex) -> Self { + Self::from_iter(Vec::::from(value)) + } +} + +impl From for Hex { + fn from(value: alloy_primitives::Bytes) -> Self { + Hex(value.to_vec()) + } +} diff --git a/evm_rpc_types/src/lib.rs b/evm_rpc_types/src/lib.rs index a8b016d2..8a6192c5 100644 --- a/evm_rpc_types/src/lib.rs +++ b/evm_rpc_types/src/lib.rs @@ -1,6 +1,8 @@ #[cfg(test)] mod tests; +#[cfg(feature = "alloy")] +mod alloy; mod lifecycle; mod request; mod response; @@ -48,6 +50,15 @@ impl Debug for Nat256 { } } +impl TryFrom for u64 { + type Error = RpcError; + + fn try_from(value: Nat256) -> Result { + u64::try_from(value.0 .0) + .map_err(|e| RpcError::ValidationError(ValidationError::Custom(format!("{:?}", e)))) + } +} + impl Nat256 { pub const ZERO: Nat256 = Nat256(Nat(BigUint::ZERO)); diff --git a/evm_rpc_types/src/request/alloy.rs b/evm_rpc_types/src/request/alloy.rs new file mode 100644 index 00000000..c6a0bab4 --- /dev/null +++ b/evm_rpc_types/src/request/alloy.rs @@ -0,0 +1,43 @@ +use crate::{BlockTag, GetLogsArgs, Hex20, RpcError}; + +impl From for BlockTag { + fn from(tag: alloy_rpc_types::BlockNumberOrTag) -> Self { + use alloy_rpc_types::BlockNumberOrTag; + match tag { + BlockNumberOrTag::Latest => Self::Latest, + BlockNumberOrTag::Finalized => Self::Finalized, + BlockNumberOrTag::Safe => Self::Safe, + BlockNumberOrTag::Earliest => Self::Earliest, + BlockNumberOrTag::Pending => Self::Pending, + BlockNumberOrTag::Number(n) => Self::Number(n.into()), + } + } +} + +impl TryFrom for alloy_rpc_types::BlockNumberOrTag { + type Error = RpcError; + + fn try_from(tag: BlockTag) -> Result { + Ok(match tag { + BlockTag::Latest => Self::Latest, + BlockTag::Finalized => Self::Finalized, + BlockTag::Safe => Self::Safe, + BlockTag::Earliest => Self::Earliest, + BlockTag::Pending => Self::Pending, + BlockTag::Number(n) => Self::Number(u64::try_from(n)?), + }) + } +} + +impl, S: Into> From for GetLogsArgs { + fn from(addresses: T) -> Self { + Self { + from_block: None, + to_block: None, + addresses: addresses.into_iter().map(Into::into).collect(), + topics: None, + } + } +} + +// TODO XC-412: impl From for GetLogsArgs diff --git a/evm_rpc_types/src/request/mod.rs b/evm_rpc_types/src/request/mod.rs index f654a0f0..9432cc0b 100644 --- a/evm_rpc_types/src/request/mod.rs +++ b/evm_rpc_types/src/request/mod.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "alloy")] +mod alloy; + use crate::{Hex, Hex20, Hex32, HexByte, Nat256}; use candid::CandidType; use serde::Deserialize; diff --git a/evm_rpc_types/src/response/alloy.rs b/evm_rpc_types/src/response/alloy.rs new file mode 100644 index 00000000..7f59008b --- /dev/null +++ b/evm_rpc_types/src/response/alloy.rs @@ -0,0 +1,31 @@ +use crate::{LogEntry, RpcError, ValidationError}; + +impl TryFrom for alloy_rpc_types::Log { + type Error = RpcError; + + fn try_from(entry: LogEntry) -> Result { + Ok(Self { + inner: alloy_primitives::Log { + address: alloy_primitives::Address::from(entry.address), + data: alloy_primitives::LogData::new( + entry + .topics + .into_iter() + .map(alloy_primitives::B256::from) + .collect(), + alloy_primitives::Bytes::from(entry.data), + ) + .ok_or(RpcError::ValidationError(ValidationError::Custom( + "Invalid log data".to_string(), + )))?, + }, + block_hash: entry.block_hash.map(alloy_primitives::BlockHash::from), + block_number: entry.block_number.map(u64::try_from).transpose()?, + block_timestamp: None, + transaction_hash: entry.transaction_hash.map(alloy_primitives::TxHash::from), + transaction_index: entry.transaction_index.map(u64::try_from).transpose()?, + log_index: entry.log_index.map(u64::try_from).transpose()?, + removed: entry.removed, + }) + } +} diff --git a/evm_rpc_types/src/response/mod.rs b/evm_rpc_types/src/response/mod.rs index a9fb2991..b184fbf2 100644 --- a/evm_rpc_types/src/response/mod.rs +++ b/evm_rpc_types/src/response/mod.rs @@ -1,3 +1,9 @@ +#[cfg(test)] +mod test; + +#[cfg(feature = "alloy")] +mod alloy; + use crate::{Hex, Hex20, Hex256, Hex32, HexByte, Nat256}; use candid::CandidType; use serde::{Deserialize, Serialize}; diff --git a/evm_rpc_types/src/response/test.rs b/evm_rpc_types/src/response/test.rs new file mode 100644 index 00000000..d76ffe0d --- /dev/null +++ b/evm_rpc_types/src/response/test.rs @@ -0,0 +1,94 @@ +use crate::{Hex, Hex20, Hex32}; +use proptest::prelude::Strategy; +use proptest::proptest; +use std::ops::RangeInclusive; + +#[cfg(feature = "alloy")] +mod alloy_conversion_tests { + use super::*; + use crate::{LogEntry, Nat256}; + use num_bigint::BigUint; + use proptest::arbitrary::any; + use proptest::option; + use serde_json::Value; + use std::str::FromStr; + + proptest! { + #[test] + fn should_convert_from_alloy(entry in arb_log_entry()) { + // Convert a number serialized as a hexadecimal string into an array of u32 digits. + // This is needed to compare a serialized `alloy_rpc_types::Log` with an + // `evm_rpc_types::LogEntry` since `transactionIndex`, `logIndex` and `blockNumber` get + // serialized as hex strings by alloy but as integers in `evm_rpc_types`. + fn hex_to_u32_digits(serialized: &mut Value, field: &str) { + if let Some(Value::String(hex)) = serialized.get(field) { + let hex = hex.strip_prefix("0x").unwrap_or(hex); + let digits = BigUint::parse_bytes(hex.as_bytes(), 16).unwrap().to_u32_digits(); + serialized[field] = digits.into(); + } + } + + let serialized = serde_json::to_value(&entry).unwrap(); + + let mut alloy_serialized = serde_json::to_value(&alloy_rpc_types::Log::try_from(entry.clone()).unwrap()).unwrap(); + hex_to_u32_digits(&mut alloy_serialized, "transactionIndex"); + hex_to_u32_digits(&mut alloy_serialized, "logIndex"); + hex_to_u32_digits(&mut alloy_serialized, "blockNumber"); + + assert_eq!(serialized, alloy_serialized); + } + } + + fn arb_log_entry() -> impl Strategy { + ( + arb_hex20(), + arb_hex(), + option::of(any::().prop_map(Nat256::from)), + option::of(arb_hex32()), + option::of(any::().prop_map(Nat256::from)), + option::of(arb_hex32()), + option::of(any::().prop_map(Nat256::from)), + any::(), + ) + .prop_map( + |( + address, + data, + block_number, + transaction_hash, + transaction_index, + block_hash, + log_index, + removed, + )| LogEntry { + address, + topics: vec![], + data, + block_number, + transaction_hash, + transaction_index, + block_hash, + log_index, + removed, + }, + ) + } + + fn arb_hex20() -> impl Strategy { + arb_var_len_hex_string(20..=20_usize).prop_map(|s| Hex20::from_str(s.as_str()).unwrap()) + } + + fn arb_hex32() -> impl Strategy { + arb_var_len_hex_string(32..=32_usize).prop_map(|s| Hex32::from_str(s.as_str()).unwrap()) + } + + fn arb_hex() -> impl Strategy { + arb_var_len_hex_string(0..=100_usize).prop_map(|s| Hex::from_str(s.as_str()).unwrap()) + } +} + +fn arb_var_len_hex_string(num_bytes_range: RangeInclusive) -> impl Strategy { + num_bytes_range.prop_flat_map(|num_bytes| { + proptest::string::string_regex(&format!("0x[0-9a-fA-F]{{{}}}", 2 * num_bytes)).unwrap() + }) +} diff --git a/evm_rpc_types/src/result/alloy.rs b/evm_rpc_types/src/result/alloy.rs new file mode 100644 index 00000000..33655c29 --- /dev/null +++ b/evm_rpc_types/src/result/alloy.rs @@ -0,0 +1,11 @@ +use crate::{LogEntry, MultiRpcResult}; + +impl From>> for MultiRpcResult> { + fn from(result: MultiRpcResult>) -> Self { + result.and_then(|logs| { + logs.into_iter() + .map(alloy_rpc_types::Log::try_from) + .collect() + }) + } +} diff --git a/evm_rpc_types/src/result/mod.rs b/evm_rpc_types/src/result/mod.rs index 5c87b98d..4c3481bd 100644 --- a/evm_rpc_types/src/result/mod.rs +++ b/evm_rpc_types/src/result/mod.rs @@ -1,6 +1,9 @@ #[cfg(test)] mod tests; +#[cfg(feature = "alloy")] +mod alloy; + use crate::RpcService; use candid::{CandidType, Deserialize}; use ic_error_types::RejectCode; @@ -38,6 +41,29 @@ impl MultiRpcResult { .collapse(), } } + + /// Maps a [`MultiRpcResult`] containing values of type `T` to a [`MultiRpcResult`] containing + /// values of type `R` by a fallible map. + pub fn and_then(self, mut f: impl FnMut(T) -> RpcResult) -> MultiRpcResult { + match self { + MultiRpcResult::Consistent(result) => MultiRpcResult::Consistent(result.and_then(f)), + MultiRpcResult::Inconsistent(results) => MultiRpcResult::Inconsistent( + results + .into_iter() + .map(|(service, result)| { + ( + service, + match result { + Ok(ok) => f(ok), + Err(err) => Err(err), + }, + ) + }) + .collect(), + ) + .collapse(), + } + } } impl MultiRpcResult { diff --git a/evm_rpc_types/src/result/tests.rs b/evm_rpc_types/src/result/tests.rs index 9e1e6524..d4f6aafa 100644 --- a/evm_rpc_types/src/result/tests.rs +++ b/evm_rpc_types/src/result/tests.rs @@ -1,5 +1,5 @@ use crate::result::{ProviderError, RpcError}; -use crate::{EthMainnetService, MultiRpcResult, RpcService}; +use crate::{EthMainnetService, MultiRpcResult, RpcService, ValidationError}; #[test] fn test_multi_rpc_result_map() { @@ -73,63 +73,131 @@ fn test_multi_rpc_result_map() { .map(|n| n / 2), MultiRpcResult::Consistent(Ok(1)) ); + assert_eq!( + MultiRpcResult::Inconsistent(vec![ + ( + RpcService::EthMainnet(EthMainnetService::Ankr), + Err(RpcError::ValidationError(ValidationError::Custom( + "error message".into() + ))) + ), + ( + RpcService::EthMainnet(EthMainnetService::Llama), + Err(RpcError::ValidationError(ValidationError::Custom( + "error message".into() + ))) + ) + ]) + .and_then(|()| unreachable!()), + MultiRpcResult::Consistent::<()>(Err(RpcError::ValidationError(ValidationError::Custom( + "error message".into() + )))) + ); } - #[test] -fn test_multi_rpc_result_collapse() { +fn test_multi_rpc_result_and_then() { let err = RpcError::ProviderError(ProviderError::ProviderNotFound); assert_eq!( - MultiRpcResult::Consistent(Ok(5)).collapse(), - MultiRpcResult::Consistent(Ok(5)) + MultiRpcResult::Consistent(Ok(5)).and_then(|n| Ok(n + 1)), + MultiRpcResult::Consistent(Ok(6)) + ); + assert_eq!( + MultiRpcResult::Consistent(Err(err.clone())).and_then(|()| unreachable!()), + MultiRpcResult::Consistent::<()>(Err(err.clone())) ); assert_eq!( MultiRpcResult::Inconsistent(vec![ - (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(2)), - (RpcService::EthMainnet(EthMainnetService::Llama), Ok(3)) + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5)), + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(6)) ]) - .collapse(), + .and_then(|n| Ok(n + 1)), MultiRpcResult::Inconsistent(vec![ - (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(2)), - (RpcService::EthMainnet(EthMainnetService::Llama), Ok(3)) + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(6)), + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(7)) ]) ); assert_eq!( MultiRpcResult::Inconsistent(vec![ + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5)), ( - RpcService::EthMainnet(EthMainnetService::Ankr), + RpcService::EthMainnet(EthMainnetService::Cloudflare), + Ok(10) + ) + ]) + .and_then(|n| Ok(n + 1)), + MultiRpcResult::Inconsistent(vec![ + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(6)), + ( + RpcService::EthMainnet(EthMainnetService::Cloudflare), + Ok(11) + ) + ]) + ); + assert_eq!( + MultiRpcResult::Inconsistent(vec![ + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(5)), + ( + RpcService::EthMainnet(EthMainnetService::PublicNode), Err(err.clone()) - ), + ) + ]) + .and_then(|n| Ok(n + 1)), + MultiRpcResult::Inconsistent(vec![ + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(6)), + ( + RpcService::EthMainnet(EthMainnetService::PublicNode), + Err(err.clone()) + ) + ]) + ); + assert_eq!( + MultiRpcResult::Inconsistent(vec![ + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(1)), (RpcService::EthMainnet(EthMainnetService::Llama), Ok(2)) ]) - .collapse(), + .and_then(|n| if n % 2 == 0 { Ok(n) } else { Err(err.clone()) }), MultiRpcResult::Inconsistent(vec![ ( RpcService::EthMainnet(EthMainnetService::Ankr), Err(err.clone()) ), - (RpcService::EthMainnet(EthMainnetService::Llama), Ok(2)) + (RpcService::EthMainnet(EthMainnetService::Llama), Ok(2)), ]) ); + assert_eq!( + MultiRpcResult::Inconsistent(vec![ + (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(1)), + (RpcService::EthMainnet(EthMainnetService::Llama), Ok(3)) + ]) + .and_then(|n| if n % 2 == 0 { Ok(n) } else { Err(err.clone()) }), + MultiRpcResult::Consistent(Err(err.clone())) + ); assert_eq!( MultiRpcResult::Inconsistent(vec![ (RpcService::EthMainnet(EthMainnetService::Ankr), Ok(2)), - (RpcService::EthMainnet(EthMainnetService::Llama), Ok(2)) + (RpcService::EthMainnet(EthMainnetService::Llama), Ok(3)) ]) - .collapse(), - MultiRpcResult::Consistent(Ok(2)) + .and_then(|n| Ok(n / 2)), + MultiRpcResult::Consistent(Ok(1)) ); assert_eq!( - MultiRpcResult::Inconsistent::<()>(vec![ + MultiRpcResult::Inconsistent(vec![ ( RpcService::EthMainnet(EthMainnetService::Ankr), - Err(err.clone()) + Err(RpcError::ValidationError(ValidationError::Custom( + "error message".into() + ))) ), ( RpcService::EthMainnet(EthMainnetService::Llama), - Err(err.clone()) + Err(RpcError::ValidationError(ValidationError::Custom( + "error message".into() + ))) ) ]) - .collapse(), - MultiRpcResult::Consistent::<()>(Err(err.clone())) + .and_then(|()| unreachable!()), + MultiRpcResult::Consistent::<()>(Err(RpcError::ValidationError(ValidationError::Custom( + "error message".into() + )))) ); } diff --git a/evm_rpc_types/src/rpc_client/mod.rs b/evm_rpc_types/src/rpc_client/mod.rs index 6179638b..747a879a 100644 --- a/evm_rpc_types/src/rpc_client/mod.rs +++ b/evm_rpc_types/src/rpc_client/mod.rs @@ -37,6 +37,16 @@ impl From for RpcConfig { } } +impl From for GetLogsRpcConfig { + fn from(config: RpcConfig) -> Self { + Self { + response_size_estimate: config.response_size_estimate, + response_consensus: config.response_consensus, + max_block_range: None, + } + } +} + impl GetLogsRpcConfig { pub fn max_block_range_or_default(&self) -> u32 { const DEFAULT_ETH_GET_LOGS_MAX_BLOCK_RANGE: u32 = 500; diff --git a/evm_rpc_types/src/tests.rs b/evm_rpc_types/src/tests.rs index 890cfa6e..1bccb8d8 100644 --- a/evm_rpc_types/src/tests.rs +++ b/evm_rpc_types/src/tests.rs @@ -1,8 +1,15 @@ +use crate::{Hex, Hex20, Hex256, Hex32, HexByte, Nat256}; +use candid::{CandidType, Decode, Encode, Nat}; +use num_bigint::BigUint; +use proptest::{ + prelude::{any, Strategy, TestCaseError}, + prop_assert, prop_assert_eq, proptest, +}; +use serde::de::DeserializeOwned; +use std::{ops::RangeInclusive, str::FromStr}; + mod nat256 { - use crate::Nat256; - use candid::{Decode, Encode, Nat}; - use num_bigint::BigUint; - use proptest::{arbitrary::any, prelude::Strategy, proptest}; + use super::*; proptest! { #[test] @@ -68,13 +75,7 @@ mod nat256 { } mod hex_string { - use crate::{Hex, Hex20, Hex256, Hex32, HexByte}; - use candid::{CandidType, Decode, Encode}; - use proptest::prelude::{Strategy, TestCaseError}; - use proptest::{prop_assert, prop_assert_eq, proptest}; - use serde::de::DeserializeOwned; - use std::ops::RangeInclusive; - use std::str::FromStr; + use super::*; proptest! { #[test] @@ -179,12 +180,37 @@ mod hex_string { ); Ok(()) } +} + +#[cfg(feature = "alloy")] +mod alloy_conversion_tests { + use super::*; + use alloy_primitives::{Address, Bytes, B256}; + + proptest! { + #[test] + fn should_convert_to_and_from_alloy(hex20 in arb_hex20(), hex32 in arb_hex32(), hex in arb_hex()) { + prop_assert_eq!(hex20.clone(), Hex20::from(Address::from(hex20))); + prop_assert_eq!(hex32.clone(), Hex32::from(B256::from(hex32))); + prop_assert_eq!(hex.clone(), Hex::from(Bytes::from(hex))); + } + } + + fn arb_hex20() -> impl Strategy { + arb_var_len_hex_string(20..=20_usize).prop_map(|s| Hex20::from_str(s.as_str()).unwrap()) + } - fn arb_var_len_hex_string( - num_bytes_range: RangeInclusive, - ) -> impl Strategy { - num_bytes_range.prop_flat_map(|num_bytes| { - proptest::string::string_regex(&format!("0x[0-9a-fA-F]{{{}}}", 2 * num_bytes)).unwrap() - }) + fn arb_hex32() -> impl Strategy { + arb_var_len_hex_string(32..=32_usize).prop_map(|s| Hex32::from_str(s.as_str()).unwrap()) } + + fn arb_hex() -> impl Strategy { + arb_var_len_hex_string(0..=100_usize).prop_map(|s| Hex::from_str(s.as_str()).unwrap()) + } +} + +fn arb_var_len_hex_string(num_bytes_range: RangeInclusive) -> impl Strategy { + num_bytes_range.prop_flat_map(|num_bytes| { + proptest::string::string_regex(&format!("0x[0-9a-fA-F]{{{}}}", 2 * num_bytes)).unwrap() + }) }