From fe149b9053866f5bdecdbdd1452e8fa9d6cd049e Mon Sep 17 00:00:00 2001 From: Goutham Denthaje Date: Tue, 23 Jun 2026 15:09:19 +0200 Subject: [PATCH] stellar: add Soroban client workspace Introduces the Stellar Soroban workspace and the wormhole-soroban-client public API crate. This crate contains shared protocol constants, errors, VAA types, the bytes reader utility, and the WormholeCoreInterface client surface used by the core contract and external Soroban integrations. Changes: - Add Stellar Rust workspace files, toolchain configuration, rustfmt configuration, and .gitignore. - Add stellar/contracts/wormhole-soroban-client/ with public types, errors, constants, VAA parsing helpers, and the generated client interface. - Update stellar/Cargo.lock for the new workspace. - Add Stellar-specific spellcheck words where needed. --- cspell-custom-words.txt | 4 + stellar/.gitignore | 21 + stellar/Cargo.lock | 1832 +++++++++++++++++ stellar/Cargo.toml | 27 + .../wormhole-soroban-client/Cargo.toml | 15 + .../src/bytes_reader.rs | 218 ++ .../wormhole-soroban-client/src/constants.rs | 116 ++ .../wormhole-soroban-client/src/error.rs | 84 + .../wormhole-soroban-client/src/lib.rs | 304 +++ .../wormhole-soroban-client/src/types.rs | 275 +++ stellar/rust-toolchain.toml | 4 + stellar/rustfmt.toml | 26 + 12 files changed, 2926 insertions(+) create mode 100644 stellar/.gitignore create mode 100644 stellar/Cargo.lock create mode 100644 stellar/Cargo.toml create mode 100644 stellar/contracts/wormhole-soroban-client/Cargo.toml create mode 100644 stellar/contracts/wormhole-soroban-client/src/bytes_reader.rs create mode 100644 stellar/contracts/wormhole-soroban-client/src/constants.rs create mode 100644 stellar/contracts/wormhole-soroban-client/src/error.rs create mode 100644 stellar/contracts/wormhole-soroban-client/src/lib.rs create mode 100644 stellar/contracts/wormhole-soroban-client/src/types.rs create mode 100644 stellar/rust-toolchain.toml create mode 100644 stellar/rustfmt.toml diff --git a/cspell-custom-words.txt b/cspell-custom-words.txt index 0c4a954c1b5..a5817c4ad80 100644 --- a/cspell-custom-words.txt +++ b/cspell-custom-words.txt @@ -276,3 +276,7 @@ xpla XPLA XRPL Zellic + +soroban +stroops +contractimpl diff --git a/stellar/.gitignore b/stellar/.gitignore new file mode 100644 index 00000000000..c446187bca7 --- /dev/null +++ b/stellar/.gitignore @@ -0,0 +1,21 @@ +# IDEs +.vscode +.idea +.nvim.lua + +# Rust/Cargo +debug/ +target/ +# TODO(template) Cargo.lock should be tracked in binary crates, but not in libraries +Cargo.lock + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# These are backup files generated by rustfmt +**/*.rs.bk + +# Code coverage output +lcov.info + +test_snapshots/ diff --git a/stellar/Cargo.lock b/stellar/Cargo.lock new file mode 100644 index 00000000000..82dd43e1799 --- /dev/null +++ b/stellar/Cargo.lock @@ -0,0 +1,1832 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[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 = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "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", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[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.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-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes-lit" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0adabf37211a5276e46335feabcbb1530c95eb3fdf85f324c7db942770aa025d" +dependencies = [ + "num-bigint", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "cc" +version = "1.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.106", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.106", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[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_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +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 = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[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]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown 0.16.0", + "serde", + "serde_core", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[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 = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.106", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "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 = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "serde", + "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 = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.11.4", + "schemars 0.8.22", + "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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7192e3a5551a7aeee90d2110b11b615798e81951fd8c8293c87ea7f88b0168f5" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "soroban-env-common" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc49a80a68fc1005847308e63b9fce39874de731940b1807b721d472de3ff01" +dependencies = [ + "arbitrary", + "crate-git-revision", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2334ba1cfe0a170ab744d96db0b4ca86934de9ff68187ceebc09dc342def55" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43af5d53c57bc2f546e122adc0b1cca6f93942c718977379aa19ddd04f06fcec" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey 0.0.13", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a989167512e3592d455b1e204d703cfe578a36672a77ed2f9e6f7e1bbfd9cc5c" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.106", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "25.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d569a1315f05216d024653ad87541aa15d3ff26dad9f8a98719cb53ccf2bf3" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror", +] + +[[package]] +name = "soroban-sdk" +version = "25.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add8d19cfd2c9941bbdc7c8223c3cf9d7ff9af4554ba3bd4ae93e16b19b08aea" +dependencies = [ + "arbitrary", + "bytes-lit", + "crate-git-revision", + "ctor", + "derive_arbitrary", + "ed25519-dalek", + "rand", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey 0.0.16", + "visibility", +] + +[[package]] +name = "soroban-sdk-macros" +version = "25.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0107e34575ec704ce29407695462e79e6b0e13ce7af6431b2f15c313e34464" +dependencies = [ + "darling 0.20.11", + "heck", + "itertools", + "macro-string", + "proc-macro2", + "quote", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.106", +] + +[[package]] +name = "soroban-spec" +version = "25.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a1c9f6ccc6aa78518545e3cf542bd26f11d9085328a2e1c06c90514733fe15" +dependencies = [ + "base64", + "stellar-xdr", + "thiserror", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "25.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8247d3c6256b544b2461606c6892351bb22978d751e07c1aea744377053d852" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn 2.0.106", + "thiserror", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.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 = "stellar-strkey" +version = "0.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913" +dependencies = [ + "crate-git-revision", + "data-encoding", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" +dependencies = [ + "crate-git-revision", + "data-encoding", + "heapless", +] + +[[package]] +name = "stellar-xdr" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d20dafed80076b227d4b17c0c508a4bbc4d5e4c3d4c1de7cd42242df4b1eaf" +dependencies = [ + "arbitrary", + "base64", + "cfg_eval", + "crate-git-revision", + "escape-bytes", + "ethnum", + "hex", + "serde", + "serde_with", + "sha2", + "stellar-strkey 0.0.13", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.11.4", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "windows-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +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.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-result" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wormhole-soroban-client" +version = "0.1.0" +dependencies = [ + "soroban-sdk", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/stellar/Cargo.toml b/stellar/Cargo.toml new file mode 100644 index 00000000000..b02c502b9aa --- /dev/null +++ b/stellar/Cargo.toml @@ -0,0 +1,27 @@ +[workspace] +resolver = "3" +members = [ + "contracts/wormhole-soroban-client" +] + +[workspace.package] +edition = "2024" + +[workspace.dependencies] +soroban-sdk = "25.1.1" +wormhole-soroban-client = { path = "contracts/wormhole-soroban-client" } + +[profile.release] +opt-level = "z" +overflow-checks = true +debug = 0 +strip = "symbols" +debug-assertions = false +panic = "abort" +codegen-units = 1 +lto = true + +# For more information about this profile see https://soroban.stellar.org/docs/basic-tutorials/logging#cargotoml-profile +[profile.release-with-logs] +inherits = "release" +debug-assertions = true diff --git a/stellar/contracts/wormhole-soroban-client/Cargo.toml b/stellar/contracts/wormhole-soroban-client/Cargo.toml new file mode 100644 index 00000000000..4ceaf2974ec --- /dev/null +++ b/stellar/contracts/wormhole-soroban-client/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "wormhole-soroban-client" +version = "0.1.0" +edition.workspace = true +publish = false + +[lib] +crate-type = ["lib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } diff --git a/stellar/contracts/wormhole-soroban-client/src/bytes_reader.rs b/stellar/contracts/wormhole-soroban-client/src/bytes_reader.rs new file mode 100644 index 00000000000..d3641bc46b5 --- /dev/null +++ b/stellar/contracts/wormhole-soroban-client/src/bytes_reader.rs @@ -0,0 +1,218 @@ +//! Cursor-based byte parsing utilities for VAA and payload deserialization. +//! +//! Provides a [`BytesReader`] that safely reads primitive types from a byte +//! buffer with automatic bounds checking. All multi-byte values are read in +//! big-endian format to match the Wormhole wire protocol. + +use crate::WormholeError; +use soroban_sdk::{Bytes, BytesN}; + +/// Cursor-based reader for parsing binary data from Soroban `Bytes`. +/// +/// Tracks read position and provides safe access to primitives with automatic +/// bounds checking. Returns [`WormholeError::InvalidVAAFormat`] when attempting +/// to read past the end of the buffer. +pub struct BytesReader<'a> { + bytes: &'a Bytes, + cursor: u32, +} + +impl<'a> BytesReader<'a> { + /// Creates a new reader starting at position 0. + pub fn new(bytes: &'a Bytes) -> Self { + Self { bytes, cursor: 0 } + } + + /// Returns the number of unread bytes remaining. + pub fn remaining(&self) -> u32 { + self.bytes.len().saturating_sub(self.cursor) + } + + /// Ensures at least `n` bytes remain; errors if insufficient data. + fn require(&self, n: u32) -> Result<(), WormholeError> { + if self.remaining() < n { + Err(WormholeError::InvalidVAAFormat) + } else { + Ok(()) + } + } + + /// Reads a byte at offset from current cursor without bounds check. + fn get_unchecked(&self, offset: u32) -> u8 { + self.bytes.get(self.cursor + offset).unwrap() + } + + /// Reads a single byte and advances the cursor. + pub fn read_u8(&mut self) -> Result { + self.require(1)?; + let value = self.get_unchecked(0); + self.cursor += 1; + + Ok(value) + } + + /// Reads a big-endian `u16` (2 bytes) and advances the cursor. + pub fn read_u16_be(&mut self) -> Result { + self.require(2)?; + let value = u16::from(self.get_unchecked(0)) << 8 | u16::from(self.get_unchecked(1)); + self.cursor += 2; + + Ok(value) + } + + /// Reads a big-endian `u32` (4 bytes) and advances the cursor. + pub fn read_u32_be(&mut self) -> Result { + self.require(4)?; + let value = (0..4).fold(0u32, |acc, i| (acc << 8) | u32::from(self.get_unchecked(i))); + self.cursor += 4; + + Ok(value) + } + + /// Reads a big-endian `u64` (8 bytes) and advances the cursor. + pub fn read_u64_be(&mut self) -> Result { + self.require(8)?; + let value = (0..8).fold(0u64, |acc, i| (acc << 8) | u64::from(self.get_unchecked(i))); + self.cursor += 8; + + Ok(value) + } + + /// Reads exactly `N` bytes into a fixed-size `BytesN` array. + /// + /// Useful for reading fixed-size fields like addresses and hashes. + pub fn read_bytes_n(&mut self) -> Result, WormholeError> { + let n = u32::try_from(N).map_err(|_| WormholeError::InvalidVAAFormat)?; + self.read_bytes(n).and_then(|bytes| { + bytes + .try_into() + .map_err(|_| WormholeError::InvalidVAAFormat) + }) + } + + /// Reads `len` bytes as a variable-length `Bytes` slice. + pub fn read_bytes(&mut self, len: u32) -> Result { + self.require(len)?; + let end = self.cursor.saturating_add(len); + let slice = self.bytes.slice(self.cursor..end); + self.cursor = end; + Ok(slice) + } + + /// Advances the cursor by `n` bytes without reading. + /// + /// Useful for skipping padding or reserved fields in payloads. + pub fn skip(&mut self, n: u32) -> Result<(), WormholeError> { + self.require(n)?; + self.cursor = self.cursor.saturating_add(n); + Ok(()) + } + + /// Returns all bytes from the current cursor to the end as a slice. + /// + /// Commonly used to extract variable-length payload data after parsing + /// fixed-size headers. + pub fn remaining_bytes(&self) -> Bytes { + self.bytes.slice(self.cursor..) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use soroban_sdk::Env; + + #[test] + fn test_read_u8() { + let env = Env::default(); + let bytes = Bytes::from_array(&env, &[0x12, 0x34, 0x56]); + let mut reader = BytesReader::new(&bytes); + + assert_eq!(reader.read_u8().unwrap(), 0x12); + assert_eq!(reader.read_u8().unwrap(), 0x34); + assert_eq!(reader.read_u8().unwrap(), 0x56); + assert!(reader.read_u8().is_err()); + } + + #[test] + fn test_read_u16_be() { + let env = Env::default(); + let bytes = Bytes::from_array(&env, &[0x12, 0x34, 0x56, 0x78]); + let mut reader = BytesReader::new(&bytes); + + assert_eq!(reader.read_u16_be().unwrap(), 0x1234); + assert_eq!(reader.read_u16_be().unwrap(), 0x5678); + assert!(reader.read_u16_be().is_err()); + } + + #[test] + fn test_read_u32_be() { + let env = Env::default(); + let bytes = Bytes::from_array(&env, &[0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD]); + let mut reader = BytesReader::new(&bytes); + + assert_eq!(reader.read_u32_be().unwrap(), 0x12345678); + assert!(reader.read_u32_be().is_err()); + } + + #[test] + fn test_read_u64_be() { + let env = Env::default(); + let bytes = Bytes::from_array(&env, &[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]); + let mut reader = BytesReader::new(&bytes); + + assert_eq!(reader.read_u64_be().unwrap(), 0x123456789ABCDEF0); + assert!(reader.read_u64_be().is_err()); + } + + #[test] + fn test_read_bytes_n() { + let env = Env::default(); + let bytes = Bytes::from_array(&env, &[0x01, 0x02, 0x03, 0x04]); + let mut reader = BytesReader::new(&bytes); + + let result: BytesN<3> = reader.read_bytes_n().unwrap(); + assert_eq!(result.to_array(), [0x01, 0x02, 0x03]); + + assert!(reader.read_bytes_n::<2>().is_err()); + } + + #[test] + fn test_skip() { + let env = Env::default(); + let bytes = Bytes::from_array(&env, &[0x01, 0x02, 0x03, 0x04, 0x05]); + let mut reader = BytesReader::new(&bytes); + + reader.skip(2).unwrap(); + assert_eq!(reader.read_u8().unwrap(), 0x03); + + assert!(reader.skip(10).is_err()); + } + + #[test] + fn test_remaining() { + let env = Env::default(); + let bytes = Bytes::from_array(&env, &[0x01, 0x02, 0x03, 0x04, 0x05]); + let mut reader = BytesReader::new(&bytes); + + assert_eq!(reader.remaining(), 5); + reader.read_u8().unwrap(); + assert_eq!(reader.remaining(), 4); + reader.skip(3).unwrap(); + assert_eq!(reader.remaining(), 1); + } + + #[test] + fn test_remaining_bytes() { + let env = Env::default(); + let bytes = Bytes::from_array(&env, &[0x01, 0x02, 0x03, 0x04, 0x05]); + let mut reader = BytesReader::new(&bytes); + + reader.skip(2).unwrap(); + let remaining = reader.remaining_bytes(); + + assert_eq!(remaining.get(0).unwrap(), 0x03); + assert_eq!(remaining.get(1).unwrap(), 0x04); + assert_eq!(remaining.get(2).unwrap(), 0x05); + } +} diff --git a/stellar/contracts/wormhole-soroban-client/src/constants.rs b/stellar/contracts/wormhole-soroban-client/src/constants.rs new file mode 100644 index 00000000000..2b4c9010d24 --- /dev/null +++ b/stellar/contracts/wormhole-soroban-client/src/constants.rs @@ -0,0 +1,116 @@ +//! Public protocol constants for the Wormhole Core contract. +//! +//! These constants define core Wormhole protocol parameters used for VAA +//! validation, governance processing, and cross-chain message handling. +//! External integrators may reference these when building on top of the +//! Wormhole Core contract. + +/// Chain ID of the governance source chain (Solana). +/// +/// All governance VAAs must originate from this chain to be considered valid. +pub const GOVERNANCE_CHAIN_ID: u32 = 1; + +/// Standard governance emitter address on Solana (`0x00...04`). +/// +/// This 32-byte address identifies the authorized governance contract that +/// can issue guardian set upgrades, fee changes, and contract upgrades. +pub const GOVERNANCE_EMITTER: [u8; 32] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, +]; + +/// Wormhole chain ID for Stellar/Soroban (61). +/// +/// Used in VAA payloads to target this specific chain. Chain IDs are encoded +/// as 2 bytes (u16) on the wire but stored as u16 for type safety. +pub const CHAIN_ID_STELLAR: u16 = 61; + +/// Grace period before an old guardian set expires (24 hours in seconds). +/// +/// After a guardian set upgrade, the old set remains valid for this duration +/// to allow in-flight VAAs signed by the previous guardians to be processed. +pub const GUARDIAN_SET_EXPIRATION_TIME: u32 = 86400; + +/// Module identifier for Wormhole Core governance actions. +/// +/// Right-padded ASCII "Core" in a 32-byte field. Governance payloads must +/// contain this exact value to be recognized as Core contract actions. +pub const MODULE_CORE: [u8; 32] = { + let mut bytes = [0u8; 32]; + bytes[28] = b'C'; + bytes[29] = b'o'; + bytes[30] = b'r'; + bytes[31] = b'e'; + bytes +}; + +// ========== Governance Action IDs ========== + +/// Action ID for contract WASM upgrade (governance action 1). +pub const ACTION_CONTRACT_UPGRADE: u8 = 1; + +/// Action ID for guardian set upgrade (governance action 2). +pub const ACTION_GUARDIAN_SET_UPGRADE: u8 = 2; + +/// Action ID for setting the message fee (governance action 3). +pub const ACTION_SET_MESSAGE_FEE: u8 = 3; + +/// Action ID for transferring accumulated fees (governance action 4). +pub const ACTION_TRANSFER_FEES: u8 = 4; + +// ========== Token Constants ========== + +/// Native XLM token symbol for Stellar Asset Contract. +pub const NATIVE_TOKEN_SYMBOL: &str = "native"; + +/// Native XLM Stellar Asset Contract address (deterministic, same on all +/// networks). +pub const NATIVE_TOKEN_ADDRESS: &str = "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"; + +// ========== Storage Configuration ========== + +/// TTL threshold for persistent storage renewal (approx. 5.8 days at +/// 5s/ledger). +/// +/// Storage entries are extended when their remaining TTL falls below this +/// threshold. +pub const STORAGE_TTL_THRESHOLD: u32 = 100_000; + +/// TTL extension amount for persistent storage (approx. 58 days at 5s/ledger). +/// +/// When extending TTL, entries are renewed to live this many additional +/// ledgers. +pub const STORAGE_TTL_EXTENSION: u32 = 1_000_000; + +// ========== VAA Structure Constants ========== + +/// Minimum VAA header size: version (1) + guardian_set_index (4) + +/// num_signatures (1). +pub const VAA_HEADER_MIN_LENGTH: u32 = 6; + +// ========== Payload Structure Constants ========== + +/// U256 padding bytes to skip when reading u64 values from Ethereum-compatible +/// payloads. +/// +/// Governance payloads encode amounts as 32-byte U256 for Ethereum +/// compatibility. On Soroban we read only the low-order 8 bytes as u64, +/// skipping the first 24. +pub const U256_PADDING_BYTES: u32 = 24; + +/// Minimum Contract Upgrade payload: module (32) + action (1) + chain (2) + +/// hash (32). +pub const CONTRACT_UPGRADE_PAYLOAD_MIN_LENGTH: u32 = 67; + +/// Minimum Guardian Set Upgrade payload: header (35) + index (4) + count (1). +/// +/// Actual length depends on guardian count (add 20 bytes per guardian). +pub const GUARDIAN_SET_UPGRADE_PAYLOAD_MIN_LENGTH: u32 = 40; + +/// Minimum Set Message Fee payload: module (32) + action (1) + chain (2) + fee +/// U256 (32). +pub const SET_MESSAGE_FEE_PAYLOAD_MIN_LENGTH: u32 = 67; + +/// Minimum Transfer Fees payload: header (35) + amount U256 (32) + recipient +/// (32). +pub const TRANSFER_FEES_PAYLOAD_MIN_LENGTH: u32 = 99; diff --git a/stellar/contracts/wormhole-soroban-client/src/error.rs b/stellar/contracts/wormhole-soroban-client/src/error.rs new file mode 100644 index 00000000000..7214187d8af --- /dev/null +++ b/stellar/contracts/wormhole-soroban-client/src/error.rs @@ -0,0 +1,84 @@ +//! Error types for the Wormhole Core contract. +//! +//! All contract operations return `Result`. Error codes are +//! grouped by category (VAA, governance, storage, fees) with reserved numeric +//! ranges for future expansion. + +use soroban_sdk::contracterror; + +/// Errors that can occur during Wormhole Core contract operations. +/// +/// Error codes are organized into ranges: +/// - 1-19: VAA parsing and verification errors +/// - 30-39: Governance action errors +/// - 40-49: Storage and guardian set errors +/// - 50-59: Fee-related errors +/// - 60+: Message posting errors +#[contracterror] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[repr(u32)] +pub enum WormholeError { + // ========== VAA Errors (1-19) ========== + /// VAA bytes are malformed or truncated. + InvalidVAAFormat = 1, + /// Referenced guardian set index does not exist. + InvalidGuardianSetIndex = 2, + /// Guardian set used for signing has expired. + GuardianSetExpired = 3, + /// VAA has fewer signatures than required quorum. + InsufficientSignatures = 4, + /// Guardian signature indices are not in ascending order. + SignaturesNotAscending = 5, + /// Signature references a guardian index beyond set size. + GuardianIndexOutOfBounds = 6, + /// ECDSA signature verification failed. + InvalidSignature = 7, + /// VAA has already been processed (replay attempt). + VAAAlreadyProcessed = 8, + /// Emitter address is malformed or invalid. + InvalidEmitterAddress = 9, + /// Payload bytes are malformed or insufficient length. + InvalidPayload = 10, + + // ========== Governance Errors (30-39) ========== + /// Governance payload module identifier is not "Core". + InvalidGovernanceModule = 30, + /// Governance action ID does not match expected action. + InvalidGovernanceAction = 31, + /// Governance VAA chain ID is not valid for this contract. + InvalidGovernanceChain = 32, + /// Governance VAA emitter address is not authorized. + InvalidGovernanceEmitter = 33, + /// New guardian set index is not current + 1. + InvalidGuardianSetSequence = 34, + /// Guardian set must contain at least one guardian. + EmptyGuardianSet = 35, + /// Governance VAA has already been consumed (replay protection). + GovernanceVAAAlreadyConsumed = 36, + + // ========== Storage Errors (40-49) ========== + /// Generic storage operation failure. + StorageError = 40, + /// Requested guardian set does not exist in storage. + GuardianSetNotFound = 41, + /// Cannot overwrite an existing guardian set. + GuardianSetAlreadyExists = 42, + /// Requested address hash does not exist in storage. + AddressNotFound = 43, + + // ========== Fee Errors (50-59) ========== + /// Emitter has not approved sufficient fee for message posting. + InsufficientFeePaid = 50, + /// Contract balance too low for requested fee transfer. + InsufficientFees = 51, + /// Fee transfer recipient address is invalid. + InvalidRecipient = 52, + /// Token transfer operation failed. + TransferFailed = 53, + /// Fee amount is invalid (e.g., exceeds safe limits). + InvalidFeeAmount = 54, + + // ========== Message Errors (60+) ========== + /// Consistency level value is not recognized. + InvalidConsistencyLevel = 60, +} diff --git a/stellar/contracts/wormhole-soroban-client/src/lib.rs b/stellar/contracts/wormhole-soroban-client/src/lib.rs new file mode 100644 index 00000000000..ab9c62d37b5 --- /dev/null +++ b/stellar/contracts/wormhole-soroban-client/src/lib.rs @@ -0,0 +1,304 @@ +//! Wormhole Core Contract Interface for Stellar/Soroban. +//! +//! This crate provides the public API for interacting with the Wormhole Core +//! contract. External contracts should depend only on this interface crate for +//! smaller WASM binaries, while the implementation lives in +//! `wormhole-contract`. +//! +//! # Key Types +//! +//! - [`VAA`] - Verifiable Action Approval, the core cross-chain message format +//! - [`GuardianSetInfo`] - Guardian set metadata stored on-chain +//! - [`ConsistencyLevel`] - Finality requirements for message attestation +//! - [`WormholeError`] - All possible error conditions +//! +//! # Example +//! +//! ```ignore +//! use wormhole_soroban_client::{WormholeCoreInterface, VAA, WormholeError}; +//! +//! // Parse and verify a VAA +//! let vaa = VAA::try_from((&env, &vaa_bytes))?; +//! client.verify_vaa(&vaa_bytes)?; +//! +//! // Post a cross-chain message (emitter is always treated as a contract) +//! let sequence = client.post_message(&emitter, nonce, &payload, ConsistencyLevel::Finalized)?; +//! ``` + +#![no_std] + +pub mod bytes_reader; +pub mod constants; +pub mod error; +pub mod types; + +pub use bytes_reader::BytesReader; +pub use constants::*; +pub use error::WormholeError; +pub use types::*; + +use soroban_sdk::{Address, Bytes, BytesN, Env, contractclient}; + +/// Computes the canonical Wormhole lookup hash for a Soroban address. +/// +/// The hash input is the address StrKey string bytes. +pub fn hash_address(env: &Env, address: &Address) -> BytesN<32> { + env.crypto() + .keccak256(&address.to_string().to_bytes()) + .to_bytes() +} + +/// Complete public interface for the Wormhole Core contract. +/// +/// Defines all contract entry points for VAA verification, governance actions, +/// cross-chain message posting, and state queries. The `wormhole-contract` +/// crate implements this trait with the `#[contractimpl]` macro. +/// +/// # Security Model +/// +/// - Governance actions require VAAs signed by a quorum (13/19) of guardians +/// - VAAs are consumed after use to prevent replay attacks +/// - The contract is its own admin—upgrades require guardian consensus +/// +/// # Initialization +/// +/// The contract is initialized via `__constructor` at deployment time. +/// Constructor arguments (initial guardians and governance emitter) are passed +/// during the `stellar contract deploy` command after `--`. +#[contractclient(name = "WormholeClient")] +pub trait WormholeCoreInterface { + // ========== VAA Verification ========== + + /// Verify a complete VAA (Verifiable Action Approval). + /// Parses the VAA and verifies all guardian signatures. + /// + /// # Arguments + /// * `vaa_bytes` - Serialized VAA bytes + /// + /// # Returns + /// `true` if VAA is valid and properly signed + /// + /// # Errors + /// * `Error::InvalidVAAFormat` - Malformed VAA bytes + /// * `Error::GuardianSetNotFound` - Guardian set not found + /// * `Error::GuardianSetExpired` - Guardian set has expired + /// * `Error::InsufficientSignatures` - Not enough signatures for quorum + /// * `Error::InvalidSignature` - Invalid guardian signature + fn verify_vaa(env: Env, vaa_bytes: Bytes) -> Result<(), WormholeError>; + + /// Parse a VAA structure without signature verification. + /// + /// # Arguments + /// * `vaa_bytes` - Serialized VAA bytes + /// + /// # Returns + /// Parsed VAA structure + /// + /// # Errors + /// * `Error::InvalidVAAFormat` - Malformed VAA bytes + fn parse_vaa(env: Env, vaa_bytes: Bytes) -> Result; + + /// Parse and verify a VAA in a single call. + /// + /// Equivalent to calling `verify_vaa` then `parse_vaa`, but parses the + /// VAA only once. This is the recommended entry point for integrators + /// who need both the parsed structure and signature verification. + /// + /// # Arguments + /// * `vaa_bytes` - Serialized VAA bytes + /// + /// # Returns + /// Parsed and verified VAA structure + /// + /// # Errors + /// * `Error::InvalidVAAFormat` - Malformed VAA bytes + /// * `Error::GuardianSetNotFound` - Guardian set not found + /// * `Error::GuardianSetExpired` - Guardian set has expired + /// * `Error::InsufficientSignatures` - Not enough signatures for quorum + /// * `Error::InvalidSignature` - Invalid guardian signature + fn parse_and_verify_vaa(env: Env, vaa_bytes: Bytes) -> Result; + + // ========== Governance Actions ========== + + /// Submit a contract upgrade governance VAA. + /// Requires valid VAA signed by current guardian set. + /// + /// # Arguments + /// * `vaa_bytes` - Serialized governance VAA containing upgrade payload + /// + /// # Errors + /// * VAA verification errors + /// * `Error::InvalidGovernanceModule` - Wrong module in payload + /// * `Error::InvalidGovernanceAction` - Wrong action ID + /// * `Error::InvalidGovernanceChain` - Wrong chain ID + /// * `Error::GovernanceVAAAlreadyConsumed` - VAA already processed + fn submit_contract_upgrade(env: Env, vaa_bytes: Bytes) -> Result<(), WormholeError>; + + /// Submit a guardian set upgrade governance VAA. + /// Requires valid VAA signed by current guardian set. + /// + /// # Arguments + /// * `vaa_bytes` - Serialized governance VAA containing guardian set + /// upgrade payload + /// + /// # Errors + /// * VAA verification errors + /// * Governance validation errors + /// * `Error::InvalidGuardianSetSequence` - New index not sequential + /// * `Error::EmptyGuardianSet` - No guardians in new set + fn submit_guardian_set_upgrade(env: Env, vaa_bytes: Bytes) -> Result<(), WormholeError>; + + /// Submit a set message fee governance VAA. + /// Requires valid VAA signed by current guardian set. + /// + /// # Arguments + /// * `vaa_bytes` - Serialized governance VAA containing fee update payload + /// + /// # Errors + /// * VAA verification errors + /// * Governance validation errors + fn submit_set_message_fee(env: Env, vaa_bytes: Bytes) -> Result<(), WormholeError>; + + /// Submit a transfer fees governance VAA. + /// Requires valid VAA signed by current guardian set. + /// + /// # Arguments + /// * `vaa_bytes` - Serialized governance VAA containing fee transfer + /// payload + /// + /// # Errors + /// * VAA verification errors + /// * Governance validation errors + /// * `Error::InsufficientFees` - Not enough fees to transfer + /// * `Error::TransferFailed` - Token transfer failed + fn submit_transfer_fees(env: Env, vaa_bytes: Bytes) -> Result<(), WormholeError>; + + // ========== Message Posting ========== + + /// Post a cross-chain message to be attested by Guardians. + /// The emitter is always treated as a contract address. Collects message + /// fee if configured. + /// + /// # Arguments + /// * `emitter` - Contract address acting as the message emitter (must + /// authorize) + /// * `nonce` - Unique nonce for the message + /// * `payload` - Message payload bytes + /// * `consistency_level` - Finality requirement (Confirmed or Finalized) + /// + /// # Returns + /// Sequence number assigned to the message + /// + /// # Errors + /// * `Error::InsufficientFeePaid` - Fee not paid (requires prior token + /// approval) + fn post_message( + env: Env, + emitter: Address, + nonce: u32, + payload: Bytes, + consistency_level: ConsistencyLevel, + ) -> Result; + + // ========== State Queries ========== + + /// Get the current active guardian set index. + /// + /// # Returns + /// Index of the current guardian set + fn get_current_guardian_set_index(env: Env) -> u32; + + /// Get a guardian set by index. + /// + /// # Arguments + /// * `index` - Guardian set index + /// + /// # Returns + /// Guardian set information + /// + /// # Errors + /// * `Error::GuardianSetNotFound` - Guardian set does not exist + fn get_guardian_set(env: Env, index: u32) -> Result; + + /// Get the expiry timestamp for a guardian set. + /// + /// # Arguments + /// * `index` - Guardian set index + /// + /// # Returns + /// Expiry timestamp, or None if not expired + fn get_guardian_set_expiry(env: Env, index: u32) -> Option; + + /// Get the current sequence number for an emitter. + /// + /// # Arguments + /// * `emitter` - Emitter address + /// + /// # Returns + /// Next sequence number for the emitter + fn get_emitter_sequence(env: Env, emitter: Address) -> u64; + + /// Get the current message fee in stroops (10^-7 XLM). + /// + /// # Returns + /// Message fee in stroops + fn get_message_fee(env: Env) -> u64; + + /// Check if a governance VAA has been consumed (replay protection). + /// + /// # Arguments + /// * `vaa_bytes` - Serialized VAA bytes + /// + /// # Returns + /// `true` if VAA has been consumed + /// + /// # Errors + /// * `Error::InvalidVAAFormat` - Malformed VAA bytes + fn is_governance_vaa_consumed(env: Env, vaa_bytes: Bytes) -> Result; + + // ========== Protocol Constants ========== + + /// Get the Wormhole chain ID for Stellar (61). + /// + /// # Returns + /// Chain ID as u32 + fn get_chain_id() -> u32; + + /// Get the governance chain ID (Solana = 1). + /// + /// # Returns + /// Governance chain ID + fn get_governance_chain_id() -> u32; + + /// Get the governance emitter address. + /// + /// # Returns + /// 32-byte governance emitter address + fn get_governance_emitter(env: Env) -> BytesN<32>; + + /// Record a Soroban address in the hash lookup table. + /// + /// Returns the canonical hash used as the lookup key. + fn record_address(env: Env, address: Address) -> Result, WormholeError>; + + /// Resolve a previously recorded address hash. + fn get_address_from_hash(env: Env, hash: BytesN<32>) -> Result; +} + +#[cfg(test)] +mod tests { + use super::*; + use soroban_sdk::testutils::Address as _; + + #[test] + fn test_hash_address_uses_strkey_string_bytes() { + let env = Env::default(); + let address = Address::generate(&env); + let expected = env + .crypto() + .keccak256(&address.to_string().to_bytes()) + .to_bytes(); + + assert_eq!(hash_address(&env, &address), expected); + } +} diff --git a/stellar/contracts/wormhole-soroban-client/src/types.rs b/stellar/contracts/wormhole-soroban-client/src/types.rs new file mode 100644 index 00000000000..653331846a1 --- /dev/null +++ b/stellar/contracts/wormhole-soroban-client/src/types.rs @@ -0,0 +1,275 @@ +//! Public type definitions for the Wormhole Core contract. +//! +//! Contains the core data structures for VAA handling, guardian sets, and +//! cross-chain messaging. External contracts should use these types when +//! integrating with Wormhole. + +use crate::{BytesReader, VAA_HEADER_MIN_LENGTH, WormholeError}; +use core::convert::TryFrom; +use soroban_sdk::{Bytes, BytesN, Env, Vec, contracttype}; + +// ========== VAA Types ========== + +/// ECDSA signature from a Wormhole guardian. +/// +/// Each signature contains the guardian's index for lookup and the secp256k1 +/// ECDSA signature components (r, s, v) used to recover the signer's Ethereum +/// address. +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub struct Signature { + /// Position of the signing guardian in the guardian set (0-indexed). + pub guardian_index: u32, + /// ECDSA signature r component (32 bytes). + pub r: BytesN<32>, + /// ECDSA signature s component (32 bytes). + pub s: BytesN<32>, + /// ECDSA recovery ID (0 or 1, stored as u32 for Soroban compatibility). + pub v: u32, +} + +/// Verifiable Action Approval (VAA) - a cross-chain message attested by +/// guardians. +/// +/// VAAs are the core primitive for Wormhole's cross-chain messaging. They +/// contain a message body signed by a quorum of guardians (13 of 19 on +/// mainnet). VAAs can carry arbitrary payloads including governance actions, +/// token transfers, or application-specific data. +/// +/// Parse from bytes using `VAA::try_from((&env, &bytes))`. +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub struct VAA { + // ===== Header ===== + /// VAA format version (currently always 1). + pub version: u32, + /// Index of the guardian set that produced these signatures. + pub guardian_set_index: u32, + /// Guardian signatures attesting to the message body. + pub signatures: Vec, + + // ===== Body (signed data) ===== + /// Unix timestamp when the message was observed by guardians. + pub timestamp: u32, + /// Application-defined nonce for deduplication. + pub nonce: u32, + /// Wormhole chain ID of the source blockchain. + pub emitter_chain: u32, + /// Address of the emitting contract on the source chain. + pub emitter_address: BytesN<32>, + /// Auto-incrementing sequence number from the emitter. + pub sequence: u64, + /// Finality level requested when the message was posted. + pub consistency_level: ConsistencyLevel, + /// Application-specific payload data. + pub payload: Bytes, +} + +// ========== Guardian Set Types ========== + +/// Metadata for a guardian set stored on-chain. +/// +/// Guardian sets are indexed sequentially starting from 0. Only the current set +/// can sign new VAAs, but expired sets remain valid for a grace period to allow +/// in-flight VAAs to be processed. +#[contracttype] +#[derive(Clone, Debug)] +pub struct GuardianSetInfo { + /// Ethereum addresses (20 bytes each) of guardians in this set. + pub keys: Vec>, + /// Ledger timestamp when this guardian set was activated. + pub creation_time: u64, +} + +// ========== Message Types ========== + +/// Finality requirement for cross-chain message attestation. +/// +/// Determines how many block confirmations guardians wait before signing. +/// Higher levels provide stronger finality guarantees but increase latency. +#[contracttype] +#[derive(Clone, Copy, Debug, PartialEq)] +#[repr(u8)] +pub enum ConsistencyLevel { + /// Standard confirmation (~1 block, fastest). + Confirmed = 1, + /// Full finality (chain-specific, most secure). + Finalized = 32, +} + +impl TryFrom for ConsistencyLevel { + type Error = crate::WormholeError; + + /// Converts a raw byte to a `ConsistencyLevel`. + /// + /// Only values 1 (`Confirmed`) and 32 (`Finalized`) are valid. + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(ConsistencyLevel::Confirmed), + 32 => Ok(ConsistencyLevel::Finalized), + _ => Err(crate::WormholeError::InvalidConsistencyLevel), + } + } +} + +/// Serializable representation of a posted cross-chain message. +/// +/// Created when a message is posted via `post_message`. The message data is +/// hashed and stored for later verification. Guardians observe these events +/// and produce VAAs attesting to their contents. +#[contracttype] +#[derive(Clone, Debug, PartialEq)] +pub struct PostedMessageData { + /// Ledger timestamp when the message was posted. + pub timestamp: u32, + /// Caller-provided nonce for application-level deduplication. + pub nonce: u32, + /// Wormhole chain ID of this blockchain (61 for Stellar). + pub emitter_chain: u32, + /// 32-byte contract ID of the emitter contract. + pub emitter_address: BytesN<32>, + /// Auto-incrementing sequence number for this emitter. + pub sequence: u64, + /// Requested finality level for guardian attestation. + pub consistency_level: ConsistencyLevel, + /// Application-specific message payload. + pub payload: Bytes, +} + +// ========== Type Implementations ========== + +impl Signature { + /// Parses a 66-byte signature from the reader. + /// + /// Wire format: guardian_index (1) + r (32) + s (32) + v (1) = 66 bytes. + pub fn parse_from_reader(reader: &mut BytesReader) -> Result { + let guardian_index = u32::from(reader.read_u8()?); + let r = reader.read_bytes_n::<32>()?; + let s = reader.read_bytes_n::<32>()?; + let v = u32::from(reader.read_u8()?); + + Ok(Signature { + guardian_index, + r, + s, + v, + }) + } +} + +impl<'a> TryFrom<(&'a Env, &'a Bytes)> for VAA { + type Error = WormholeError; + + /// Parses a VAA from its wire format representation. + /// + /// Validates minimum length but does not verify signatures; use + /// `verify_vaa` or `verify_vaa_signatures` for full verification. + fn try_from(value: (&'a Env, &'a Bytes)) -> Result { + let (env, vaa_bytes) = value; + + if vaa_bytes.len() < VAA_HEADER_MIN_LENGTH { + return Err(WormholeError::InvalidVAAFormat); + } + + let mut reader = BytesReader::new(vaa_bytes); + + let version = u32::from(reader.read_u8()?); + if version != 1 { + return Err(WormholeError::InvalidVAAFormat); + } + let guardian_set_index = reader.read_u32_be()?; + let num_signatures = u32::from(reader.read_u8()?); + + let mut signatures = Vec::new(env); + for _ in 0..num_signatures { + let sig = Signature::parse_from_reader(&mut reader)?; + signatures.push_back(sig); + } + + let timestamp = reader.read_u32_be()?; + let nonce = reader.read_u32_be()?; + let emitter_chain = u32::from(reader.read_u16_be()?); + let emitter_address = reader.read_bytes_n::<32>()?; + let sequence = reader.read_u64_be()?; + let consistency_level = ConsistencyLevel::try_from(reader.read_u8()?)?; + let payload = reader.remaining_bytes(); + + Ok(VAA { + version, + guardian_set_index, + signatures, + timestamp, + nonce, + emitter_chain, + emitter_address, + sequence, + consistency_level, + payload, + }) + } +} + +impl VAA { + /// Serializes the VAA body for hashing during signature verification. + /// + /// Returns the 51-byte fixed header plus variable payload. The body + /// excludes the version, guardian set index, and signatures—guardians + /// sign only this data. Output is big-endian encoded to match the + /// Wormhole wire format. + pub fn serialize_body(&self, env: &Env) -> Bytes { + let mut bytes = Bytes::new(env); + + // timestamp (4 bytes, big-endian) + for i in (0..4).rev() { + bytes.push_back(((self.timestamp >> (i * 8)) & 0xFF) as u8); + } + + // nonce (4 bytes, big-endian) + for i in (0..4).rev() { + bytes.push_back(((self.nonce >> (i * 8)) & 0xFF) as u8); + } + + // emitter_chain (2 bytes, big-endian) + bytes.push_back((self.emitter_chain >> 8) as u8); + bytes.push_back((self.emitter_chain & 0xFF) as u8); + + // emitter_address (32 bytes) + for byte in self.emitter_address.to_array().iter() { + bytes.push_back(*byte); + } + + // sequence (8 bytes, big-endian) + for i in (0..8).rev() { + bytes.push_back(((self.sequence >> (i * 8)) & 0xFF) as u8); + } + + // consistency_level (1 byte) + bytes.push_back(self.consistency_level as u8); + + // payload (variable length) + bytes.append(&self.payload); + + bytes + } + + /// Extracts the body bytes from raw VAA data without full parsing. + /// + /// Computes the body offset by reading the signature count and slicing + /// past the header and signatures. Useful for hash computation when + /// only the body is needed. + pub fn get_body_bytes(vaa_bytes: &Bytes) -> Result { + if vaa_bytes.len() < VAA_HEADER_MIN_LENGTH { + return Err(WormholeError::InvalidVAAFormat); + } + + // Calculate body offset: 6 (header) + 66 * num_signatures + let num_sigs = u32::from(vaa_bytes.get(5).ok_or(WormholeError::InvalidVAAFormat)?); + let body_offset = 6u32.saturating_add(66u32.saturating_mul(num_sigs)); + + if body_offset >= vaa_bytes.len() { + return Err(WormholeError::InvalidVAAFormat); + } + + Ok(vaa_bytes.slice(body_offset..)) + } +} diff --git a/stellar/rust-toolchain.toml b/stellar/rust-toolchain.toml new file mode 100644 index 00000000000..c0418380445 --- /dev/null +++ b/stellar/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.89.0" +components = ["rustfmt", "clippy"] +targets = ["wasm32v1-none"] \ No newline at end of file diff --git a/stellar/rustfmt.toml b/stellar/rustfmt.toml new file mode 100644 index 00000000000..e459209deb8 --- /dev/null +++ b/stellar/rustfmt.toml @@ -0,0 +1,26 @@ +############################################################################## +# Rustfmt configuration – project-wide style guide +# +# Docs: https://rust-lang.github.io/rustfmt +############################################################################## + +# --- Imports ---------------------------------------------------------------- +reorder_imports = true # Alphabetise & group `use` items. +imports_granularity = "Crate" # Merge paths that share the same crate + # → e.g. `use std::{fmt, io};` +condense_wildcard_suffixes = true # Prefer `use foo::{self, Bar};` + # over two separate lines. + +# --- Readability ------------------------------------------------------------ +max_width = 100 # Hard column limit for all code +wrap_comments = true # Break comments to fit on the line +format_code_in_doc_comments= true # Rust-format code blocks in docs +use_field_init_shorthand = true # `{ x, y }` instead of `{ x: x, y: y }` +trailing_comma = "Vertical" # Comma-terminate every multi-line list + +# ---------- Implementation order ------------------------------------------- +reorder_impl_items = true # Deterministic ordering inside impl blocks + +# --- Misc ------------------------------------------------------------------- +edition = "2024" # Controls the edition of the Rust Style Guide + # to use for formatting (RFC 3338)