diff --git a/.gitignore b/.gitignore index f6e8d30..4216bac 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ sdk/tests/move/object_crawler_test/Move.lock sdk/tests/move/onchain_tool_test/build sdk/tests/move/onchain_tool_test/Move.lock +# Move build output (onchain tools) +onchain/**/build/ + # Environment .env diff --git a/offchain/Cargo.lock b/offchain/Cargo.lock index 3e7e945..54d7022 100644 --- a/offchain/Cargo.lock +++ b/offchain/Cargo.lock @@ -52,6 +52,143 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "alloy-dyn-abi" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf69d3061e2e908a4370bda5d8d6529d5080232776975489eec0b49ce971027e" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow", +] + +[[package]] +name = "alloy-json-abi" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4584e3641181ff073e9d5bec5b3b8f78f9749d9fb108a1cfbc4399a4a139c72a" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777d58b30eb9a4db0e5f59bc30e8c2caef877fee7dc8734cf242a51a60f22e05" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "foldhash 0.1.5", + "hashbrown 0.15.5", + "indexmap", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.8.5", + "ruint", + "rustc-hash", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" +dependencies = [ + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e68b32b6fa0d09bb74b4cefe35ccc8269d711c26629bc7cd98a47eeb12fe353f" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2afe6879ac373e58fd53581636f2cce843998ae0b058ebe1e4f649195e2bd23c" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.111", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ba01aee235a8c699d07e5be97ba215607564e71be72f433665329bec307d28" +dependencies = [ + "const-hex", + "dunce", + "heck", + "macro-string", + "proc-macro2", + "quote", + "syn 2.0.111", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c13fc168b97411e04465f03e632f31ef94cad1c7c8951bf799237fd7870d535" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e960c4b52508ef2ae1e37cae5058e905e9ae099b107900067a503f8c454036f" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -117,6 +254,201 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[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" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint", + "num-traits", + "paste", + "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-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.111", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[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-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-std 0.5.0", + "arrayvec", + "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 = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -172,7 +504,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -181,6 +513,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -302,6 +645,18 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake2" version = "0.10.6" @@ -377,6 +732,12 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + [[package]] name = "bytecount" version = "0.6.9" @@ -400,6 +761,9 @@ name = "bytes" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +dependencies = [ + "serde", +] [[package]] name = "bytestring" @@ -434,6 +798,26 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chains-evm" +version = "1.0.0" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "hex", + "mockito", + "nexus-sdk", + "nexus-toolkit", + "reqwest 0.12.25", + "schemars", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "toml", +] + [[package]] name = "chrono" version = "0.4.44" @@ -500,6 +884,18 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" +[[package]] +name = "const-hex" +version = "1.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e2a781ebdf4467d1428dc4593067825fb646f6871475098d8577421af73558" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "proptest", + "serde_core", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -512,6 +908,36 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +[[package]] +name = "const_format" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -565,6 +991,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -607,7 +1039,7 @@ dependencies = [ "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "rustc_version", + "rustc_version 0.4.1", "subtle", "zeroize", ] @@ -620,7 +1052,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -644,7 +1076,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.111", ] [[package]] @@ -655,7 +1087,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -684,6 +1116,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_builder" version = "0.20.2" @@ -702,7 +1145,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -712,7 +1155,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn", + "syn 2.0.111", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "syn 2.0.111", + "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]] @@ -746,7 +1221,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -805,6 +1280,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "either" version = "1.15.0" @@ -849,6 +1336,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-ordinalize" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "enum_dispatch" version = "0.3.13" @@ -858,7 +1365,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -956,6 +1463,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", + "auto_impl", + "bytes", +] + +[[package]] +name = "fastrlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + [[package]] name = "ff" version = "0.13.1" @@ -978,6 +1507,18 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fixedbitset" version = "0.5.7" @@ -1089,6 +1630,12 @@ dependencies = [ "libc", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.31" @@ -1145,7 +1692,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1297,6 +1844,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "foldhash 0.1.5", + "serde", ] [[package]] @@ -1709,6 +2257,26 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "indexmap" version = "2.12.1" @@ -1760,17 +2328,35 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" name = "iri-string" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ - "memchr", - "serde", + "either", ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.2" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] [[package]] name = "itertools" @@ -1808,7 +2394,7 @@ checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1836,9 +2422,9 @@ checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" dependencies = [ "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.1", "simd_cesu8", - "syn", + "syn 2.0.111", ] [[package]] @@ -1857,7 +2443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -1945,9 +2531,44 @@ dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", + "once_cell", "sha2 0.10.9", ] +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures 0.2.17", +] + +[[package]] +name = "keccak-asm" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1766b89733097006f3a1388a02849865d6bc98c89273cb622e29fdd209922183" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "kqueue" version = "1.1.1" @@ -1988,7 +2609,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn", + "syn 2.0.111", ] [[package]] @@ -2009,6 +2630,12 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -2067,6 +2694,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.111", +] + [[package]] name = "math" version = "1.0.0" @@ -2420,6 +3058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2456,7 +3095,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2493,7 +3132,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2538,6 +3177,34 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -2561,6 +3228,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -2576,6 +3249,16 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + [[package]] name = "petgraph" version = "0.7.1" @@ -2603,7 +3286,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2689,7 +3372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.111", ] [[package]] @@ -2701,6 +3384,17 @@ dependencies = [ "elliptic-curve", ] +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + [[package]] name = "proc-macro-crate" version = "3.4.0" @@ -2733,6 +3427,28 @@ dependencies = [ "version_check", ] +[[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.111", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -2757,6 +3473,25 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.10.0", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + [[package]] name = "prost" version = "0.14.1" @@ -2774,10 +3509,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" dependencies = [ "anyhow", - "itertools", + "itertools 0.14.0", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -2809,6 +3544,12 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quinn" version = "0.11.9" @@ -2885,6 +3626,12 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -2894,6 +3641,7 @@ dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", + "serde", ] [[package]] @@ -2944,6 +3692,15 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -2970,7 +3727,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3167,6 +3924,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + [[package]] name = "roaring" version = "0.11.2" @@ -3201,24 +3968,73 @@ dependencies = [ "quote", "regex", "relative-path", - "rustc_version", - "syn", + "rustc_version 0.4.1", + "syn 2.0.111", "unicode-ident", ] +[[package]] +name = "ruint" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0298da754d1395046b0afdc2f20ee76d29a8ae310cd30ffa84ed42acba9cb12a" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "ark-ff 0.5.0", + "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_core", + "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-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.27", ] [[package]] @@ -3317,6 +4133,18 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.20" @@ -3372,7 +4200,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 2.0.111", ] [[package]] @@ -3440,12 +4268,30 @@ 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.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +[[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.228" @@ -3473,7 +4319,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3484,7 +4330,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3569,7 +4415,7 @@ checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3605,6 +4451,26 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sha3-asm" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3f15d4e239ebe08413eed880e0f9b5af4b40ee0472543320efa91d488e96a7" +dependencies = [ + "cc", + "cfg-if", +] + [[package]] name = "shlex" version = "1.3.0" @@ -3642,7 +4508,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" dependencies = [ - "rustc_version", + "rustc_version 0.4.1", "simdutf8", ] @@ -3731,6 +4597,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" @@ -3752,7 +4624,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3812,7 +4684,7 @@ dependencies = [ "bs58", "bytes", "bytestring", - "itertools", + "itertools 0.14.0", "rand_core 0.6.4", "roaring", "serde", @@ -3837,6 +4709,17 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.111" @@ -3848,6 +4731,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4e6eed052a117409a1a744c8bda9c3ea6934597cf7419f791cb7d590871c4c" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -3865,7 +4760,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3948,7 +4843,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3959,7 +4854,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -3981,6 +4876,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.8.2" @@ -4031,7 +4935,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4275,7 +5179,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4318,6 +5222,30 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicase" version = "2.8.1" @@ -4336,6 +5264,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -4400,6 +5334,12 @@ dependencies = [ "vsimd", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "vcpkg" version = "0.2.15" @@ -4418,6 +5358,15 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -4550,7 +5499,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.111", "wasm-bindgen-shared", ] @@ -4607,7 +5556,7 @@ dependencies = [ "bitflags 2.10.0", "hashbrown 0.15.5", "indexmap", - "semver", + "semver 1.0.27", ] [[package]] @@ -4678,7 +5627,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4689,7 +5638,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -4937,7 +5886,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn", + "syn 2.0.111", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -4953,7 +5902,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.111", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -4987,7 +5936,7 @@ dependencies = [ "id-arena", "indexmap", "log", - "semver", + "semver 1.0.27", "serde", "serde_derive", "serde_json", @@ -5001,6 +5950,15 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "yoke" version = "0.8.1" @@ -5020,7 +5978,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", "synstructure", ] @@ -5041,7 +5999,7 @@ checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -5061,7 +6019,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", "synstructure", ] @@ -5082,7 +6040,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] @@ -5115,7 +6073,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.111", ] [[package]] diff --git a/onchain/README.md b/onchain/README.md index 0481939..7bc6932 100644 --- a/onchain/README.md +++ b/onchain/README.md @@ -1,6 +1,21 @@ # onchain/ -Reserved for onchain (Move / Sui package) tools and their CI. Empty -for now — see the offchain CI pipeline spec in -[docs/superpowers/specs/2026-05-19-offchain-tools-pipeline-design.md](../docs/superpowers/specs/2026-05-19-offchain-tools-pipeline-design.md) -for context. +Onchain (Move / Sui package) Nexus tools. Each tool is a self-contained Move +package with `sources/`, `tests/`, and canonical `schemas/`. + +## Tools + +- [`evm-signin`](evm-signin/README.md) — `xyz.taluslabs.evm.verify-signature@1`. + Verifies an Ethereum secp256k1 signature onchain and recovers the signer's + `0x` address. Ships `evm_access` for replay-safe MetaMask → Sui identity + binding (`EvmIdentityPass` + `EvmBindingRegistry`), a reusable + `assert_controls` gate, and an example `claim_member_badge` gated action. + +Each package builds and tests against `nexus-next` as a sibling checkout of +`nexus-tools` (see each tool's `Move.toml` local paths): + +```bash +cd onchain/ +sui move build +sui move test +``` diff --git a/onchain/evm-signin/Move.toml b/onchain/evm-signin/Move.toml new file mode 100644 index 0000000..82616f2 --- /dev/null +++ b/onchain/evm-signin/Move.toml @@ -0,0 +1,11 @@ +[package] +name = "evm_signin" +edition = "2024.beta" + +[dependencies] +nexus_primitives = { local = "../../../nexus-next/sui/primitives" } +nexus_workflow = { local = "../../../nexus-next/sui/workflow" } +nexus_interface = { local = "../../../nexus-next/sui/interface" } + +[addresses] +evm_signin = "0x0" diff --git a/onchain/evm-signin/README.md b/onchain/evm-signin/README.md new file mode 100644 index 0000000..62eda5e --- /dev/null +++ b/onchain/evm-signin/README.md @@ -0,0 +1,194 @@ +# Onchain tool: `xyz.taluslabs.evm.verify-signature@1` + +Sui Move tool that verifies an **Ethereum (secp256k1) signature onchain** and +recovers the signer's `0x` address — using Sui's native +`ecdsa_k1::secp256k1_ecrecover` with Keccak256, exactly like Ethereum's +`ecrecover` precompile. + +## Why this matters + +The #1 friction for EVM users coming to Sui is "install a new wallet, learn a +new seed phrase." This tool removes it: an EVM user signs a message with their +existing wallet (MetaMask `personal_sign` / EIP-712), and a Sui Move contract +verifies it and derives their Ethereum address. That address can then authorize +a Sui action — gated mints, DAO actions, claims, or binding an `0x` identity to a +Sui object — **without the user ever leaving their Ethereum wallet**. + +It is the identity half of using Sui + Nexus as a verifiable coordination layer +for EVM. + +## Package layout + +Three Move modules in one package: + +- `evm_crypto` — secp256k1 `ecrecover` + Keccak256 → Ethereum address recovery. + Shared, dependency-free core. +- `evm_signin` — the Nexus onchain tool (`verify-signature`). Verifies a + signature and returns the recovered address as tool output. +- `evm_access` — identity binding & access control: `bind` (mint + `EvmIdentityPass`), `assert_controls` (reusable gate), and + `claim_member_badge` (example gated action). Emits events for indexers. + +End-to-end flow: + +```text +MetaMask sign → evm_signin::execute (verify, in a workflow) + ↘ evm_access::bind (0x ↔ Sui, mint pass, emit event) + ↘ assert_controls / claim_member_badge (gated Sui action) +``` + +## Build + +```bash +cd onchain/evm-signin +sui move build +``` + +Requires `nexus-next` as a sibling of `nexus-tools` (see `Move.toml` local paths). + +## Test + +```bash +cd onchain/evm-signin +sui move test +``` + +The test vector is a real `personal_sign` signature generated with foundry +`cast` (Anvil account #0), so the test proves the full Ethereum recovery path +end to end. + +## Publish + +```bash +# Ensure the Sui CLI active env is testnet +sui client publish --gas-budget 100000000 +``` + +Note the **package ID** and the **witness object ID** (the `EvmSigninWitness` +inside `EvmSigninState`, retrievable via `witness_id`). + +On publish, `evm_signin::init` creates **both** shared objects: + +- `EvmSigninState` — Nexus tool state + witness (register this tool against it). +- `EvmBindingRegistry` — identity-binding registry for `evm_access::bind`. + +## Register onchain + +```bash +nexus tool register onchain \ + --package 0xYOUR_PACKAGE_ID \ + --module evm_signin \ + --tool-fqn xyz.taluslabs.evm.verify-signature@1 \ + --description "Verify an Ethereum secp256k1 signature onchain and recover the signer 0x address" \ + --witness-id 0xYOUR_WITNESS_OBJECT_ID \ + --timeout 10s +``` + +Canonical schemas: `schemas/input.json`, `schemas/output.json`. + +## Input + +| Port | Move param | Type | Description | +|------|------------|------|-------------| +| `0` | `state` | Shared `EvmSigninState` (mutable) | Tool state + witness | +| `1` | `message` | `vector` | Exact signed bytes (EIP-191/EIP-712 preimage); Keccak256-hashed internally | +| `2` | `signature` | `vector` | 65-byte `(r, s, v)` secp256k1 signature; `v` may be 27/28 or 0/1 | +| `3` | `expected_address` | `vector` | Optional 20-byte address to check against; empty to skip | + +## Output + +Success: + +```json +{ "ok": { "recovered_address": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", "matches": true } } +``` + +Error (signature not 65 bytes): + +```json +{ "err": { "reason": "Signature must be 65 bytes (r, s, v)" } } +``` + +A cryptographically invalid (but correctly sized) signature aborts the +transaction via the native `ecrecover`, rather than returning `err`. + +## Companion module: identity binding & access (`evm_access`) + +`evm_crypto::recover_address` is reusable, so the same package ships +`evm_access`, which turns a verified signature into real Sui actions — the +flagship "EVM user controls Sui with MetaMask" flow. + +### Objects + +- `EvmBindingRegistry` — shared object mapping each `0x` address to the Sui + account bound to it. +- `EvmIdentityPass` — soulbound (`key`-only, no transfer) proof that a Sui + account controls an `0x` address. +- `EvmMemberBadge` — soulbound badge minted by the gated example action. + +### Functions + +- `bind(registry, signature, ctx)` — recovers the signer of the canonical + statement for `ctx.sender()`, records the binding, emits `EvmAddressBound`, + and mints the caller an `EvmIdentityPass`. +- `assert_controls(pass, eth_address)` — reusable gate: aborts unless the pass + proves control of `eth_address`. Any dApp can call this to require "only the + holder of this Ethereum address may proceed." +- `claim_member_badge(pass, ctx)` — example gated action: requires the + `EvmIdentityPass` (a soulbound object only its owner can pass by reference), + emits `MemberBadgeClaimed`, and mints an `EvmMemberBadge`. Demonstrates the + full MetaMask → Sui loop. + +### Events + +- `EvmAddressBound { eth_address, sui_owner }` +- `MemberBadgeClaimed { eth_address, sui_owner }` + +**Replay safety.** The caller does not pass the message. The contract +reconstructs the exact statement from `ctx.sender()`: + +```text +Bind my Ethereum address to Sui account: 0x<32-byte-sui-address-hex> +``` + +A signature made to bind account A cannot be replayed under account B, because +the reconstructed message — and therefore the recovered address — differs. The +client must sign this exact statement (via `personal_sign`). + +This is the building block for gated mints, DAO membership, and airdrop claims +keyed on a user's existing Ethereum identity, with no new wallet. + +### Client: sign the bind statement + +The user must sign this exact string with MetaMask (`personal_sign`): + +```text +Bind my Ethereum address to Sui account: 0x<32-byte-sui-address-hex> +``` + +Example for Sui address `@0xA11CE` (padded to 32 bytes): + +```bash +cast wallet sign --private-key $PK \ + "Bind my Ethereum address to Sui account: 0x00000000000000000000000000000000000000000000000000000000000a11ce" +``` + +Then call `bind` with the 65-byte signature (hex-decoded) as the second argument. + +## Generating a signature (verify tool) + +Any EVM wallet/library produces a compatible signature. With foundry `cast`: + +```bash +cast wallet sign --private-key $PK "Login to Talus Nexus" +``` + +The `message` port must be the **EIP-191 preimage** that was hashed: +`0x19 || "Ethereum Signed Message:\n" || len(msg) || msg`. For EIP-712, pass the +full EIP-712 encoded preimage instead. + +## Verify + +```bash +nexus tool list +``` diff --git a/onchain/evm-signin/schemas/input.json b/onchain/evm-signin/schemas/input.json new file mode 100644 index 0000000..97dfa19 --- /dev/null +++ b/onchain/evm-signin/schemas/input.json @@ -0,0 +1,23 @@ +{ + "0": { + "type": "object", + "name": "state", + "mutable": true, + "description": "Shared EvmSigninState object holding the tool witness" + }, + "1": { + "type": "string", + "name": "message", + "description": "The exact bytes that were signed (EIP-191/EIP-712 preimage), hex-encoded. ecrecover Keccak256-hashes these internally." + }, + "2": { + "type": "string", + "name": "signature", + "description": "65-byte secp256k1 signature (r, s, v), hex-encoded. v may be 27/28 (Ethereum) or 0/1 (raw recovery id)." + }, + "3": { + "type": "string", + "name": "expected_address", + "description": "Optional 20-byte Ethereum address to check the recovered signer against, hex-encoded. Empty to skip the check." + } +} diff --git a/onchain/evm-signin/schemas/output.json b/onchain/evm-signin/schemas/output.json new file mode 100644 index 0000000..0c87891 --- /dev/null +++ b/onchain/evm-signin/schemas/output.json @@ -0,0 +1,9 @@ +{ + "ok": { + "recovered_address": "string", + "matches": "bool" + }, + "err": { + "reason": "string" + } +} diff --git a/onchain/evm-signin/sources/evm_access.move b/onchain/evm-signin/sources/evm_access.move new file mode 100644 index 0000000..59c0ba9 --- /dev/null +++ b/onchain/evm-signin/sources/evm_access.move @@ -0,0 +1,205 @@ +/// EVM identity binding and access control for Sui. +/// +/// Turns a verified Ethereum signature into real, replay-safe Sui actions: +/// +/// 1. `bind` — an EVM user signs a statement naming *their Sui account*; this +/// module recovers the signer, records the `0x` -> Sui binding, and mints a +/// soulbound [`EvmIdentityPass`]. +/// 2. `assert_controls` — a reusable gate any dApp can call: "only the holder of +/// this Ethereum address may proceed." +/// 3. `claim_member_badge` — a concrete example of gating a Sui action behind +/// the pass, closing the MetaMask -> Sui loop end to end. +module evm_signin::evm_access; + +use evm_signin::evm_crypto; +use sui::address; +use sui::event; +use sui::table::{Self, Table}; +use sui::transfer; + +// === Errors === + +/// The recovered binding already exists for this Ethereum address. +const EAlreadyBound: u64 = 1; +/// Signature is not 65 bytes (r, s, v). +const EInvalidSignatureLength: u64 = 2; +/// The provided pass does not control the required Ethereum address. +const ENotController: u64 = 3; + +// === Constants === + +/// Fixed statement prefix the EVM user must sign, followed by their Sui address +/// as a `0x`-prefixed hex string. Keep in sync with any client that produces +/// the signature. +const STATEMENT_PREFIX: vector = b"Bind my Ethereum address to Sui account: "; + +// === Objects === + +/// Soulbound proof that a Sui account controls an Ethereum address. +/// `key` only (no `store`) and no transfer function: it cannot be moved once +/// minted. +public struct EvmIdentityPass has key { + id: UID, + eth_address: vector, + sui_owner: address, +} + +/// Soulbound badge gated behind EVM identity. Minting requires the +/// `EvmIdentityPass`, so only an account that has proven control of an Ethereum +/// address can claim it. +public struct EvmMemberBadge has key { + id: UID, + eth_address: vector, +} + +/// Shared registry mapping an Ethereum address to the Sui account bound to it. +public struct EvmBindingRegistry has key { + id: UID, + bound: Table, address>, +} + +// === Events === + +/// Emitted when an Ethereum address is bound to a Sui account. +public struct EvmAddressBound has copy, drop { + eth_address: vector, + sui_owner: address, +} + +/// Emitted when a member badge is claimed. +public struct MemberBadgeClaimed has copy, drop { + eth_address: vector, + sui_owner: address, +} + +// === Registry lifecycle === + +/// Create and share a new empty binding registry. Called from `evm_signin::init` +/// on publish so both shared objects exist after a single package init. +public fun share_registry(ctx: &mut TxContext) { + transfer::share_object(new_registry(ctx)); +} + +/// Create a new empty binding registry. +public fun new_registry(ctx: &mut TxContext): EvmBindingRegistry { + EvmBindingRegistry { + id: object::new(ctx), + bound: table::new(ctx), + } +} + +// === Binding === + +/// Bind the caller's Sui account to the Ethereum address that signed the +/// canonical statement for `ctx.sender()`, and mint a soulbound pass. +/// +/// The caller does not pass the message: the contract reconstructs it from +/// `ctx.sender()`, so the signature is provably an authorization for *this* Sui +/// account and cannot be replayed under another account. +entry fun bind(registry: &mut EvmBindingRegistry, signature: vector, ctx: &mut TxContext) { + assert!(signature.length() == 65, EInvalidSignatureLength); + + let sender = ctx.sender(); + let preimage = bind_preimage(sender); + let eth_address = evm_crypto::recover_address(preimage, signature); + + assert!(!registry.bound.contains(eth_address), EAlreadyBound); + registry.bound.add(eth_address, sender); + + event::emit(EvmAddressBound { eth_address, sui_owner: sender }); + + transfer::transfer( + EvmIdentityPass { + id: object::new(ctx), + eth_address, + sui_owner: sender, + }, + sender, + ); +} + +// === Access control === + +/// Abort unless `pass` proves control of `eth_address`. Reusable gate primitive: +/// a dApp can require "only the holder of this Ethereum address may proceed." +public fun assert_controls(pass: &EvmIdentityPass, eth_address: vector) { + assert!(pass.eth_address == eth_address, ENotController); +} + +/// Example gated action: mint a soulbound member badge. Requires the caller to +/// own an `EvmIdentityPass` (a soulbound object only its owner can pass by +/// reference), so the Sui action is gated on proven EVM identity. +entry fun claim_member_badge(pass: &EvmIdentityPass, ctx: &mut TxContext) { + let sender = ctx.sender(); + + event::emit(MemberBadgeClaimed { eth_address: pass.eth_address, sui_owner: sender }); + + transfer::transfer( + EvmMemberBadge { + id: object::new(ctx), + eth_address: pass.eth_address, + }, + sender, + ); +} + +// === Message construction === + +fun bind_preimage(sui_addr: address): vector { + let mut statement = STATEMENT_PREFIX; + statement.append(evm_crypto::to_hex_string(&address::to_bytes(sui_addr))); + personal_sign_preimage(statement) +} + +/// Wrap `message` in the EIP-191 envelope: +/// `0x19 || "Ethereum Signed Message:\n" || ascii(len) || message`. +fun personal_sign_preimage(message: vector): vector { + let mut out = vector[0x19]; + out.append(b"Ethereum Signed Message:\n"); + out.append(u64_to_ascii(message.length())); + out.append(message); + out +} + +/// Decimal ASCII encoding of `n`. +fun u64_to_ascii(mut n: u64): vector { + if (n == 0) { + return b"0" + }; + let mut digits = vector[]; + while (n > 0) { + digits.push_back(48 + ((n % 10) as u8)); + n = n / 10; + }; + digits.reverse(); + digits +} + +// === Getters === + +public fun eth_address(pass: &EvmIdentityPass): vector { + pass.eth_address +} + +public fun sui_owner(pass: &EvmIdentityPass): address { + pass.sui_owner +} + +public fun badge_eth_address(badge: &EvmMemberBadge): vector { + badge.eth_address +} + +public fun is_bound(registry: &EvmBindingRegistry, eth_address: vector): bool { + registry.bound.contains(eth_address) +} + +public fun owner_of(registry: &EvmBindingRegistry, eth_address: vector): address { + *registry.bound.borrow(eth_address) +} + +// === Test-only === + +#[test_only] +public fun bind_preimage_for_test(sui_addr: address): vector { + bind_preimage(sui_addr) +} diff --git a/onchain/evm-signin/sources/evm_crypto.move b/onchain/evm-signin/sources/evm_crypto.move new file mode 100644 index 0000000..34ed607 --- /dev/null +++ b/onchain/evm-signin/sources/evm_crypto.move @@ -0,0 +1,60 @@ +/// Shared Ethereum secp256k1 signature recovery for the `evm_signin` package. +/// +/// Isolated so `evm_signin` (Nexus tool) and `evm_access` (identity binding) +/// can both use the same crypto without a circular module dependency. +module evm_signin::evm_crypto; + +use sui::ecdsa_k1; +use sui::hash; + +/// Hash-function selector for `secp256k1_ecrecover`: 0 = Keccak256 (Ethereum). +const KECCAK256: u8 = 0; + +/// Recover the 20-byte Ethereum address that signed `message` with `signature`. +/// +/// Aborts on a cryptographically invalid (but correctly sized) signature; guard +/// `signature.length() == 65` first. +public fun recover_address(message: vector, signature: vector): vector { + recover_eth_address(&message, signature) +} + +/// Encodes bytes as a `0x`-prefixed lowercase hex ASCII string. +public fun to_hex_string(bytes: &vector): vector { + let hex_chars = b"0123456789abcdef"; + let mut out = b"0x"; + let mut i = 0; + let n = bytes.length(); + while (i < n) { + let byte = bytes[i]; + out.push_back(hex_chars[((byte >> 4) as u64)]); + out.push_back(hex_chars[((byte & 0x0f) as u64)]); + i = i + 1; + }; + out +} + +/// Recover the 20-byte Ethereum address that signed `message`. +fun recover_eth_address(message: &vector, mut signature: vector): vector { + let v = signature[64]; + if (v >= 27) { + *signature.borrow_mut(64) = v - 27; + }; + + let compressed = ecdsa_k1::secp256k1_ecrecover(&signature, message, KECCAK256); + let uncompressed = ecdsa_k1::decompress_pubkey(&compressed); + + let pubkey_xy = slice(&uncompressed, 1, 65); + let hashed = hash::keccak256(&pubkey_xy); + + slice(&hashed, 12, 32) +} + +fun slice(bytes: &vector, start: u64, end: u64): vector { + let mut out = vector[]; + let mut i = start; + while (i < end) { + out.push_back(bytes[i]); + i = i + 1; + }; + out +} diff --git a/onchain/evm-signin/sources/evm_signin.move b/onchain/evm-signin/sources/evm_signin.move new file mode 100644 index 0000000..a3c34b2 --- /dev/null +++ b/onchain/evm-signin/sources/evm_signin.move @@ -0,0 +1,112 @@ +/// # `xyz.taluslabs.evm.verify-signature@1` +/// +/// Onchain Nexus Tool that verifies an Ethereum (secp256k1) signature *inside* +/// a Sui Move contract and recovers the signer's 0x address. +module evm_signin::evm_signin; + +use evm_signin::evm_access; +use evm_signin::evm_crypto; +use nexus_primitives::data; +use nexus_primitives::proof_of_uid::ProofOfUID; +use nexus_primitives::tagged_output::{Self as tagged_output, TaggedOutput}; +use std::ascii::String as AsciiString; +use sui::bag::{Self, Bag}; +use sui::transfer::share_object; + +/// One-time witness for package initialization. +public struct EVM_SIGNIN has drop {} + +/// Witness object used by the Nexus framework to identify this tool. +public struct EvmSigninWitness has key, store { + id: UID, +} + +/// Shared state object. Holds only the tool witness; signature verification is +/// pure, so the tool keeps no per-call state. +public struct EvmSigninState has key { + id: UID, + witness: Bag, +} + +/// Tool execution output variants. Only used so the SDK can fetch the output +/// schema at registration time; runtime output is built via `TaggedOutput`. +public enum Output { + Ok { + recovered_address: AsciiString, + matches: bool, + }, + Err { + reason: AsciiString, + }, +} + +/// Package init: creates both the Nexus tool state and the identity-binding +/// registry. Sui only invokes init for the package OTW (`EVM_SIGNIN`), so both +/// shared objects must be created here. +fun init(_otw: EVM_SIGNIN, ctx: &mut TxContext) { + let state = EvmSigninState { + id: object::new(ctx), + witness: { + let mut bag = bag::new(ctx); + bag.add(b"witness", EvmSigninWitness { id: object::new(ctx) }); + bag + }, + }; + share_object(state); + evm_access::share_registry(ctx); +} + +/// Verify an Ethereum signature and recover the signer address. +public fun execute( + worksheet: &mut ProofOfUID, + state: &mut EvmSigninState, + message: vector, + signature: vector, + expected_address: vector, + _ctx: &mut TxContext, +): TaggedOutput { + let witness = state.witness(); + worksheet.stamp_with_data(&witness.id, b"evm_signin_executed"); + + if (signature.length() != 65) { + return tagged_output::new(b"err").with_named_payload( + b"reason", + data::inline_one(b"Signature must be 65 bytes (r, s, v)").as_string(), + ) + }; + + let recovered = evm_crypto::recover_address(message, signature); + let recovered_hex = evm_crypto::to_hex_string(&recovered); + + let matches = expected_address.is_empty() || expected_address == recovered; + + tagged_output::new(b"ok") + .with_named_payload(b"recovered_address", data::inline_one(recovered_hex).as_string()) + .with_named_payload( + b"matches", + data::inline_one(if (matches) b"true" else b"false").as_bool(), + ) +} + +fun witness(self: &EvmSigninState): &EvmSigninWitness { + self.witness.borrow(b"witness") +} + +public fun witness_id(self: &EvmSigninState): ID { + self.witness().id.to_inner() +} + +/// Re-export for callers that imported recovery from this module historically. +public fun recover_address(message: vector, signature: vector): vector { + evm_crypto::recover_address(message, signature) +} + +/// Re-export hex helper. +public fun to_hex_string(bytes: &vector): vector { + evm_crypto::to_hex_string(bytes) +} + +#[test_only] +public fun init_for_test(otw: EVM_SIGNIN, ctx: &mut TxContext) { + init(otw, ctx); +} diff --git a/onchain/evm-signin/tests/evm_access.test.move b/onchain/evm-signin/tests/evm_access.test.move new file mode 100644 index 0000000..a0ebf96 --- /dev/null +++ b/onchain/evm-signin/tests/evm_access.test.move @@ -0,0 +1,178 @@ +#[test_only] +module evm_signin::evm_access_test; + +use evm_signin::evm_access::{Self, EvmBindingRegistry, EvmIdentityPass, EvmMemberBadge}; +use evm_signin::evm_signin::{Self, EVM_SIGNIN}; +use sui::test_scenario; +use sui::test_utils; + +const USER: address = @0xA11CE; +const OTHER: address = @0xB0B; + +// SIGNER / SIGNATURE are NOT arbitrary: they are produced from the well-known +// public Anvil/Hardhat test account #0 (a standard, non-secret test key). +// Reproduce exactly: +// PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +// cast wallet address --private-key $PK +// -> 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (== SIGNER) +// cast wallet sign --private-key $PK \ +// "Bind my Ethereum address to Sui account: 0x00000000000000000000000000000000000000000000000000000000000a11ce" +// -> 0x073b...1c (== SIGNATURE) +// The Sui account in the statement is USER (@0xA11CE) left-padded to 32 bytes. +const SIGNER: vector = x"f39fd6e51aad88f6f4ce6ab8827279cfffb92266"; +const SIGNATURE: vector = + x"073bb824168198df8af80de7cdce73d69191b975ecfb1e5beab9db7f0686acdb19cf36d8d90d1ab2bffbc3a93e8afea3fc01399d47aa23bc15a880d21d0441931c"; + +#[test] +fun package_init_creates_registry() { + let mut scenario = test_scenario::begin(USER); + { + let otw = test_utils::create_one_time_witness(); + evm_signin::init_for_test(otw, scenario.ctx()); + }; + scenario.next_tx(USER); + + // Publish init must create the binding registry alongside tool state. + let _registry = scenario.take_shared(); + let _state = scenario.take_shared(); + test_scenario::return_shared(_registry); + test_scenario::return_shared(_state); + scenario.end(); +} + +#[test] +fun bind_mints_pass_and_records_binding() { + let mut scenario = test_scenario::begin(USER); + { + let otw = test_utils::create_one_time_witness(); + evm_signin::init_for_test(otw, scenario.ctx()); + }; + scenario.next_tx(USER); + + let mut registry = scenario.take_shared(); + evm_access::bind(&mut registry, SIGNATURE, scenario.ctx()); + test_scenario::return_shared(registry); + + // bind must emit exactly one EvmAddressBound event. + let effects = scenario.next_tx(USER); + assert!(effects.num_user_events() == 1, 4); + + let pass = scenario.take_from_sender(); + assert!(evm_access::eth_address(&pass) == SIGNER, 0); + assert!(evm_access::sui_owner(&pass) == USER, 1); + + let registry = scenario.take_shared(); + assert!(evm_access::is_bound(®istry, SIGNER), 2); + assert!(evm_access::owner_of(®istry, SIGNER) == USER, 3); + test_scenario::return_shared(registry); + + scenario.return_to_sender(pass); + scenario.end(); +} + +#[test] +fun claim_member_badge_gated_by_pass() { + let mut scenario = test_scenario::begin(USER); + { + let otw = test_utils::create_one_time_witness(); + evm_signin::init_for_test(otw, scenario.ctx()); + }; + scenario.next_tx(USER); + + // Bind to obtain the identity pass. + let mut registry = scenario.take_shared(); + evm_access::bind(&mut registry, SIGNATURE, scenario.ctx()); + test_scenario::return_shared(registry); + + scenario.next_tx(USER); + + // Use the pass to claim a gated member badge. + let pass = scenario.take_from_sender(); + evm_access::claim_member_badge(&pass, scenario.ctx()); + + let effects = scenario.next_tx(USER); + assert!(effects.num_user_events() == 1, 0); + + let badge = scenario.take_from_sender(); + assert!(evm_access::badge_eth_address(&badge) == SIGNER, 1); + + scenario.return_to_sender(badge); + scenario.return_to_sender(pass); + scenario.end(); +} + +#[test] +fun assert_controls_accepts_correct_address() { + let mut scenario = test_scenario::begin(USER); + { + let otw = test_utils::create_one_time_witness(); + evm_signin::init_for_test(otw, scenario.ctx()); + }; + scenario.next_tx(USER); + + let mut registry = scenario.take_shared(); + evm_access::bind(&mut registry, SIGNATURE, scenario.ctx()); + test_scenario::return_shared(registry); + + scenario.next_tx(USER); + + let pass = scenario.take_from_sender(); + evm_access::assert_controls(&pass, SIGNER); + + scenario.return_to_sender(pass); + scenario.end(); +} + +#[test] +#[expected_failure(abort_code = evm_access::ENotController)] +fun assert_controls_rejects_wrong_address() { + let mut scenario = test_scenario::begin(USER); + { + let otw = test_utils::create_one_time_witness(); + evm_signin::init_for_test(otw, scenario.ctx()); + }; + scenario.next_tx(USER); + + let mut registry = scenario.take_shared(); + evm_access::bind(&mut registry, SIGNATURE, scenario.ctx()); + test_scenario::return_shared(registry); + + scenario.next_tx(USER); + + let pass = scenario.take_from_sender(); + // Wrong address must abort. + evm_access::assert_controls(&pass, x"0000000000000000000000000000000000000000"); + + scenario.return_to_sender(pass); + scenario.end(); +} + +#[test] +#[expected_failure(abort_code = evm_access::EAlreadyBound)] +fun double_bind_aborts() { + let mut scenario = test_scenario::begin(USER); + { + let otw = test_utils::create_one_time_witness(); + evm_signin::init_for_test(otw, scenario.ctx()); + }; + scenario.next_tx(USER); + + let mut registry = scenario.take_shared(); + evm_access::bind(&mut registry, SIGNATURE, scenario.ctx()); + evm_access::bind(&mut registry, SIGNATURE, scenario.ctx()); + test_scenario::return_shared(registry); + + scenario.end(); +} + +#[test] +fun binding_is_replay_safe() { + let preimage_user = evm_access::bind_preimage_for_test(USER); + let preimage_other = evm_access::bind_preimage_for_test(OTHER); + + let eth_user = evm_signin::recover_address(preimage_user, SIGNATURE); + let eth_other = evm_signin::recover_address(preimage_other, SIGNATURE); + + assert!(eth_user == SIGNER, 0); + assert!(eth_other != SIGNER, 1); +} diff --git a/onchain/evm-signin/tests/evm_signin.test.move b/onchain/evm-signin/tests/evm_signin.test.move new file mode 100644 index 0000000..33ae709 --- /dev/null +++ b/onchain/evm-signin/tests/evm_signin.test.move @@ -0,0 +1,147 @@ +#[test_only] +module evm_signin::evm_signin_test; + +use evm_signin::evm_signin::{Self, EvmSigninState, EVM_SIGNIN}; +use nexus_primitives::proof_of_uid; +use sui::test_scenario; +use sui::test_utils; + +const USER: address = @0xA11CE; + +// Deterministic vector generated with foundry `cast` using Anvil account #0: +// key : 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +// addr : 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 +// msg : "Login to Talus Nexus" (signed with personal_sign / EIP-191) +// +// `MESSAGE` is the EIP-191 preimage: 0x19 || "Ethereum Signed Message:\n20" || msg. +const MESSAGE: vector = + x"19457468657265756d205369676e6564204d6573736167653a0a32304c6f67696e20746f2054616c7573204e65787573"; +const SIGNATURE: vector = + x"a32c85b1f7bd8572112fa50788c209581ec16c4c6279dd8f2233f0c1f6492a26345a0c4147bbfc5c542c57d74407ded4d2a3046f8150491cda66ed4a66bbb6fb1c"; +// Same signature with `v` in raw recovery-id form (0x01) instead of Ethereum's +// 0x1c (28). The tool must normalize both to the same recovered address. +const SIGNATURE_RAW_V: vector = + x"a32c85b1f7bd8572112fa50788c209581ec16c4c6279dd8f2233f0c1f6492a26345a0c4147bbfc5c542c57d74407ded4d2a3046f8150491cda66ed4a66bbb6fb01"; +const SIGNER: vector = x"f39fd6e51aad88f6f4ce6ab8827279cfffb92266"; +const OTHER_ADDR: vector = x"0000000000000000000000000000000000000000"; + +#[test] +fun recovers_signer_address() { + let recovered = evm_signin::recover_address(MESSAGE, SIGNATURE); + assert!(recovered == SIGNER, 0); +} + +#[test] +fun recovers_with_raw_recovery_id() { + // 0x1c (28) and 0x01 (raw id 1) must recover the same address. + let recovered = evm_signin::recover_address(MESSAGE, SIGNATURE_RAW_V); + assert!(recovered == SIGNER, 0); +} + +#[test] +fun execute_stamps_and_returns_ok() { + let mut scenario = test_scenario::begin(USER); + { + let otw = test_utils::create_one_time_witness(); + evm_signin::init_for_test(otw, scenario.ctx()); + }; + scenario.next_tx(USER); + + let mut state: EvmSigninState = scenario.take_shared(); + let witness_id = state.witness_id(); + + let from = object::new(scenario.ctx()); + let mut worksheet = proof_of_uid::new(&from); + + let out = evm_signin::execute( + &mut worksheet, + &mut state, + MESSAGE, + SIGNATURE, + SIGNER, + scenario.ctx(), + ); + + // The tool must stamp the worksheet with its witness id. + assert!(worksheet.has_stamp(witness_id), 0); + + let (tag, _payload) = out.into_parts(); + assert!(tag == b"ok", 1); + + let _ = worksheet.consume(&from); + object::delete(from); + test_scenario::return_shared(state); + scenario.end(); +} + +#[test] +fun execute_rejects_bad_signature_length() { + let mut scenario = test_scenario::begin(USER); + { + let otw = test_utils::create_one_time_witness(); + evm_signin::init_for_test(otw, scenario.ctx()); + }; + scenario.next_tx(USER); + + let mut state: EvmSigninState = scenario.take_shared(); + + let from = object::new(scenario.ctx()); + let mut worksheet = proof_of_uid::new(&from); + + // Truncated signature -> err variant (no abort). + let out = evm_signin::execute( + &mut worksheet, + &mut state, + MESSAGE, + x"deadbeef", + SIGNER, + scenario.ctx(), + ); + + let (tag, _payload) = out.into_parts(); + assert!(tag == b"err", 0); + + let _ = worksheet.consume(&from); + object::delete(from); + test_scenario::return_shared(state); + scenario.end(); +} + +#[test] +fun execute_ok_with_empty_expected_skips_check() { + let mut scenario = test_scenario::begin(USER); + { + let otw = test_utils::create_one_time_witness(); + evm_signin::init_for_test(otw, scenario.ctx()); + }; + scenario.next_tx(USER); + + let mut state: EvmSigninState = scenario.take_shared(); + + let from = object::new(scenario.ctx()); + let mut worksheet = proof_of_uid::new(&from); + + // Empty expected_address -> recovery still succeeds, check is skipped. + let out = evm_signin::execute( + &mut worksheet, + &mut state, + MESSAGE, + SIGNATURE, + x"", + scenario.ctx(), + ); + + let (tag, _payload) = out.into_parts(); + assert!(tag == b"ok", 0); + + let _ = worksheet.consume(&from); + object::delete(from); + test_scenario::return_shared(state); + scenario.end(); +} + +#[test] +fun recovered_address_differs_from_other() { + let recovered = evm_signin::recover_address(MESSAGE, SIGNATURE); + assert!(recovered != OTHER_ADDR, 0); +}