diff --git a/Cargo.lock b/Cargo.lock index db2b8a2..eb3789a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "addr2line" version = "0.24.2" @@ -27,6 +37,70 @@ dependencies = [ "generic-array", ] +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + +[[package]] +name = "agave-feature-set" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816a655a90bda7e1f2f41188b66eea4afa5e8241e15d33e8a95ebebb4a61c264" +dependencies = [ + "ahash", + "solana-epoch-schedule", + "solana-hash", + "solana-pubkey", + "solana-sha256-hasher", + "solana-svm-feature-set", +] + +[[package]] +name = "agave-reserved-account-keys" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a81224b65697d8e5a7d3bcc0f7d00107247c47b1769bfc0d1874b2b731ea33" +dependencies = [ + "agave-feature-set", + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.3", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -52,7 +126,7 @@ dependencies = [ "serde", "serde_json", "serde_qs", - "thiserror 2.0.12", + "thiserror 2.0.17", "tower-layer", "tower-service", "tracing", @@ -64,11 +138,26 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be8e0d4af7cc08353807aaf80722125a229bf2d67be7fe0b89163c648db3d223" dependencies = [ - "darling", + "darling 0.20.11", "quote", "syn 2.0.104", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -77,9 +166,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ecf116474faea3e30ecb03cb14548598ca8243d5316ce50f820e67b3e848473" +checksum = "b17c19591d57add4f0c47922877a48aae1f47074e3433436545f8948353b3bbb" dependencies = [ "alloy-consensus", "alloy-contract", @@ -100,6 +189,7 @@ dependencies = [ "alloy-signer-local", "alloy-transport", "alloy-transport-http", + "alloy-trie", ] [[package]] @@ -115,9 +205,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6093bc69509849435a2d68237a2e9fea79d27390c8e62f1e4012c460aabad8" +checksum = "6a0dd3ed764953a6b20458b2b7abbfdc93d20d14b38babe1a70fe631a443a9f1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -134,15 +224,16 @@ dependencies = [ "rand 0.8.5", "secp256k1", "serde", + "serde_json", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] name = "alloy-consensus-any" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d1cfed4fefd13b5620cb81cdb6ba397866ff0de514c1b24806e6e79cdff5570" +checksum = "9556182afa73cddffa91e64a5aa9508d5e8c912b3a15f26998d2388a824d2c7b" dependencies = [ "alloy-consensus", "alloy-eips", @@ -154,9 +245,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28074a21cd4f7c3a7ab218c4f38fae6be73944e1feae3b670c68b60bf85ca40" +checksum = "b19d7092c96defc3d132ee0d8969ca1b79ef512b5eda5c66e3065266b253adf2" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -171,7 +262,7 @@ dependencies = [ "futures", "futures-util", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -214,7 +305,7 @@ dependencies = [ "alloy-rlp", "crc", "serde", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -237,14 +328,14 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "serde", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] name = "alloy-eips" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5937e2d544e9b71000942d875cbc57965b32859a666ea543cc57aae5a06d602d" +checksum = "305fa99b538ca7006b0c03cfed24ec6d82beda67aac857ef4714be24231d15e6" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -257,14 +348,16 @@ dependencies = [ "derive_more", "either", "serde", + "serde_with", "sha2", + "thiserror 2.0.17", ] [[package]] name = "alloy-genesis" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51b4c13e02a8104170a4de02ccf006d7c233e6c10ab290ee16e7041e6ac221d" +checksum = "a272533715aefc900f89d51db00c96e6fd4f517ea081a12fea482a352c8c815c" dependencies = [ "alloy-eips", "alloy-primitives", @@ -301,24 +394,24 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b590caa6b6d8bc10e6e7a7696c59b1e550e89f27f50d1ee13071150d3a3e3f66" +checksum = "d91676d242c0ced99c0dd6d0096d7337babe9457cc43407d26aa6367fcf90553" dependencies = [ "alloy-primitives", "alloy-sol-types", "http 1.3.1", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", ] [[package]] name = "alloy-network" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36fe5af1fca03277daa56ad4ce5f6d623d3f4c2273ea30b9ee8674d18cefc1fa" +checksum = "77f82150116b30ba92f588b87f08fa97a46a1bd5ffc0d0597efdf0843d36bfda" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -337,14 +430,14 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] name = "alloy-network-primitives" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793df1e3457573877fbde8872e4906638fde565ee2d3bd16d04aad17d43dbf0e" +checksum = "223612259a080160ce839a4e5df0125ca403a1d5e7206cc911cea54af5d769aa" dependencies = [ "alloy-consensus", "alloy-eips", @@ -355,9 +448,9 @@ dependencies = [ [[package]] name = "alloy-node-bindings" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de75f0d0af3c6cb0bd3648e530289b2c542b7bf57e7d4296d1c29281418a476" +checksum = "3652a65bacfba0a169755090d4ecd7d3c63fa534b21d09b8e604dc2609760da6" dependencies = [ "alloy-genesis", "alloy-hardforks", @@ -369,7 +462,7 @@ dependencies = [ "rand 0.8.5", "serde_json", "tempfile", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "url", ] @@ -395,7 +488,7 @@ dependencies = [ "proptest", "rand 0.9.1", "ruint", - "rustc-hash 2.1.1", + "rustc-hash", "serde", "sha3", "tiny-keccak", @@ -403,9 +496,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59879a772ebdcde9dc4eb38b2535d32e8503d3175687cc09e763a625c5fcf32" +checksum = "f7283b81b6f136100b152e699171bc7ed8184a58802accbc91a7df4ebb944445" dependencies = [ "alloy-chains", "alloy-consensus", @@ -425,18 +518,17 @@ dependencies = [ "async-stream", "async-trait", "auto_impl", - "dashmap", + "dashmap 6.1.0", "either", "futures", "futures-utils-wasm", - "http 1.3.1", "lru", "parking_lot", "pin-project 1.1.10", "reqwest", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "url", @@ -467,9 +559,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f060e3bb9f319eb01867a2d6d1ff9e0114e8877f5ca8f5db447724136106cae" +checksum = "1154b12d470bef59951c62676e106f4ce5de73b987d86b9faa935acebb138ded" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -490,9 +582,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d47b637369245d2dafef84b223b1ff5ea59e6cd3a98d2d3516e32788a0b216df" +checksum = "47ab76bf97648a1c6ad8fb00f0d594618942b5a9e008afbfb5c8a8fca800d574" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -502,9 +594,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b1f499acb3fc729615147bc113b8b798b17379f19d43058a687edc5792c102" +checksum = "456cfc2c1677260edbd7ce3eddb7de419cb46de0e9826c43401f42b0286a779a" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -514,9 +606,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e26b4dd90b33bd158975307fb9cf5fafa737a0e33cbb772a8648bf8be13c104" +checksum = "23cc57ee0c1ac9fb14854195fc249494da7416591dc4a4d981ddfd5dd93b9bce" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -525,9 +617,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46586ec3c278639fc0e129f0eb73dbfa3d57f683c44b2ff5e066fab7ba63fa1f" +checksum = "6d7d47bca1a2a1541e4404aa38b7e262bb4dffd9ac23b4f178729a4ddc5a5caa" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -537,18 +629,18 @@ dependencies = [ "alloy-rlp", "alloy-serde", "alloy-sol-types", - "itertools 0.14.0", + "itertools 0.13.0", "serde", "serde_json", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] name = "alloy-serde" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1722bc30feef87cc0fa824e43c9013f9639cc6c037be7be28a31361c788be2" +checksum = "6a8468f1a7f9ee3bae73c24eead0239abea720dbf7779384b9c7e20d51bfb6b0" dependencies = [ "alloy-primitives", "serde", @@ -557,9 +649,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3674beb29e68fbbc7be302b611cf35fe07b736e308012a280861df5a2361395" +checksum = "33387c90b0a5021f45a5a77c2ce6c49b8f6980e66a318181468fb24cea771670" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -569,32 +661,33 @@ dependencies = [ "either", "elliptic-curve", "k256", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] name = "alloy-signer-aws" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605b1659b320b16708bb84b41038b2f0e2a60d90972c28319c4f5a4866f0efd4" +checksum = "83bf90f2355769ad93f790b930434b8d3d2948317f3e484de458010409024462" dependencies = [ "alloy-consensus", "alloy-network", "alloy-primitives", "alloy-signer", "async-trait", + "aws-config", "aws-sdk-kms", "k256", "spki", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", ] [[package]] name = "alloy-signer-gcp" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a207671ef0bf6f61e9c80c9ccb6d203439071252fb35886d6a89aae5431cd9c" +checksum = "f2c768277bfc541a7aab3c3a079d838b3925b6c2f367e29be943f002ecde2712" dependencies = [ "alloy-consensus", "alloy-network", @@ -604,15 +697,15 @@ dependencies = [ "gcloud-sdk", "k256", "spki", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", ] [[package]] name = "alloy-signer-ledger" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce5e8c97a526d39052035a99652b9cfacf0d646d4a3625fac9c919d20a46fb0" +checksum = "7ccf703581d2c0b2dd2d5bd235de2b5ccfd6bdc43e750ac767327fe0fb0b4ea1" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -624,15 +717,15 @@ dependencies = [ "coins-ledger", "futures-util", "semver 1.0.26", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", ] [[package]] name = "alloy-signer-local" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad7094c39cd41b03ed642145b0bd37251e31a9cf2ed19e1ce761f089867356a6" +checksum = "b55d9e795c85e36dcea08786d2e7ae9b73cb554b6bea6ac4c212def24e1b4d03" dependencies = [ "alloy-consensus", "alloy-network", @@ -641,7 +734,7 @@ dependencies = [ "async-trait", "k256", "rand 0.8.5", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -719,12 +812,13 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f89bec2f59a41c0e259b6fe92f78dfc49862c17d10f938db9c33150d5a7f42b6" +checksum = "702002659778d89a94cd4ff2044f6b505460df6c162e2f47d1857573845b0ace" dependencies = [ "alloy-json-rpc", "alloy-primitives", + "auto_impl", "base64 0.22.1", "derive_more", "futures", @@ -732,7 +826,7 @@ dependencies = [ "parking_lot", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tower", "tracing", @@ -742,9 +836,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3615ec64d775fec840f4e9d5c8e1f739eb1854d8d28db093fb3d4805e0cb53" +checksum = "0d6bdc0830e5e8f08a4c70a4c791d400a86679c694a3b4b986caf26fad680438" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -773,12 +867,12 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.0.23" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f916ff6d52f219c44a9684aea764ce2c7e1d53bd4a724c9b127863aeacc30bb" +checksum = "7bf39928a5e70c9755d6811a2928131b53ba785ad37c8bf85c90175b5d43b818" dependencies = [ "alloy-primitives", - "darling", + "darling 0.21.3", "proc-macro2", "quote", "syn 2.0.104", @@ -813,9 +907,9 @@ checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arc-swap" @@ -834,7 +928,7 @@ dependencies = [ "ark-serialize 0.3.0", "ark-std 0.3.0", "derivative", - "num-bigint", + "num-bigint 0.4.6", "num-traits", "paste", "rustc_version 0.3.3", @@ -854,7 +948,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools 0.10.5", - "num-bigint", + "num-bigint 0.4.6", "num-traits", "paste", "rustc_version 0.4.1", @@ -887,7 +981,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "quote", "syn 1.0.109", @@ -899,7 +993,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "proc-macro2", "quote", @@ -924,7 +1018,7 @@ checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ "ark-std 0.4.0", "digest 0.10.7", - "num-bigint", + "num-bigint 0.4.6", ] [[package]] @@ -953,6 +1047,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" @@ -962,12 +1062,69 @@ dependencies = [ "serde", ] +[[package]] +name = "ascii" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure 0.12.6", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + [[package]] name = "async-compression" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40f6024f3f856663b45fd0c9b6f2024034a702f453549449e0d84a305900dad4" dependencies = [ + "brotli", "flate2", "futures-core", "memchr", @@ -977,11 +1134,11 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] @@ -1010,9 +1167,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", @@ -1097,9 +1254,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.13.3" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" +checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" dependencies = [ "aws-lc-sys", "zeroize", @@ -1107,15 +1264,16 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.30.0" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" +checksum = "a2b715a6010afb9e457ca2b7c9d2b9c344baa8baed7b38dc476034c171b32575" dependencies = [ "bindgen", "cc", "cmake", "dunce", "fs_extra", + "libloading", ] [[package]] @@ -1305,7 +1463,7 @@ dependencies = [ "hyper-util", "pin-project-lite", "rustls 0.21.12", - "rustls 0.23.28", + "rustls 0.23.32", "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", @@ -1527,6 +1685,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -1555,6 +1719,15 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bincode" version = "2.0.1" @@ -1577,25 +1750,22 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.5" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "bitflags 2.9.1", "cexpr", "clang-sys", - "itertools 0.10.5", - "lazy_static", - "lazycell", + "itertools 0.13.0", "log", "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash", "shlex", "syn 2.0.104", - "which", ] [[package]] @@ -1656,6 +1826,20 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake3" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "digest 0.10.7", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -1677,18 +1861,101 @@ dependencies = [ "zeroize", ] +[[package]] +name = "borsh" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bumpalo" version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +[[package]] +name = "bv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" +dependencies = [ + "feature-probe", + "serde", +] + [[package]] name = "byte-slice-cast" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -1729,6 +1996,16 @@ dependencies = [ "serde", ] +[[package]] +name = "caps" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b" +dependencies = [ + "libc", + "thiserror 1.0.69", +] + [[package]] name = "cast" version = "0.3.0" @@ -1746,6 +2023,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -1767,6 +2050,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[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.104", +] + [[package]] name = "chacha20" version = "0.9.1" @@ -1803,7 +2097,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -1903,7 +2197,7 @@ dependencies = [ "hidapi-rusb", "js-sys", "log", - "nix", + "nix 0.26.4", "once_cell", "thiserror 1.0.69", "tokio", @@ -1912,6 +2206,19 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "combine" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" +dependencies = [ + "ascii", + "byteorder", + "either", + "memchr", + "unreachable", +] + [[package]] name = "combine" version = "4.6.7" @@ -1954,6 +2261,19 @@ dependencies = [ "yaml-rust2", ] +[[package]] +name = "console" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.61.2", +] + [[package]] name = "const-hex" version = "1.14.1" @@ -2013,6 +2333,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "convert_case" version = "0.6.0" @@ -2178,6 +2504,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "curve25519-dalek" version = "4.1.3" @@ -2187,8 +2522,11 @@ dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", + "digest 0.10.7", "fiat-crypto", + "rand_core 0.6.4", "rustc_version 0.4.1", + "serde", "subtle", "zeroize", ] @@ -2210,8 +2548,18 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "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]] @@ -2229,19 +2577,58 @@ dependencies = [ ] [[package]] -name = "darling_macro" -version = "0.20.11" +name = "darling_core" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ - "darling_core", + "fnv", + "ident_case", + "proc-macro2", "quote", + "serde", + "strsim", "syn 2.0.104", ] [[package]] -name = "dashmap" -version = "6.1.0" +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.104", +] + +[[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.104", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "dashmap" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ @@ -2253,6 +2640,12 @@ dependencies = [ "parking_lot_core", ] +[[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" @@ -2264,6 +2657,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint 0.4.6", + "num-traits", + "rusticata-macros", +] + [[package]] name = "deranged" version = "0.4.0" @@ -2274,6 +2681,12 @@ dependencies = [ "serde", ] +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + [[package]] name = "derivative" version = "2.2.0" @@ -2338,6 +2751,29 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "dlopen2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "dlv-list" version = "0.5.2" @@ -2374,6 +2810,43 @@ dependencies = [ "spki", ] +[[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 0.6.4", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "ed25519-dalek-bip32" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b49a684b133c4980d7ee783936af771516011c8cd15f429dbda77245e282f03" +dependencies = [ + "derivation-path", + "ed25519-dalek", + "hmac", + "sha2", +] + [[package]] name = "either" version = "1.15.0" @@ -2404,6 +2877,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -2435,7 +2914,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "utoipa", ] @@ -2449,13 +2928,17 @@ dependencies = [ "aws-credential-types", "aws-sdk-kms", "engine-aa-types", + "engine-solana-core", "moka", "schemars 0.8.22", "serde", "serde_json", "serde_with", + "solana-client", + "solana-commitment-config", + "solana-sdk", "thirdweb-core", - "thiserror 2.0.12", + "thiserror 2.0.17", "tower", "tracing", "twmq", @@ -2475,7 +2958,7 @@ dependencies = [ "serde", "serde_json", "serde_repr", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "vault-types", @@ -2486,11 +2969,13 @@ name = "engine-executors" version = "0.1.0" dependencies = [ "alloy", + "base64 0.22.1", "chrono", "engine-aa-core", "engine-aa-types", "engine-core", "engine-eip7702-core", + "engine-solana-core", "futures", "hex", "hmac", @@ -2501,15 +2986,38 @@ dependencies = [ "reqwest", "serde", "serde_json", + "serde_with", "sha2", + "solana-client", + "solana-commitment-config", + "solana-sdk", + "solana-transaction-status", + "spl-memo-interface", "thirdweb-core", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "twmq", "uuid", ] +[[package]] +name = "engine-solana-core" +version = "0.1.0" +dependencies = [ + "base64 0.22.1", + "hex", + "serde", + "serde_json", + "serde_with", + "solana-compute-budget-interface", + "solana-sdk", + "solana-transaction-status", + "thiserror 2.0.17", + "tracing", + "utoipa", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -2526,6 +3034,12 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "event-listener" version = "5.4.0" @@ -2543,10 +3057,22 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener", + "event-listener 5.4.0", "pin-project-lite", ] +[[package]] +name = "fastbloom" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27cea6e7f512d43b098939ff4d5a5d6fe3db07971e1d05176fe26c642d33f5b8" +dependencies = [ + "getrandom 0.3.3", + "rand 0.9.1", + "siphasher 1.0.1", + "wide", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -2575,6 +3101,12 @@ dependencies = [ "bytes", ] +[[package]] +name = "feature-probe" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" + [[package]] name = "ff" version = "0.13.1" @@ -2591,6 +3123,30 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "five8" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75b8549488b4715defcb0d8a8a1c1c76a80661b5fa106b4ca0e7fce59d7d875" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_const" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26dec3da8bc3ef08f2c04f61eab298c3ab334523e55f076354d6d6f613799a7b" +dependencies = [ + "five8_core", +] + +[[package]] +name = "five8_core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2551bf44bc5f776c15044b9b94153a00198be06743e262afaaa61f11ac7523a5" + [[package]] name = "fixed-hash" version = "0.8.0" @@ -2732,6 +3288,12 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.31" @@ -2809,6 +3371,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -2848,6 +3420,26 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "governor" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" +dependencies = [ + "cfg-if", + "dashmap 5.5.3", + "futures", + "futures-timer", + "no-std-compat", + "nonzero_ext", + "parking_lot", + "portable-atomic", + "quanta", + "rand 0.8.5", + "smallvec", + "spinning_top", +] + [[package]] name = "group" version = "0.13.0" @@ -2907,6 +3499,15 @@ dependencies = [ "crunchy", ] +[[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" @@ -2982,6 +3583,12 @@ dependencies = [ "rusb", ] +[[package]] +name = "histogram" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cb882ccb290b8646e554b157ab0b71e64e8d5bef775cd66b6531e52d302669" + [[package]] name = "hkdf" version = "0.12.4" @@ -3000,15 +3607,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "home" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "http" version = "0.2.12" @@ -3094,7 +3692,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -3147,13 +3745,13 @@ dependencies = [ "http 1.3.1", "hyper 1.6.0", "hyper-util", - "rustls 0.23.28", + "rustls 0.23.32", "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", "tokio-rustls 0.26.2", "tower-service", - "webpki-roots", + "webpki-roots 1.0.1", ] [[package]] @@ -3203,7 +3801,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -3390,6 +3988,19 @@ dependencies = [ "serde", ] +[[package]] +name = "indicatif" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" +dependencies = [ + "console", + "portable-atomic", + "unicode-width", + "unit-prefix", + "web-time", +] + [[package]] name = "inout" version = "0.1.4" @@ -3437,18 +4048,18 @@ dependencies = [ [[package]] name = "itertools" -version = "0.13.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itertools" -version = "0.14.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -3459,6 +4070,28 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine 4.6.7", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.33" @@ -3490,6 +4123,21 @@ dependencies = [ "serde", ] +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "jsonwebtoken" version = "9.3.1" @@ -3498,7 +4146,7 @@ checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" dependencies = [ "base64 0.22.1", "js-sys", - "pem", + "pem 3.0.5", "ring", "serde", "serde_json", @@ -3517,6 +4165,7 @@ dependencies = [ "once_cell", "serdect", "sha2", + "signature", ] [[package]] @@ -3544,17 +4193,11 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" -version = "0.2.174" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libloading" @@ -3584,12 +4227,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -3687,6 +4324,27 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "mime" version = "0.3.17" @@ -3739,7 +4397,7 @@ dependencies = [ "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "event-listener", + "event-listener 5.4.0", "futures-util", "loom", "parking_lot", @@ -3786,23 +4444,48 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset", + "memoffset 0.7.1", "pin-utils", ] [[package]] -name = "nom" -version = "7.1.3" +name = "nix" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "memchr", - "minimal-lexical", + "bitflags 2.9.1", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset 0.9.1", ] [[package]] -name = "nu-ansi-term" -version = "0.46.0" +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ @@ -3810,6 +4493,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint 0.2.6", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -3820,12 +4528,33 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "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.104", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -3835,6 +4564,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint 0.2.6", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -3871,6 +4623,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.104", @@ -3898,6 +4651,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -4051,6 +4813,24 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "pem" version = "3.0.5" @@ -4076,6 +4856,15 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "percentage" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd23b938276f14057220b707937bcb42fa76dda7560e57a2da30cb52d557937" +dependencies = [ + "num", +] + [[package]] name = "pest" version = "2.8.1" @@ -4083,7 +4872,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", - "thiserror 2.0.12", + "thiserror 2.0.17", "ucd-trie", ] @@ -4227,6 +5016,18 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.11.1" @@ -4370,7 +5171,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.104", @@ -4391,6 +5192,30 @@ version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi 0.11.1+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -4408,10 +5233,10 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.1", - "rustls 0.23.28", - "socket2", - "thiserror 2.0.12", + "rustc-hash", + "rustls 0.23.32", + "socket2 0.5.10", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -4424,15 +5249,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" dependencies = [ "bytes", + "fastbloom", "getrandom 0.3.3", "lru-slab", "rand 0.9.1", "ring", - "rustc-hash 2.1.1", - "rustls 0.23.28", + "rustc-hash", + "rustls 0.23.32", "rustls-pki-types", + "rustls-platform-verifier", "slab", - "thiserror 2.0.12", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -4447,7 +5274,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] @@ -4544,6 +5371,15 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.9.1", +] + [[package]] name = "rayon" version = "1.10.0" @@ -4574,16 +5410,16 @@ dependencies = [ "backon", "bytes", "cfg-if", - "combine", + "combine 4.6.7", "futures-channel", "futures-util", "itoa", - "num-bigint", + "num-bigint 0.4.6", "percent-encoding", "pin-project-lite", "ryu", "sha1_smol", - "socket2", + "socket2 0.5.10", "tokio", "tokio-util", "url", @@ -4670,14 +5506,15 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "async-compression", "base64 0.22.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2 0.4.11", @@ -4696,7 +5533,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.28", + "rustls 0.23.32", "rustls-native-certs 0.8.1", "rustls-pki-types", "serde", @@ -4715,7 +5552,22 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 1.0.1", +] + +[[package]] +name = "reqwest-middleware" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f17d28a6e6acfe1733fe24bcd30774d13bffa4b8a22535b4c8c98423088d4e" +dependencies = [ + "anyhow", + "async-trait", + "http 1.3.1", + "reqwest", + "serde", + "thiserror 1.0.69", + "tower-service", ] [[package]] @@ -4776,7 +5628,7 @@ dependencies = [ "bytes", "fastrlp 0.3.1", "fastrlp 0.4.0", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits", "parity-scale-codec", @@ -4823,12 +5675,6 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -4860,16 +5706,12 @@ dependencies = [ ] [[package]] -name = "rustix" -version = "0.38.44" +name = "rusticata-macros" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" dependencies = [ - "bitflags 2.9.1", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "nom", ] [[package]] @@ -4881,7 +5723,7 @@ dependencies = [ "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys 0.9.4", + "linux-raw-sys", "windows-sys 0.59.0", ] @@ -4899,16 +5741,16 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.28" +version = "0.23.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" dependencies = [ "aws-lc-rs", "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.3", + "rustls-webpki 0.103.7", "subtle", "zeroize", ] @@ -4956,6 +5798,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls 0.23.32", + "rustls-native-certs 0.8.1", + "rustls-platform-verifier-android", + "rustls-webpki 0.103.7", + "security-framework 3.2.0", + "security-framework-sys", + "webpki-root-certs 0.26.11", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -4968,9 +5837,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" dependencies = [ "aws-lc-rs", "ring", @@ -5002,6 +5871,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + [[package]] name = "same-file" version = "1.0.6" @@ -5202,13 +6080,23 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + [[package]] name = "serde-bool" version = "0.1.3" @@ -5218,11 +6106,30 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -5242,14 +6149,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -5272,7 +6180,7 @@ dependencies = [ "futures", "percent-encoding", "serde", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -5333,7 +6241,7 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", "syn 2.0.104", @@ -5349,6 +6257,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sha1_smol" version = "1.0.1" @@ -5367,104 +6286,2109 @@ dependencies = [ ] [[package]] -name = "sha3" -version = "0.10.8" +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "thiserror 2.0.17", + "time", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "solana-account" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e5a5c395c41a30f0e36fa487b8cda3280f0d9e4c7b461c0881fa23564f4c28" +dependencies = [ + "bincode 1.3.3", + "serde", + "serde_bytes", + "serde_derive", + "solana-account-info", + "solana-clock", + "solana-instruction-error", + "solana-pubkey", + "solana-sdk-ids", + "solana-sysvar", +] + +[[package]] +name = "solana-account-decoder" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4148b8ca32a5079336424da7f9fbcb114c0e14d847b4ed865d5c22aca75929" +dependencies = [ + "Inflector", + "base64 0.22.1", + "bincode 1.3.3", + "bs58", + "bv", + "serde", + "serde_derive", + "serde_json", + "solana-account", + "solana-account-decoder-client-types", + "solana-address-lookup-table-interface", + "solana-clock", + "solana-config-interface", + "solana-epoch-schedule", + "solana-fee-calculator", + "solana-instruction", + "solana-loader-v3-interface", + "solana-nonce", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-slot-hashes", + "solana-slot-history", + "solana-stake-interface", + "solana-sysvar", + "solana-vote-interface", + "spl-generic-token", + "spl-token-2022-interface", + "spl-token-group-interface", + "spl-token-interface", + "spl-token-metadata-interface", + "thiserror 2.0.17", + "zstd", +] + +[[package]] +name = "solana-account-decoder-client-types" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdc3319d4a03a8a2c40a366051612bab4cd8336245ad3254ce10ffc9d70c7c5" +dependencies = [ + "base64 0.22.1", + "bs58", + "serde", + "serde_derive", + "serde_json", + "solana-account", + "solana-pubkey", + "zstd", +] + +[[package]] +name = "solana-account-info" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f4691b69b172c687d218dd2f1f23fc7ea5e9aa79df9ac26dab3d8dd829ce48" +dependencies = [ + "bincode 1.3.3", + "serde", + "solana-program-error", + "solana-program-memory", + "solana-pubkey", +] + +[[package]] +name = "solana-address" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7a457086457ea9db9a5199d719dc8734dc2d0342fad0d8f77633c31eb62f19" +dependencies = [ + "borsh", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek", + "five8", + "five8_const", + "rand 0.8.5", + "serde", + "serde_derive", + "solana-atomic-u64", + "solana-define-syscall 3.0.0", + "solana-program-error", + "solana-sanitize", + "solana-sha256-hasher", +] + +[[package]] +name = "solana-address-lookup-table-interface" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2f56cac5e70517a2f27d05e5100b20de7182473ffd0035b23ea273307905987" +dependencies = [ + "bincode 1.3.3", + "bytemuck", + "serde", + "serde_derive", + "solana-clock", + "solana-instruction", + "solana-instruction-error", + "solana-pubkey", + "solana-sdk-ids", + "solana-slot-hashes", +] + +[[package]] +name = "solana-atomic-u64" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a933ff1e50aff72d02173cfcd7511bd8540b027ee720b75f353f594f834216d0" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "solana-big-mod-exp" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c80fb6d791b3925d5ec4bf23a7c169ef5090c013059ec3ed7d0b2c04efa085" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "solana-define-syscall 3.0.0", +] + +[[package]] +name = "solana-blake3-hasher" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa2e3bdac3339c6d0423275e45dafc5ac25f4d43bf344d026a3cc9a85e244a6" +dependencies = [ + "blake3", + "solana-define-syscall 3.0.0", + "solana-hash", +] + +[[package]] +name = "solana-borsh" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc402b16657abbfa9991cd5cbfac5a11d809f7e7d28d3bb291baeb088b39060e" +dependencies = [ + "borsh", +] + +[[package]] +name = "solana-client" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cea1ab3310e8225df043c5634fcc3da5e1249f066e451cefdc1848a3ff6583f" +dependencies = [ + "async-trait", + "bincode 1.3.3", + "dashmap 5.5.3", + "futures", + "futures-util", + "indexmap 2.10.0", + "indicatif", + "log", + "quinn", + "rayon", + "solana-account", + "solana-client-traits", + "solana-commitment-config", + "solana-connection-cache", + "solana-epoch-info", + "solana-hash", + "solana-instruction", + "solana-keypair", + "solana-measure", + "solana-message", + "solana-pubkey", + "solana-pubsub-client", + "solana-quic-client", + "solana-quic-definitions", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-rpc-client-nonce-utils", + "solana-signature", + "solana-signer", + "solana-streamer", + "solana-time-utils", + "solana-tpu-client", + "solana-transaction", + "solana-transaction-error", + "solana-transaction-status-client-types", + "solana-udp-client", + "thiserror 2.0.17", + "tokio", +] + +[[package]] +name = "solana-client-traits" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08618ed587e128105510c54ae3e456b9a06d674d8640db75afe66dad65cb4e02" +dependencies = [ + "solana-account", + "solana-commitment-config", + "solana-epoch-info", + "solana-hash", + "solana-instruction", + "solana-keypair", + "solana-message", + "solana-pubkey", + "solana-signature", + "solana-signer", + "solana-system-interface", + "solana-transaction", + "solana-transaction-error", +] + +[[package]] +name = "solana-clock" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb62e9381182459a4520b5fe7fb22d423cae736239a6427fc398a88743d0ed59" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-cluster-type" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb7692fa6bf10a1a86b450c4775526f56d7e0e2116a53313f2533b5694abea64" +dependencies = [ + "solana-hash", +] + +[[package]] +name = "solana-commitment-config" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fa5933a62dadb7d3ed35e6329de5cebb0678acc8f9cfdf413269084eeccc63f" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-compute-budget-interface" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8292c436b269ad23cecc8b24f7da3ab07ca111661e25e00ce0e1d22771951ab9" +dependencies = [ + "solana-instruction", + "solana-sdk-ids", +] + +[[package]] +name = "solana-config-interface" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e401ae56aed512821cc7a0adaa412ff97fecd2dff4602be7b1330d2daec0c4" +dependencies = [ + "bincode 1.3.3", + "serde", + "serde_derive", + "solana-account", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-short-vec", + "solana-system-interface", +] + +[[package]] +name = "solana-connection-cache" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927947ec50efb643a4e58636de66d97995c99203a09e48ab518be3a09fb81b45" +dependencies = [ + "async-trait", + "bincode 1.3.3", + "crossbeam-channel", + "futures-util", + "indexmap 2.10.0", + "log", + "rand 0.8.5", + "rayon", + "solana-keypair", + "solana-measure", + "solana-metrics", + "solana-time-utils", + "solana-transaction-error", + "thiserror 2.0.17", + "tokio", +] + +[[package]] +name = "solana-cpi" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16238feb63d1cbdf915fb287f29ef7a7ebf81469bd6214f8b72a53866b593f8f" +dependencies = [ + "solana-account-info", + "solana-define-syscall 3.0.0", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-stable-layout", +] + +[[package]] +name = "solana-curve25519" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae4261b9a8613d10e77ac831a8fa60b6fa52b9b103df46d641deff9f9812a23" +dependencies = [ + "bytemuck", + "bytemuck_derive", + "curve25519-dalek", + "solana-define-syscall 2.3.0", + "subtle", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-define-syscall" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae3e2abcf541c8122eafe9a625d4d194b4023c20adde1e251f94e056bb1aee2" + +[[package]] +name = "solana-define-syscall" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9697086a4e102d28a156b8d6b521730335d6951bd39a5e766512bbe09007cee" + +[[package]] +name = "solana-derivation-path" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff71743072690fdbdfcdc37700ae1cb77485aaad49019473a81aee099b1e0b8c" +dependencies = [ + "derivation-path", + "qstring", + "uriparse", +] + +[[package]] +name = "solana-epoch-info" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a6b69bd71386f61344f2bcf0f527f5fd6dd3b22add5880e2e1bf1dd1fa8059" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-epoch-rewards" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b319a4ed70390af911090c020571f0ff1f4ec432522d05ab89f5c08080381995" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-epoch-rewards-hasher" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e507099d0c2c5d7870c9b1848281ea67bbeee80d171ca85003ee5767994c9c38" +dependencies = [ + "siphasher 0.3.11", + "solana-hash", + "solana-pubkey", +] + +[[package]] +name = "solana-epoch-schedule" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5481e72cc4d52c169db73e4c0cd16de8bc943078aac587ec4817a75cc6388f" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-epoch-stake" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc6693d0ea833b880514b9b88d95afb80b42762dca98b0712465d1fcbbcb89e" +dependencies = [ + "solana-define-syscall 3.0.0", + "solana-pubkey", +] + +[[package]] +name = "solana-example-mocks" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978855d164845c1b0235d4b4d101cadc55373fffaf0b5b6cfa2194d25b2ed658" +dependencies = [ + "serde", + "serde_derive", + "solana-address-lookup-table-interface", + "solana-clock", + "solana-hash", + "solana-instruction", + "solana-keccak-hasher", + "solana-message", + "solana-nonce", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-feature-gate-interface" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7347ab62e6d47a82e340c865133795b394feea7c2b2771d293f57691c6544c3f" +dependencies = [ + "serde", + "serde_derive", + "solana-program-error", + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-fee-calculator" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a73cc03ca4bed871ca174558108835f8323e85917bb38b9c81c7af2ab853efe" +dependencies = [ + "log", + "serde", + "serde_derive", +] + +[[package]] +name = "solana-fee-structure" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2abdb1223eea8ec64136f39cb1ffcf257e00f915c957c35c0dd9e3f4e700b0" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-hard-forks" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abacc4b66ce471f135f48f22facf75cbbb0f8a252fbe2c1e0aa59d5b203f519" + +[[package]] +name = "solana-hash" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a063723b9e84c14d8c0d2cdf0268207dc7adecf546e31251f9e07c7b00b566c" +dependencies = [ + "borsh", + "bytemuck", + "bytemuck_derive", + "five8", + "serde", + "serde_derive", + "solana-atomic-u64", + "solana-sanitize", +] + +[[package]] +name = "solana-inflation" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e92f37a14e7c660628752833250dd3dcd8e95309876aee751d7f8769a27947c6" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-instruction" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df4e8fcba01d7efa647ed20a081c234475df5e11a93acb4393cc2c9a7b99bab" +dependencies = [ + "bincode 1.3.3", + "borsh", + "serde", + "serde_derive", + "solana-define-syscall 3.0.0", + "solana-instruction-error", + "solana-pubkey", +] + +[[package]] +name = "solana-instruction-error" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f0d483b8ae387178d9210e0575b666b05cdd4bd0f2f188128249f6e454d39d" +dependencies = [ + "num-traits", + "serde", + "serde_derive", + "solana-program-error", +] + +[[package]] +name = "solana-instructions-sysvar" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ddf67876c541aa1e21ee1acae35c95c6fbc61119814bfef70579317a5e26955" +dependencies = [ + "bitflags 2.9.1", + "solana-account-info", + "solana-instruction", + "solana-instruction-error", + "solana-program-error", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-serialize-utils", + "solana-sysvar-id", +] + +[[package]] +name = "solana-keccak-hasher" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57eebd3012946913c8c1b8b43cdf8a6249edb09c0b6be3604ae910332a3acd97" +dependencies = [ + "sha3", + "solana-define-syscall 3.0.0", + "solana-hash", +] + +[[package]] +name = "solana-keypair" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "952ed9074c12edd2060cb09c2a8c664303f4ab7f7056a407ac37dd1da7bdaa3e" +dependencies = [ + "ed25519-dalek", + "ed25519-dalek-bip32", + "five8", + "rand 0.8.5", + "solana-derivation-path", + "solana-pubkey", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-signature", + "solana-signer", +] + +[[package]] +name = "solana-last-restart-slot" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcda154ec827f5fc1e4da0af3417951b7e9b8157540f81f936c4a8b1156134d0" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-loader-v2-interface" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4a6f0ad4fd9c30679bfee2ce3ea6a449cac38049f210480b751f65676dfe82" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-loader-v3-interface" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee44c9b1328c5c712c68966fb8de07b47f3e7bac006e74ddd1bb053d3e46e5d" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-system-interface", +] + +[[package]] +name = "solana-measure" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd4adf43d4e09cd8e23aed4a1cd8e8f19fbc9eefc8994df1fac4eaf59936d8" + +[[package]] +name = "solana-message" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85666605c9fd727f865ed381665db0a8fc29f984a030ecc1e40f43bfb2541623" +dependencies = [ + "bincode 1.3.3", + "blake3", + "lazy_static", + "serde", + "serde_derive", + "solana-address", + "solana-hash", + "solana-instruction", + "solana-sanitize", + "solana-sdk-ids", + "solana-short-vec", + "solana-transaction-error", +] + +[[package]] +name = "solana-metrics" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ebb2777b1514fccedff71a8659e5108ea2017212a57962c62b07b5d32e746ff" +dependencies = [ + "crossbeam-channel", + "gethostname", + "log", + "reqwest", + "solana-cluster-type", + "solana-sha256-hasher", + "solana-time-utils", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-msg" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "264275c556ea7e22b9d3f87d56305546a38d4eee8ec884f3b126236cb7dcbbb4" +dependencies = [ + "solana-define-syscall 3.0.0", +] + +[[package]] +name = "solana-native-token" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8dd4c280dca9d046139eb5b7a5ac9ad10403fbd64964c7d7571214950d758f" + +[[package]] +name = "solana-net-utils" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faac23c0275e96a09df695158afd5421c482296ae8deec2ffd77301d8ab40be2" +dependencies = [ + "anyhow", + "bincode 1.3.3", + "bytes", + "itertools 0.12.1", + "log", + "nix 0.30.1", + "rand 0.8.5", + "serde", + "serde_derive", + "socket2 0.6.0", + "solana-serde", + "tokio", + "url", +] + +[[package]] +name = "solana-nonce" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abbdc6c8caf1c08db9f36a50967539d0f72b9f1d4aea04fec5430f532e5afadc" +dependencies = [ + "serde", + "serde_derive", + "solana-fee-calculator", + "solana-hash", + "solana-pubkey", + "solana-sha256-hasher", +] + +[[package]] +name = "solana-offchain-message" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e2a1141a673f72a05cf406b99e4b2b8a457792b7c01afa07b3f00d4e2de393" +dependencies = [ + "num_enum", + "solana-hash", + "solana-packet", + "solana-pubkey", + "solana-sanitize", + "solana-sha256-hasher", + "solana-signature", + "solana-signer", +] + +[[package]] +name = "solana-packet" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edf2f25743c95229ac0fdc32f8f5893ef738dbf332c669e9861d33ddb0f469d" +dependencies = [ + "bincode 1.3.3", + "bitflags 2.9.1", + "cfg_eval", + "serde", + "serde_derive", + "serde_with", +] + +[[package]] +name = "solana-perf" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532ef77651683be04a7431227a26cf357ee328512d5c564136c5c45aaf9c21ca" +dependencies = [ + "ahash", + "bincode 1.3.3", + "bv", + "bytes", + "caps", + "curve25519-dalek", + "dlopen2", + "fnv", + "libc", + "log", + "nix 0.30.1", + "rand 0.8.5", + "rayon", + "serde", + "solana-hash", + "solana-message", + "solana-metrics", + "solana-packet", + "solana-pubkey", + "solana-rayon-threadlimit", + "solana-sdk-ids", + "solana-short-vec", + "solana-signature", + "solana-time-utils", +] + +[[package]] +name = "solana-presigner" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f704eaf825be3180832445b9e4983b875340696e8e7239bf2d535b0f86c14a2" +dependencies = [ + "solana-pubkey", + "solana-signature", + "solana-signer", +] + +[[package]] +name = "solana-program" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91b12305dd81045d705f427acd0435a2e46444b65367d7179d7bdcfc3bc5f5eb" +dependencies = [ + "memoffset 0.9.1", + "solana-account-info", + "solana-big-mod-exp", + "solana-blake3-hasher", + "solana-borsh", + "solana-clock", + "solana-cpi", + "solana-define-syscall 3.0.0", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-epoch-stake", + "solana-example-mocks", + "solana-fee-calculator", + "solana-hash", + "solana-instruction", + "solana-instruction-error", + "solana-instructions-sysvar", + "solana-keccak-hasher", + "solana-last-restart-slot", + "solana-msg", + "solana-native-token", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-secp256k1-recover", + "solana-serde-varint", + "solana-serialize-utils", + "solana-sha256-hasher", + "solana-short-vec", + "solana-slot-hashes", + "solana-slot-history", + "solana-stable-layout", + "solana-sysvar", + "solana-sysvar-id", +] + +[[package]] +name = "solana-program-entrypoint" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6557cf5b5e91745d1667447438a1baa7823c6086e4ece67f8e6ebfa7a8f72660" +dependencies = [ + "solana-account-info", + "solana-define-syscall 3.0.0", + "solana-msg", + "solana-program-error", + "solana-pubkey", +] + +[[package]] +name = "solana-program-error" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1af32c995a7b692a915bb7414d5f8e838450cf7c70414e763d8abcae7b51f28" +dependencies = [ + "borsh", + "serde", + "serde_derive", +] + +[[package]] +name = "solana-program-memory" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10e5660c60749c7bfb30b447542529758e4dbcecd31b1e8af1fdc92e2bdde90a" +dependencies = [ + "solana-define-syscall 3.0.0", +] + +[[package]] +name = "solana-program-option" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7b4ddb464f274deb4a497712664c3b612e3f5f82471d4e47710fc4ab1c3095" + +[[package]] +name = "solana-program-pack" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c169359de21f6034a63ebf96d6b380980307df17a8d371344ff04a883ec4e9d0" +dependencies = [ + "solana-program-error", +] + +[[package]] +name = "solana-pubkey" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8909d399deb0851aa524420beeb5646b115fd253ef446e35fe4504c904da3941" +dependencies = [ + "rand 0.8.5", + "solana-address", +] + +[[package]] +name = "solana-pubsub-client" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756e556f06be18b31426872c5acaf706d401d5d550d61ec8c50b4c52fa842e22" +dependencies = [ + "crossbeam-channel", + "futures-util", + "http 0.2.12", + "log", + "semver 1.0.26", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder-client-types", + "solana-clock", + "solana-pubkey", + "solana-rpc-client-types", + "solana-signature", + "thiserror 2.0.17", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tungstenite", + "url", +] + +[[package]] +name = "solana-quic-client" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d10beec99ad8b6fc68cb7d35da772dd2f6f0da6b16a5da8fb8eb6409d495423" +dependencies = [ + "async-lock", + "async-trait", + "futures", + "itertools 0.12.1", + "log", + "quinn", + "quinn-proto", + "rustls 0.23.32", + "solana-connection-cache", + "solana-keypair", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-pubkey", + "solana-quic-definitions", + "solana-rpc-client-api", + "solana-signer", + "solana-streamer", + "solana-tls-utils", + "solana-transaction-error", + "thiserror 2.0.17", + "tokio", +] + +[[package]] +name = "solana-quic-definitions" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15319accf7d3afd845817aeffa6edd8cc185f135cefbc6b985df29cfd8c09609" +dependencies = [ + "solana-keypair", +] + +[[package]] +name = "solana-rayon-threadlimit" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc3dcac63a9fae71e7dc760f0252fae7f08860750933f86112fff8bfcc7cf50" +dependencies = [ + "log", + "num_cpus", +] + +[[package]] +name = "solana-rent" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b702d8c43711e3c8a9284a4f1bbc6a3de2553deb25b0c8142f9a44ef0ce5ddc1" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-sysvar-id", +] + +[[package]] +name = "solana-reward-info" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82be7946105c2ee6be9f9ee7bd18a068b558389221d29efa92b906476102bfcc" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "solana-rpc-client" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be62cbd74da0fc15c9adbe6d58806358d3345bbebc749c42fe774715fd007a6" +dependencies = [ + "async-trait", + "base64 0.22.1", + "bincode 1.3.3", + "bs58", + "futures", + "indicatif", + "log", + "reqwest", + "reqwest-middleware", + "semver 1.0.26", + "serde", + "serde_derive", + "serde_json", + "solana-account", + "solana-account-decoder-client-types", + "solana-clock", + "solana-commitment-config", + "solana-epoch-info", + "solana-epoch-schedule", + "solana-feature-gate-interface", + "solana-hash", + "solana-instruction", + "solana-message", + "solana-pubkey", + "solana-rpc-client-api", + "solana-signature", + "solana-transaction", + "solana-transaction-error", + "solana-transaction-status-client-types", + "solana-version", + "solana-vote-interface", + "tokio", +] + +[[package]] +name = "solana-rpc-client-api" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ebdc858d814771451ee9dc6a5cf5ed5ea589ac7e1a5a61c4450119307632320" +dependencies = [ + "anyhow", + "jsonrpc-core", + "reqwest", + "reqwest-middleware", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder-client-types", + "solana-clock", + "solana-rpc-client-types", + "solana-signer", + "solana-transaction-error", + "solana-transaction-status-client-types", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-rpc-client-nonce-utils" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0f3ca3eeb92254ef4640271cfd4af45b5eecc8851e3ad6c1b3c054cc06a741" +dependencies = [ + "solana-account", + "solana-commitment-config", + "solana-hash", + "solana-message", + "solana-nonce", + "solana-pubkey", + "solana-rpc-client", + "solana-sdk-ids", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-rpc-client-types" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82661b56f57858a5ad8b7143dd9eb5308503f0e924a61bed02843cd234ff84d" +dependencies = [ + "base64 0.22.1", + "bs58", + "semver 1.0.26", + "serde", + "serde_derive", + "serde_json", + "solana-account", + "solana-account-decoder-client-types", + "solana-clock", + "solana-commitment-config", + "solana-fee-calculator", + "solana-inflation", + "solana-pubkey", + "solana-transaction-error", + "solana-transaction-status-client-types", + "solana-version", + "spl-generic-token", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-sanitize" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf09694a0fc14e5ffb18f9b7b7c0f15ecb6eac5b5610bf76a1853459d19daf9" + +[[package]] +name = "solana-sbpf" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f224d906c14efc7ed7f42bc5fe9588f3f09db8cabe7f6023adda62a69678e1a" +dependencies = [ + "byteorder", + "combine 3.8.1", + "hash32", + "log", + "rustc-demangle", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-sdk" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f03df7969f5e723ad31b6c9eadccc209037ac4caa34d8dc259316b05c11e82b" +dependencies = [ + "bincode 1.3.3", + "bs58", + "serde", + "solana-account", + "solana-epoch-info", + "solana-epoch-rewards-hasher", + "solana-fee-structure", + "solana-inflation", + "solana-keypair", + "solana-message", + "solana-offchain-message", + "solana-presigner", + "solana-program", + "solana-program-memory", + "solana-pubkey", + "solana-sanitize", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-serde", + "solana-serde-varint", + "solana-short-vec", + "solana-shred-version", + "solana-signature", + "solana-signer", + "solana-time-utils", + "solana-transaction", + "solana-transaction-error", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-sdk-ids" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1b6d6aaf60669c592838d382266b173881c65fb1cdec83b37cb8ce7cb89f9ad" +dependencies = [ + "solana-pubkey", +] + +[[package]] +name = "solana-sdk-macro" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6430000e97083460b71d9fbadc52a2ab2f88f53b3a4c5e58c5ae3640a0e8c00" +dependencies = [ + "bs58", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "solana-secp256k1-recover" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "394a4470477d66296af5217970a905b1c5569032a7732c367fb69e5666c8607e" +dependencies = [ + "k256", + "solana-define-syscall 3.0.0", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-seed-derivable" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff7bdb72758e3bec33ed0e2658a920f1f35dfb9ed576b951d20d63cb61ecd95c" +dependencies = [ + "solana-derivation-path", +] + +[[package]] +name = "solana-seed-phrase" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc905b200a95f2ea9146e43f2a7181e3aeb55de6bc12afb36462d00a3c7310de" +dependencies = [ + "hmac", + "pbkdf2", + "sha2", +] + +[[package]] +name = "solana-serde" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709a93cab694c70f40b279d497639788fc2ccbcf9b4aa32273d4b361322c02dd" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-serde-varint" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5174c57d5ff3c1995f274d17156964664566e2cde18a07bba1586d35a70d3b" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-serialize-utils" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e41dd8feea239516c623a02f0a81c2367f4b604d7965237fed0751aeec33ed" +dependencies = [ + "solana-instruction-error", + "solana-pubkey", + "solana-sanitize", +] + +[[package]] +name = "solana-sha256-hasher" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b912ba6f71cb202c0c3773ec77bf898fa9fe0c78691a2d6859b3b5b8954719" +dependencies = [ + "sha2", + "solana-define-syscall 3.0.0", + "solana-hash", +] + +[[package]] +name = "solana-short-vec" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69d029da5428fc1c57f7d49101b2077c61f049d4112cd5fb8456567cc7d2638" +dependencies = [ + "serde", +] + +[[package]] +name = "solana-shred-version" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94953e22ca28fe4541a3447d6baeaf519cc4ddc063253bfa673b721f34c136bb" +dependencies = [ + "solana-hard-forks", + "solana-hash", + "solana-sha256-hasher", +] + +[[package]] +name = "solana-signature" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb8057cc0e9f7b5e89883d49de6f407df655bb6f3a71d0b7baf9986a2218fd9" +dependencies = [ + "ed25519-dalek", + "five8", + "rand 0.8.5", + "serde", + "serde-big-array", + "serde_derive", + "solana-sanitize", +] + +[[package]] +name = "solana-signer" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bfea97951fee8bae0d6038f39a5efcb6230ecdfe33425ac75196d1a1e3e3235" +dependencies = [ + "solana-pubkey", + "solana-signature", + "solana-transaction-error", +] + +[[package]] +name = "solana-slot-hashes" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80a293f952293281443c04f4d96afd9d547721923d596e92b4377ed2360f1746" +dependencies = [ + "serde", + "serde_derive", + "solana-hash", + "solana-sdk-ids", + "solana-sysvar-id", +] + +[[package]] +name = "solana-slot-history" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f914f6b108f5bba14a280b458d023e3621c9973f27f015a4d755b50e88d89e97" +dependencies = [ + "bv", + "serde", + "serde_derive", + "solana-sdk-ids", + "solana-sysvar-id", +] + +[[package]] +name = "solana-stable-layout" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1da74507795b6e8fb60b7c7306c0c36e2c315805d16eaaf479452661234685ac" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] + +[[package]] +name = "solana-stake-interface" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f912ae679b683365348dea482dbd9468d22ff258b554fd36e3d3683c2122e3" +dependencies = [ + "num-traits", + "serde", + "serde_derive", + "solana-clock", + "solana-cpi", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "solana-system-interface", + "solana-sysvar", + "solana-sysvar-id", +] + +[[package]] +name = "solana-streamer" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e49b16c113860eda6f3fc68a5b0df11d448de8585870d9bc3b431903a27d52f2" +dependencies = [ + "arc-swap", + "async-channel", + "bytes", + "crossbeam-channel", + "dashmap 5.5.3", + "futures", + "futures-util", + "governor", + "histogram", + "indexmap 2.10.0", + "itertools 0.12.1", + "libc", + "log", + "nix 0.30.1", + "num_cpus", + "pem 1.1.1", + "percentage", + "quinn", + "quinn-proto", + "rand 0.8.5", + "rustls 0.23.32", + "smallvec", + "socket2 0.6.0", + "solana-keypair", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-packet", + "solana-perf", + "solana-pubkey", + "solana-quic-definitions", + "solana-signature", + "solana-signer", + "solana-time-utils", + "solana-tls-utils", + "solana-transaction-error", + "solana-transaction-metrics-tracker", + "thiserror 2.0.17", + "tokio", + "tokio-util", + "x509-parser", +] + +[[package]] +name = "solana-svm-feature-set" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45cb106d92777438df2af16956ff269b2853337cc08517b4aa08784c061da30" + +[[package]] +name = "solana-system-interface" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e1790547bfc3061f1ee68ea9d8dc6c973c02a163697b24263a8e9f2e6d4afa2" +dependencies = [ + "num-traits", + "serde", + "serde_derive", + "solana-instruction", + "solana-msg", + "solana-program-error", + "solana-pubkey", +] + +[[package]] +name = "solana-sysvar" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63205e68d680bcc315337dec311b616ab32fea0a612db3b883ce4de02e0953f9" +dependencies = [ + "base64 0.22.1", + "bincode 1.3.3", + "bytemuck", + "bytemuck_derive", + "lazy_static", + "serde", + "serde_derive", + "solana-account-info", + "solana-clock", + "solana-define-syscall 3.0.0", + "solana-epoch-rewards", + "solana-epoch-schedule", + "solana-fee-calculator", + "solana-hash", + "solana-instruction", + "solana-last-restart-slot", + "solana-program-entrypoint", + "solana-program-error", + "solana-program-memory", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-sdk-macro", + "solana-slot-hashes", + "solana-slot-history", + "solana-sysvar-id", +] + +[[package]] +name = "solana-sysvar-id" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5051bc1a16d5d96a96bc33b5b2ec707495c48fe978097bdaba68d3c47987eb32" +dependencies = [ + "solana-pubkey", + "solana-sdk-ids", +] + +[[package]] +name = "solana-time-utils" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced92c60aa76ec4780a9d93f3bd64dfa916e1b998eacc6f1c110f3f444f02c9" + +[[package]] +name = "solana-tls-utils" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e192d53f86899bbdf5cce9d406d7db1a17fdfc04168f086eea89616436db4e40" +dependencies = [ + "rustls 0.23.32", + "solana-keypair", + "solana-pubkey", + "solana-signer", + "x509-parser", +] + +[[package]] +name = "solana-tpu-client" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514ca37b1d97036dace0d5798d5b8b21e908917c10065b5f7d8010584019c704" +dependencies = [ + "async-trait", + "bincode 1.3.3", + "futures-util", + "indexmap 2.10.0", + "indicatif", + "log", + "rayon", + "solana-client-traits", + "solana-clock", + "solana-commitment-config", + "solana-connection-cache", + "solana-epoch-schedule", + "solana-measure", + "solana-message", + "solana-net-utils", + "solana-pubkey", + "solana-pubsub-client", + "solana-quic-definitions", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-signature", + "solana-signer", + "solana-transaction", + "solana-transaction-error", + "thiserror 2.0.17", + "tokio", +] + +[[package]] +name = "solana-transaction" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64928e6af3058dcddd6da6680cbe08324b4e071ad73115738235bbaa9e9f72a5" +dependencies = [ + "bincode 1.3.3", + "serde", + "serde_derive", + "solana-address", + "solana-hash", + "solana-instruction", + "solana-instruction-error", + "solana-message", + "solana-sanitize", + "solana-sdk-ids", + "solana-short-vec", + "solana-signature", + "solana-signer", + "solana-transaction-error", +] + +[[package]] +name = "solana-transaction-context" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917fe382dafb8f73cf35a39638a342b6b3321bb55157f1ea517d709628b17567" +dependencies = [ + "bincode 1.3.3", + "serde", + "serde_derive", + "solana-account", + "solana-instruction", + "solana-instructions-sysvar", + "solana-pubkey", + "solana-rent", + "solana-sbpf", + "solana-sdk-ids", +] + +[[package]] +name = "solana-transaction-error" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4222065402340d7e6aec9dc3e54d22992ddcf923d91edcd815443c2bfca3144a" +dependencies = [ + "serde", + "serde_derive", + "solana-instruction-error", + "solana-sanitize", +] + +[[package]] +name = "solana-transaction-metrics-tracker" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54f26dce6ccfba544fc462a1d98368a24c00724538644593534c1b01ae49241b" +dependencies = [ + "base64 0.22.1", + "bincode 1.3.3", + "log", + "rand 0.8.5", + "solana-packet", + "solana-perf", + "solana-short-vec", + "solana-signature", +] + +[[package]] +name = "solana-transaction-status" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510007171942cc8c42d3772860bdacda446cf2799f3032af14caf8b2cb9ec9b5" +dependencies = [ + "Inflector", + "agave-reserved-account-keys", + "base64 0.22.1", + "bincode 1.3.3", + "borsh", + "bs58", + "log", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-address-lookup-table-interface", + "solana-clock", + "solana-hash", + "solana-instruction", + "solana-loader-v2-interface", + "solana-loader-v3-interface", + "solana-message", + "solana-program-option", + "solana-pubkey", + "solana-reward-info", + "solana-sdk-ids", + "solana-signature", + "solana-stake-interface", + "solana-system-interface", + "solana-transaction", + "solana-transaction-error", + "solana-transaction-status-client-types", + "solana-vote-interface", + "spl-associated-token-account-interface", + "spl-memo-interface", + "spl-token-2022-interface", + "spl-token-group-interface", + "spl-token-interface", + "spl-token-metadata-interface", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-transaction-status-client-types" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f429cdb4bc6b9373ff399f7cbc9e0d6312411eaa718654cfd6ae9a3763459483" +dependencies = [ + "base64 0.22.1", + "bincode 1.3.3", + "bs58", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder-client-types", + "solana-commitment-config", + "solana-instruction", + "solana-message", + "solana-pubkey", + "solana-reward-info", + "solana-signature", + "solana-transaction", + "solana-transaction-context", + "solana-transaction-error", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-udp-client" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20dc356737f932556041b42cfc0e1700587593b42012d10bc35406def8ed2546" +dependencies = [ + "async-trait", + "solana-connection-cache", + "solana-keypair", + "solana-net-utils", + "solana-streamer", + "solana-transaction-error", + "thiserror 2.0.17", + "tokio", +] + +[[package]] +name = "solana-version" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e657494d07daa44808f358034aa47df2e541fc0489de22dc2747764cf3341a5" +dependencies = [ + "agave-feature-set", + "rand 0.8.5", + "semver 1.0.26", + "serde", + "serde_derive", + "solana-sanitize", + "solana-serde-varint", +] + +[[package]] +name = "solana-vote-interface" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66631ddbe889dab5ec663294648cd1df395ec9df7a4476e7b3e095604cfdb539" +dependencies = [ + "bincode 1.3.3", + "cfg_eval", + "num-derive", + "num-traits", + "serde", + "serde_derive", + "serde_with", + "solana-clock", + "solana-hash", + "solana-instruction", + "solana-instruction-error", + "solana-pubkey", + "solana-rent", + "solana-sdk-ids", + "solana-serde-varint", + "solana-serialize-utils", + "solana-short-vec", + "solana-system-interface", +] + +[[package]] +name = "solana-zk-sdk" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9602bcb1f7af15caef92b91132ec2347e1c51a72ecdbefdaefa3eac4b8711475" +dependencies = [ + "aes-gcm-siv", + "base64 0.22.1", + "bincode 1.3.3", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek", + "getrandom 0.2.16", + "itertools 0.12.1", + "js-sys", + "merlin", + "num-derive", + "num-traits", + "rand 0.8.5", + "serde", + "serde_derive", + "serde_json", + "sha3", + "solana-derivation-path", + "solana-instruction", + "solana-pubkey", + "solana-sdk-ids", + "solana-seed-derivable", + "solana-seed-phrase", + "solana-signature", + "solana-signer", + "subtle", + "thiserror 2.0.17", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "spl-associated-token-account-interface" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6433917b60441d68d99a17e121d9db0ea15a9a69c0e5afa34649cf5ba12612f" +dependencies = [ + "borsh", + "solana-instruction", + "solana-pubkey", +] + +[[package]] +name = "spl-discriminator" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48cc11459e265d5b501534144266620289720b4c44522a47bc6b63cd295d2f3" +dependencies = [ + "bytemuck", + "solana-program-error", + "solana-sha256-hasher", + "spl-discriminator-derive", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" dependencies = [ - "digest 0.10.7", - "keccak", + "quote", + "spl-discriminator-syn", + "syn 2.0.104", ] [[package]] -name = "sha3-asm" -version = "0.1.4" +name = "spl-discriminator-syn" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +checksum = "5d1dbc82ab91422345b6df40a79e2b78c7bce1ebb366da323572dd60b7076b67" dependencies = [ - "cc", - "cfg-if", + "proc-macro2", + "quote", + "sha2", + "syn 2.0.104", + "thiserror 1.0.69", ] [[package]] -name = "sharded-slab" -version = "0.1.7" +name = "spl-generic-token" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +checksum = "233df81b75ab99b42f002b5cdd6e65a7505ffa930624f7096a7580a56765e9cf" dependencies = [ - "lazy_static", + "bytemuck", + "solana-pubkey", ] [[package]] -name = "shlex" -version = "1.3.0" +name = "spl-memo-interface" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "3d4e2aedd58f858337fa609af5ad7100d4a243fdaf6a40d6eb4c28c5f19505d3" +dependencies = [ + "solana-instruction", + "solana-pubkey", +] [[package]] -name = "signal-hook-registry" -version = "1.4.5" +name = "spl-pod" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b1233fdecd7461611d69bb87bc2e95af742df47291975d21232a0be8217da9de" dependencies = [ - "libc", + "borsh", + "bytemuck", + "bytemuck_derive", + "num-derive", + "num-traits", + "num_enum", + "solana-program-error", + "solana-program-option", + "solana-pubkey", + "solana-zk-sdk", + "thiserror 2.0.17", ] [[package]] -name = "signature" -version = "2.2.0" +name = "spl-token-2022-interface" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +checksum = "0888304af6b3d839e435712e6c84025e09513017425ff62045b6b8c41feb77d9" dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-account-info", + "solana-instruction", + "solana-program-error", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-sdk-ids", + "solana-zk-sdk", + "spl-pod", + "spl-token-confidential-transfer-proof-extraction", + "spl-token-confidential-transfer-proof-generation", + "spl-token-group-interface", + "spl-token-metadata-interface", + "spl-type-length-value", + "thiserror 2.0.17", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-extraction" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a22217af69b7a61ca813f47c018afb0b00b02a74a4c70ff099cd4287740bc3d" +dependencies = [ + "bytemuck", + "solana-account-info", + "solana-curve25519", + "solana-instruction", + "solana-instructions-sysvar", + "solana-msg", + "solana-program-error", + "solana-pubkey", + "solana-sdk-ids", + "solana-zk-sdk", + "spl-pod", + "thiserror 2.0.17", ] [[package]] -name = "simple_asn1" -version = "0.6.3" +name = "spl-token-confidential-transfer-proof-generation" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +checksum = "f63a2b41095945dc15274b924b21ccae9b3ec9dc2fdd43dbc08de8c33bbcd915" dependencies = [ - "num-bigint", - "num-traits", - "thiserror 2.0.12", - "time", + "curve25519-dalek", + "solana-zk-sdk", + "thiserror 2.0.17", ] [[package]] -name = "slab" -version = "0.4.10" +name = "spl-token-group-interface" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "452d0f758af20caaa10d9a6f7608232e000d4c74462f248540b3d2ddfa419776" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "thiserror 2.0.17", +] [[package]] -name = "smallvec" -version = "1.15.1" +name = "spl-token-interface" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8c564ac05a7c8d8b12e988a37d82695b5ba4db376d07ea98bc4882c81f96c7f3" dependencies = [ - "serde", + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-instruction", + "solana-program-error", + "solana-program-option", + "solana-program-pack", + "solana-pubkey", + "solana-sdk-ids", + "thiserror 2.0.17", ] [[package]] -name = "socket2" -version = "0.5.10" +name = "spl-token-metadata-interface" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "9c467c7c3bd056f8fe60119e7ec34ddd6f23052c2fa8f1f51999098063b72676" dependencies = [ - "libc", - "windows-sys 0.52.0", + "borsh", + "num-derive", + "num-traits", + "solana-borsh", + "solana-instruction", + "solana-program-error", + "solana-pubkey", + "spl-discriminator", + "spl-pod", + "spl-type-length-value", + "thiserror 2.0.17", ] [[package]] -name = "spki" -version = "0.7.3" +name = "spl-type-length-value" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +checksum = "ca20a1a19f4507a98ca4b28ff5ed54cac9b9d34ed27863e2bde50a3238f9a6ac" dependencies = [ - "base64ct", - "der", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-account-info", + "solana-msg", + "solana-program-error", + "spl-discriminator", + "spl-pod", + "thiserror 2.0.17", ] [[package]] @@ -5556,6 +8480,18 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -5609,7 +8545,7 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", + "rustix", "windows-sys 0.59.0", ] @@ -5624,7 +8560,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "url", "utoipa", @@ -5654,7 +8590,7 @@ dependencies = [ "serde_json", "serde_with", "thirdweb-core", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tower-http", "tracing", @@ -5678,11 +8614,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -5698,9 +8634,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -5802,9 +8738,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.46.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1140bb80481756a8cbe10541f37433b459c5aa1e727b4c020fbfebdc25bf3ec4" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", @@ -5815,9 +8751,9 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "slab", - "socket2", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5857,7 +8793,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.28", + "rustls 0.23.32", "tokio", ] @@ -5873,11 +8809,26 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio-tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +dependencies = [ + "futures-util", + "log", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", + "tungstenite", + "webpki-roots 0.25.4", +] + [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -5941,7 +8892,7 @@ dependencies = [ "pin-project 1.1.10", "prost", "rustls-native-certs 0.8.1", - "socket2", + "socket2 0.5.10", "tokio", "tokio-rustls 0.26.2", "tokio-stream", @@ -6094,6 +9045,27 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 0.2.12", + "httparse", + "log", + "rand 0.8.5", + "rustls 0.21.12", + "sha1", + "thiserror 1.0.69", + "url", + "utf-8", + "webpki-roots 0.24.0", +] + [[package]] name = "twmq" version = "0.1.0" @@ -6105,7 +9077,7 @@ dependencies = [ "redis", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "tracing-subscriber", @@ -6160,12 +9132,24 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unit-prefix" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" + [[package]] name = "universal-hash" version = "0.5.1" @@ -6176,6 +9160,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -6188,6 +9181,16 @@ version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" +[[package]] +name = "uriparse" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff" +dependencies = [ + "fnv", + "lazy_static", +] + [[package]] name = "url" version = "2.5.4" @@ -6205,6 +9208,12 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -6282,7 +9291,7 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vault-sdk" version = "0.1.0" -source = "git+ssh://git@github.com/thirdweb-dev/vault.git?branch=pb%2Fupdate-alloy#b0a72f93335ff05f722c070f32f0697c5478243a" +source = "git+ssh://git@github.com/thirdweb-dev/vault.git?branch=main#39338ab7c5a8694164ba17a5110ac43497cf69f9" dependencies = [ "alloy", "chacha20poly1305", @@ -6294,7 +9303,8 @@ dependencies = [ "serde", "serde_json", "sha2", - "thiserror 2.0.12", + "solana-sdk", + "thiserror 2.0.17", "uuid", "vault-types", "x25519-dalek", @@ -6303,13 +9313,16 @@ dependencies = [ [[package]] name = "vault-types" version = "0.1.0" -source = "git+ssh://git@github.com/thirdweb-dev/vault.git?branch=pb%2Fupdate-alloy#b0a72f93335ff05f722c070f32f0697c5478243a" +source = "git+ssh://git@github.com/thirdweb-dev/vault.git?branch=main#39338ab7c5a8694164ba17a5110ac43497cf69f9" dependencies = [ "alloy", - "bincode", + "base64 0.22.1", + "bincode 2.0.1", "chrono", "serde", "serde_json", + "serde_with", + "solana-sdk", "uuid", ] @@ -6331,6 +9344,12 @@ version = "0.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "vsimd" version = "0.8.0" @@ -6498,6 +9517,39 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.3", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d651ec480de84b762e7be71e6efa7461699c19d9e2c272c8d93455f567786e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" +dependencies = [ + "rustls-webpki 0.101.7", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "webpki-roots" version = "1.0.1" @@ -6508,15 +9560,13 @@ dependencies = [ ] [[package]] -name = "which" -version = "4.4.2" +name = "wide" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.44", + "bytemuck", + "safe_arch", ] [[package]] @@ -6559,7 +9609,7 @@ dependencies = [ "windows-collections", "windows-core", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] @@ -6580,7 +9630,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -6592,7 +9642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", "windows-threading", ] @@ -6624,6 +9674,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-numerics" version = "0.2.0" @@ -6631,7 +9687,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6640,7 +9696,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -6651,7 +9707,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -6660,7 +9716,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -6690,6 +9755,30 @@ dependencies = [ "windows-targets 0.53.2", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -6728,9 +9817,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -6743,6 +9838,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -6755,6 +9856,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -6779,6 +9886,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -6791,6 +9904,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -6803,6 +9922,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -6815,6 +9940,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -6872,6 +10003,24 @@ dependencies = [ "zeroize", ] +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs", + "base64 0.13.1", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + [[package]] name = "xmlparser" version = "0.13.6" @@ -6910,7 +10059,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.104", - "synstructure", + "synstructure 0.13.2", ] [[package]] @@ -6951,7 +10100,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.104", - "synstructure", + "synstructure 0.13.2", ] [[package]] @@ -7006,3 +10155,31 @@ dependencies = [ "quote", "syn 2.0.104", ] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index c2593ef..cffad68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,12 +6,95 @@ members = [ "eip7702-core", "executors", "server", + "solana-core", "thirdweb-core", "twmq", ] resolver = "2" [workspace.dependencies] -alloy = { version = "1.0.23" } -vault-types = { version = "0.1.0", git = "ssh://git@github.com/thirdweb-dev/vault.git", branch = "pb/update-alloy" } -vault-sdk = { version = "0.1.0", git = "ssh://git@github.com/thirdweb-dev/vault.git", branch = "pb/update-alloy" } +# Alloy and related +alloy = { version = "1.0.36" } +alloy-signer-aws = { version = "1.0.23" } + +# Vault +vault-types = { version = "0.1.0", git = "ssh://git@github.com/thirdweb-dev/vault.git", branch = "main" } +vault-sdk = { version = "0.1.0", git = "ssh://git@github.com/thirdweb-dev/vault.git", branch = "main" } + +# Solana +solana-sdk = "3.0" +solana-client = "3.0" +solana-transaction-status = "3.0" +solana-connection-cache = "3.0" +solana-commitment-config = "3.0" +solana-compute-budget-interface = "3.0" +spl-memo-interface = "2.0" + +# AWS +aws-config = "1.8.2" +aws-sdk-kms = "1.79.0" +aws-credential-types = "1.2.4" + +# Serialization +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" +serde_with = "3.14.0" +serde-bool = "0.1.3" +serde_repr = "0.1.20" + +# Error handling +thiserror = "2.0.12" +anyhow = "1.0.98" + +# Logging and tracing +tracing = "0.1.41" +tracing-subscriber = { version = "0.3.19" } + +# Async runtime +tokio = { version = "1.45.0" } +futures = "0.3.31" + +# API documentation +utoipa = { version = "5.4.0" } +utoipa-axum = "0.2.0" +utoipa-scalar = { version = "0.3.0" } +schemars = "0.8.22" +aide = { version = "0.14.2" } + +# Web framework +axum = { version = "0.8.4" } +tower = "0.5.2" +tower-http = { version = "0.6.2" } + +# Caching +moka = { version = "0.12.10", features = ["future"] } + +# Utilities +uuid = { version = "1.17.0", features = ["v4"] } +rand = "0.9.1" +chrono = "0.4.41" +base64 = "0.22.1" +hex = "0.4.3" +url = "2.5.4" +nanoid = "0.4.0" + +# HTTP client +reqwest = "0.12.18" + +# Cryptography +hmac = "0.12.1" +sha2 = "0.10.9" + +# Metrics +prometheus = "0.13.4" +lazy_static = "1.5.0" + +# Configuration +config = "0.15.11" +aws-arn = "0.3.1" + +# Redis +redis = { version = "0.31.0", features = ["tokio-comp", "connection-manager"] } + +# Dev dependencies +criterion = { version = "0.6", features = ["html_reports", "async_tokio"] } \ No newline at end of file diff --git a/aa-core/Cargo.toml b/aa-core/Cargo.toml index ae93cb9..9324148 100644 --- a/aa-core/Cargo.toml +++ b/aa-core/Cargo.toml @@ -5,10 +5,10 @@ edition = "2024" [dependencies] alloy = { workspace = true, features = ["serde"] } -tokio = "1.44.2" +tokio = { workspace = true } engine-aa-types = { path = "../aa-types" } engine-core = { path = "../core" } vault-types = { workspace = true } vault-sdk = { workspace = true } -serde = "1.0.219" -tracing = "0.1.41" +serde = { workspace = true } +tracing = { workspace = true } diff --git a/aa-types/Cargo.toml b/aa-types/Cargo.toml index cc33f28..15dce77 100644 --- a/aa-types/Cargo.toml +++ b/aa-types/Cargo.toml @@ -4,9 +4,9 @@ version = "0.1.0" edition = "2024" [dependencies] -alloy = { version = "1.0.8", features = ["serde"] } -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.140" -thiserror = "2.0.12" -schemars = "0.8.22" -utoipa = "5.4.0" \ No newline at end of file +alloy = { workspace = true, features = ["serde"] } +serde = { workspace = true } +serde_json = { workspace = true } +thiserror = { workspace = true } +schemars = { workspace = true } +utoipa = { workspace = true } \ No newline at end of file diff --git a/core/Cargo.toml b/core/Cargo.toml index 0cd1787..5ac00c5 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -4,23 +4,27 @@ version = "0.1.0" edition = "2024" [dependencies] -alloy = { version = "1.0.8", features = ["serde", "json-rpc"] } +alloy = { workspace = true, features = ["serde", "json-rpc"] } engine-aa-types = { path = "../aa-types" } -schemars = "0.8.22" -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.140" -thiserror = "2.0.12" +schemars = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +thiserror = { workspace = true } vault-types = { workspace = true } vault-sdk = { workspace = true } -tower = "0.5.2" -tracing = "0.1.41" +tower = { workspace = true } +tracing = { workspace = true } twmq = { version = "0.1.0", path = "../twmq" } thirdweb-core = { version = "0.1.0", path = "../thirdweb-core" } -uuid = { version = "1.17.0", features = ["v4"] } -utoipa = { version = "5.4.0", features = ["preserve_order"] } -serde_with = "3.13.0" -alloy-signer-aws = { version = "1.0.23", features = ["eip712"] } -aws-config = "1.8.2" -aws-sdk-kms = "1.79.0" -aws-credential-types = "1.2.4" -moka = { version = "0.12", features = ["future"] } +uuid = { workspace = true } +utoipa = { workspace = true, features = ["preserve_order"] } +serde_with = { workspace = true } +alloy-signer-aws = { workspace = true, features = ["eip712"] } +aws-config = { workspace = true } +aws-sdk-kms = { workspace = true } +aws-credential-types = { workspace = true } +moka = { workspace = true } +solana-sdk = { workspace = true } +solana-commitment-config = { workspace = true } +solana-client = { workspace = true } +engine-solana-core = { version = "0.1.0", path = "../solana-core" } diff --git a/core/src/error.rs b/core/src/error.rs index 99e2255..1c64020 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -18,6 +18,95 @@ use twmq::error::TwmqError; use crate::chain::Chain; +/// Serializable version of Solana's RpcResponseErrorData +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, utoipa::ToSchema)] +#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum SolanaRpcResponseErrorData { + /// Empty error data + Empty, + /// Transaction simulation/preflight failed + SendTransactionPreflightFailure { + /// Error message from simulation + #[serde(skip_serializing_if = "Option::is_none")] + err: Option, + /// Logs from simulation + #[serde(skip_serializing_if = "Option::is_none")] + logs: Option>, + /// Accounts used in simulation + #[serde(skip_serializing_if = "Option::is_none")] + accounts: Option, + /// Units consumed during simulation + #[serde(skip_serializing_if = "Option::is_none")] + units_consumed: Option, + /// Return data from the transaction + #[serde(skip_serializing_if = "Option::is_none")] + return_data: Option, + }, + /// Node is unhealthy + NodeUnhealthy { + #[serde(skip_serializing_if = "Option::is_none")] + num_slots_behind: Option, + }, +} + +/// Solana-specific RPC error types +#[derive(Debug, Error, Clone, Serialize, Deserialize, JsonSchema, utoipa::ToSchema)] +#[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")] +pub enum SolanaRpcErrorKind { + /// IO error (network issues, connection problems) + #[error("IO error: {message}")] + Io { + message: String, + #[serde(skip_serializing_if = "Option::is_none")] + kind: Option, + }, + + /// Reqwest HTTP error + #[error("HTTP error: {message}")] + Reqwest { + message: String, + #[serde(skip)] + url: Option, + #[serde(skip_serializing_if = "Option::is_none")] + status: Option, + }, + + /// RPC protocol error (JSON-RPC error response) + #[error("RPC protocol error (code {code}): {message}")] + RpcError { + code: i64, + message: String, + /// Structured error data from Solana RPC + data: SolanaRpcResponseErrorData, + }, + + /// JSON serialization/deserialization error + #[error("JSON error: {message}")] + SerdeJson { + message: String, + #[serde(skip_serializing_if = "Option::is_none")] + line: Option, + #[serde(skip_serializing_if = "Option::is_none")] + column: Option, + }, + + /// Transaction error from Solana (e.g., insufficient funds, invalid signature) + #[error("Transaction error: {error_type}")] + TransactionError { error_type: String, message: String }, + + /// Signing error + #[error("Signing error: {message}")] + SigningError { message: String }, + + /// Custom error from middleware or validators + #[error("Custom error: {message}")] + Custom { message: String }, + + /// Unknown/other error + #[error("Unknown error: {message}")] + Unknown { message: String }, +} + #[derive(Debug, Error, Clone, Serialize, Deserialize, JsonSchema, utoipa::ToSchema)] #[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")] pub enum RpcErrorKind { @@ -206,6 +295,17 @@ pub enum EngineError { kind: RpcErrorKind, }, + #[schema(title = "Solana RPC Error")] + #[error("Solana RPC error on {chain_id}: {message}")] + SolanaRpcError { + /// Solana chain identifier + chain_id: String, + /// Human-readable error message + message: String, + /// Structured error kind + kind: SolanaRpcErrorKind, + }, + #[schema(title = "Engine Vault KMS Error")] #[error("Error interaction with vault: {message}")] #[serde(rename_all = "camelCase")] @@ -325,6 +425,9 @@ pub enum SerialisableAwsSignerError { #[error("Unknown error: {message}")] Unknown { message: String }, + + #[error("Signature recovery failed")] + SignatureRecoveryFailed, } impl From> for SerialisableAwsSdkError { @@ -386,6 +489,9 @@ impl From for EngineError { AwsSignerError::PublicKeyNotFound => EngineError::AwsKmsSignerError { error: SerialisableAwsSignerError::PublicKeyNotFound, }, + AwsSignerError::SignatureRecoveryFailed => EngineError::AwsKmsSignerError { + error: SerialisableAwsSignerError::SignatureRecoveryFailed, + }, } } } @@ -459,6 +565,120 @@ pub trait AlloyRpcErrorToEngineError { fn to_engine_paymaster_error(&self, chain: &impl Chain) -> EngineError; } +/// Trait for converting Solana RPC errors to EngineError +pub trait SolanaRpcErrorToEngineError { + fn to_engine_solana_error(&self, chain_id: &str) -> EngineError; +} + +// Implementation for Solana client errors +impl SolanaRpcErrorToEngineError for solana_client::client_error::ClientError { + fn to_engine_solana_error(&self, chain_id: &str) -> EngineError { + use solana_client::client_error::ClientErrorKind; + + let kind = match self.kind() { + ClientErrorKind::Io(err) => SolanaRpcErrorKind::Io { + message: err.to_string(), + kind: Some(format!("{:?}", err.kind())), + }, + ClientErrorKind::Reqwest(err) => { + let status = err.status().map(|s| s.as_u16()); + let url = err.url().map(|u| u.to_string()); + SolanaRpcErrorKind::Reqwest { + message: err.to_string(), + url, + status, + } + } + ClientErrorKind::RpcError(rpc_err) => { + use solana_client::rpc_request::{RpcError, RpcResponseErrorData}; + match rpc_err { + RpcError::RpcResponseError { + code, + message, + data, + } => { + let structured_data = match data { + RpcResponseErrorData::Empty => SolanaRpcResponseErrorData::Empty, + RpcResponseErrorData::SendTransactionPreflightFailure(result) => { + SolanaRpcResponseErrorData::SendTransactionPreflightFailure { + err: result.err.as_ref().map(|e| format!("{:?}", e)), + logs: result.logs.clone(), + accounts: result.accounts.as_ref().map(|a| { + serde_json::to_value(a).unwrap_or(serde_json::Value::Null) + }), + units_consumed: result.units_consumed, + return_data: result.return_data.as_ref().map(|d| { + serde_json::to_value(d).unwrap_or(serde_json::Value::Null) + }), + } + } + RpcResponseErrorData::NodeUnhealthy { num_slots_behind } => { + SolanaRpcResponseErrorData::NodeUnhealthy { + num_slots_behind: *num_slots_behind, + } + } + }; + SolanaRpcErrorKind::RpcError { + code: *code, + message: message.clone(), + data: structured_data, + } + } + RpcError::RpcRequestError(msg) => SolanaRpcErrorKind::RpcError { + code: -32600, + message: msg.clone(), + data: SolanaRpcResponseErrorData::Empty, + }, + RpcError::ParseError(msg) => SolanaRpcErrorKind::SerdeJson { + message: msg.clone(), + line: None, + column: None, + }, + RpcError::ForUser(msg) => SolanaRpcErrorKind::Custom { + message: msg.clone(), + }, + } + } + ClientErrorKind::SerdeJson(err) => { + let line = err.line(); + let column = err.column(); + SolanaRpcErrorKind::SerdeJson { + message: err.to_string(), + line: Some(line), + column: Some(column), + } + } + ClientErrorKind::SigningError(err) => SolanaRpcErrorKind::SigningError { + message: err.to_string(), + }, + ClientErrorKind::TransactionError(err) => { + // Extract structured transaction error information + let error_type = format!("{:?}", err) + .split('(') + .next() + .unwrap_or("Unknown") + .to_string(); + SolanaRpcErrorKind::TransactionError { + error_type, + message: err.to_string(), + } + } + ClientErrorKind::Custom(msg) => SolanaRpcErrorKind::Custom { + message: msg.clone(), + }, + _ => SolanaRpcErrorKind::Unknown { + message: self.to_string(), + }, + }; + + EngineError::SolanaRpcError { + chain_id: chain_id.to_string(), + message: self.to_string(), + kind, + } + } +} + fn to_engine_rpc_error_kind(err: &AlloyRpcError) -> RpcErrorKind { match err { AlloyRpcError::ErrorResp(err) => RpcErrorKind::ErrorResp(RpcErrorResponse { diff --git a/core/src/execution_options/mod.rs b/core/src/execution_options/mod.rs index 671327f..c33d974 100644 --- a/core/src/execution_options/mod.rs +++ b/core/src/execution_options/mod.rs @@ -9,6 +9,7 @@ pub mod aa; pub mod auto; pub mod eip7702; pub mod eoa; +pub mod solana; // Base execution options for all transactions // All specific execution options share this diff --git a/core/src/execution_options/solana.rs b/core/src/execution_options/solana.rs new file mode 100644 index 0000000..e2aa7d6 --- /dev/null +++ b/core/src/execution_options/solana.rs @@ -0,0 +1,169 @@ +use engine_solana_core::SolanaInstructionData; +use serde::{Deserialize, Serialize}; +use serde_with::{DisplayFromStr, serde_as}; +use solana_commitment_config::CommitmentLevel as SolanaCommitmentLevel; +use solana_sdk::pubkey::Pubkey; + +/// Solana chain identifier +#[derive(Deserialize, Serialize, Debug, Clone, utoipa::ToSchema, Hash, Eq, PartialEq, Copy)] +pub enum SolanaChainId { + /// Solana mainnet-beta + #[serde(rename = "solana:mainnet")] + SolanaMainnet, + /// Solana devnet + #[serde(rename = "solana:devnet")] + SolanaDevnet, + /// Local Solana cluster (usually localhost) + #[serde(rename = "solana:local")] + SolanaLocal, +} + +impl SolanaChainId { + /// Get the RPC URL for this chain + pub fn default_rpc_url(&self) -> &'static str { + match self { + SolanaChainId::SolanaMainnet => "https://api.mainnet-beta.solana.com", + SolanaChainId::SolanaDevnet => "https://api.devnet.solana.com", + SolanaChainId::SolanaLocal => "http://127.0.0.1:8899", + } + } + + /// Get a string representation for logging + pub fn as_str(&self) -> &'static str { + match self { + SolanaChainId::SolanaMainnet => "solana:mainnet", + SolanaChainId::SolanaDevnet => "solana:devnet", + SolanaChainId::SolanaLocal => "solana:local", + } + } +} + +/// ### Solana Execution Options +/// This struct configures Solana transaction execution. +/// +/// Solana execution sends transactions directly to Solana RPC nodes with +/// support for priority fees, compute budgets, and commitment levels. +/// +/// ### Use Cases +/// - SOL transfers +/// - SPL token operations +/// - Program interactions (NFTs, DeFi, etc.) +/// - Arbitrary instruction composition +/// +/// ### Features +/// - Versioned transactions with address lookup tables +/// - Priority fee configuration for faster inclusion +/// - Compute budget optimization +/// - Configurable commitment levels +/// - Blockhash retry mechanism +#[serde_as] +#[derive(Deserialize, Serialize, Debug, Clone, utoipa::ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct SolanaExecutionOptions { + /// The Solana address (public key) to send transactions from + /// This account must have sufficient SOL to pay for transaction fees + #[serde_as(as = "DisplayFromStr")] + #[schema(value_type = PubkeyDef)] + pub signer_address: Pubkey, + + /// Solana chain to use + pub chain_id: SolanaChainId, + + /// Maximum number of times to retry fetching a new blockhash if transaction fails + /// Default: 0 (fail fast) + /// Max: 1000 + #[serde(default)] + pub max_blockhash_retries: u32, + + /// Commitment level for transaction confirmation + /// Options: "processed", "confirmed", "finalized" + /// Default: "finalized" + #[serde(default)] + pub commitment: CommitmentLevel, + + /// Priority fee for the transaction. If omitted, no priority fee will be used. If provided, engine will add the compute budget instruction to the transaction. + #[serde(default)] + pub priority_fee: Option, + + /// Compute unit limit for the transaction. If omitted, the transaction will use the default compute unit limit. If provided, engine will add the compute budget instruction to the transaction. + #[serde(default)] + pub compute_unit_limit: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug, utoipa::ToSchema, Copy)] +#[schema(title = "Solana Priority Fee")] +#[serde(rename_all = "camelCase", tag = "type")] +pub enum SolanaPriorityFee { + /// Auto-select the priority fee based on the current network conditions + Auto, + /// Manually set the priority fee + #[serde(rename_all = "camelCase")] + Manual { micro_lamports_per_unit: u64 }, + /// Select the priority fee based on the current network conditions + Percentile { percentile: u8 }, +} + +#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema, Default, Debug)] +#[schema(title = "Commitment Level")] +pub enum CommitmentLevel { + #[serde(rename = "confirmed")] + Confirmed, + #[serde(rename = "finalized")] + #[default] + Finalized, +} + +#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)] +#[schema(title = "Pubkey")] +pub struct PubkeyDef(pub String); + +impl CommitmentLevel { + pub fn to_commitment_level(&self) -> SolanaCommitmentLevel { + match self { + CommitmentLevel::Confirmed => SolanaCommitmentLevel::Confirmed, + CommitmentLevel::Finalized => SolanaCommitmentLevel::Finalized, + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, utoipa::ToSchema)] +#[schema(title = "Solana Transaction Options")] +#[serde(rename_all = "camelCase")] +pub struct SolanaTransactionOptions { + /// List of instructions to execute in this transaction + pub instructions: Vec, + + /// Solana execution options + pub execution_options: SolanaExecutionOptions, +} + +/// Request to send a Solana transaction +#[derive(Serialize, Deserialize, Clone, Debug, utoipa::ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct SendSolanaTransactionRequest { + /// Idempotency key for this transaction (defaults to random UUID) + #[serde(default = "super::default_idempotency_key")] + pub idempotency_key: String, + + /// List of Solana instructions to execute + pub instructions: Vec, + + /// Solana execution options + pub execution_options: SolanaExecutionOptions, + + /// Webhook options for transaction status notifications + #[serde(default)] + pub webhook_options: Vec, +} + +/// Response for a queued Solana transaction +#[derive(Serialize, Deserialize, Clone, Debug, utoipa::ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct QueuedSolanaTransactionResponse { + /// The idempotency key for this transaction + pub transaction_id: String, + /// The Solana chain + pub chain_id: SolanaChainId, + /// The signer address + pub signer_address: String, +} diff --git a/core/src/signer.rs b/core/src/signer.rs index 48e75a7..9b4614b 100644 --- a/core/src/signer.rs +++ b/core/src/signer.rs @@ -548,3 +548,82 @@ pub struct TypedDataSignerParams { fn default_account_salt() -> String { "0x".to_string() } + +/// Solana Signer Implementation +#[derive(Clone)] +pub struct SolanaSigner { + pub vault_client: VaultClient, + pub iaw_client: IAWClient, +} + +impl SolanaSigner { + /// Create a new Solana signer + pub fn new(vault_client: VaultClient, iaw_client: IAWClient) -> Self { + Self { + vault_client, + iaw_client, + } + } + + /// Sign a Solana transaction using Vault + pub async fn sign_transaction( + &self, + mut transaction: solana_sdk::transaction::VersionedTransaction, + from: solana_sdk::pubkey::Pubkey, + credentials: &SigningCredential, + ) -> Result { + let signature = match credentials { + SigningCredential::Vault(auth_method) => { + let vault_result = self + .vault_client + .sign_solana_transaction(auth_method.clone(), transaction.clone(), from) + .await + .map_err(|e| { + tracing::error!("Error signing Solana transaction (Vault): {:?}", e); + e + })?; + + vault_result.signature + } + SigningCredential::Iaw { .. } => { + return Err(EngineError::ValidationError { + message: "IAW does not support Solana transaction signing".to_string(), + }); + } + SigningCredential::AwsKms(_) => { + // AWS KMS does not support Solana signing yet + return Err(EngineError::ValidationError { + message: "AWS KMS does not support Solana transaction signing".to_string(), + }); + } + SigningCredential::PrivateKey(_) => { + // Private key signing for Solana would require a different signer type + return Err(EngineError::ValidationError { + message: "Private key signing not yet implemented for Solana".to_string(), + }); + } + }; + + // add the signature to the correct position in the transaction + // get the index of the signer + let signer_index = transaction + .message + .static_account_keys() + .iter() + .position(|key| key == &from) + .ok_or(EngineError::ValidationError { + message: "Signer not found in transaction".to_string(), + })?; + + if signer_index >= transaction.signatures.len() { + transaction.signatures.resize( + signer_index + 1, + solana_sdk::signature::Signature::default(), + ); + }; + + transaction.signatures[signer_index] = signature; + + Ok(transaction) + } +} diff --git a/eip7702-core/Cargo.toml b/eip7702-core/Cargo.toml index a18cf83..2d3f297 100644 --- a/eip7702-core/Cargo.toml +++ b/eip7702-core/Cargo.toml @@ -5,14 +5,14 @@ edition = "2024" [dependencies] alloy = { workspace = true, features = ["serde"] } -tokio = "1.44.2" +tokio = { workspace = true } engine-core = { path = "../core" } -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0" -tracing = "0.1.41" -rand = "0.9" -thiserror = "2.0" -serde_repr = "0.1.20" +serde = { workspace = true } +serde_json = { workspace = true } +tracing = { workspace = true } +rand = { workspace = true } +thiserror = { workspace = true } +serde_repr = { workspace = true } [dev-dependencies] alloy = { workspace = true, features = [ diff --git a/executors/Cargo.toml b/executors/Cargo.toml index 6e2803a..4d957db 100644 --- a/executors/Cargo.toml +++ b/executors/Cargo.toml @@ -4,26 +4,34 @@ version = "0.1.0" edition = "2024" [dependencies] -hex = "0.4.3" -alloy = { version = "1.0.8", features = ["serde"] } +hex = { workspace = true } +alloy = { workspace = true, features = ["serde"] } thirdweb-core = { version = "0.1.0", path = "../thirdweb-core" } -hmac = "0.12.1" -reqwest = "0.12.15" -serde = "1.0.219" -serde_json = "1.0.140" -sha2 = "0.10.9" -thiserror = "2.0.12" -tracing = "0.1.41" +hmac = { workspace = true } +reqwest = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +serde_with = { workspace = true } +sha2 = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } twmq = { version = "0.1.0", path = "../twmq" } engine-aa-types = { version = "0.1.0", path = "../aa-types" } engine-eip7702-core = { version = "0.1.0", path = "../eip7702-core" } engine-core = { version = "0.1.0", path = "../core" } engine-aa-core = { version = "0.1.0", path = "../aa-core" } -rand = "0.9.1" -uuid = { version = "1.17.0", features = ["v4"] } -chrono = "0.4.41" -tokio = { version = "1.45.0", features = ["full"] } -futures = "0.3.31" -moka = { version = "0.12.10", features = ["future"] } -prometheus = "0.13.4" -lazy_static = "1.5.0" +rand = { workspace = true } +uuid = { workspace = true } +chrono = { workspace = true } +tokio = { workspace = true, features = ["full"] } +futures = { workspace = true } +moka = { workspace = true } +prometheus = { workspace = true } +lazy_static = { workspace = true } +engine-solana-core = { version = "0.1.0", path = "../solana-core" } +solana-sdk = { workspace = true } +solana-client = { workspace = true } +solana-transaction-status = { workspace = true } +solana-commitment-config = { workspace = true } +spl-memo-interface = { workspace = true } +base64 = { workspace = true } \ No newline at end of file diff --git a/executors/src/lib.rs b/executors/src/lib.rs index 13a751d..72ad668 100644 --- a/executors/src/lib.rs +++ b/executors/src/lib.rs @@ -2,6 +2,7 @@ pub mod eip7702_executor; pub mod eoa; pub mod external_bundler; pub mod metrics; +pub mod solana_executor; pub mod transaction_registry; pub mod webhook; diff --git a/executors/src/solana_executor/mod.rs b/executors/src/solana_executor/mod.rs new file mode 100644 index 0000000..9cf80bb --- /dev/null +++ b/executors/src/solana_executor/mod.rs @@ -0,0 +1,7 @@ +pub mod worker; +pub mod rpc_cache; +pub mod storage; + +pub use worker::{SolanaExecutorJobHandler, SolanaExecutorJobData, SolanaExecutorResult}; +pub use rpc_cache::SolanaRpcCache; +pub use storage::{LockError, SolanaTransactionAttempt, SolanaTransactionStorage, TransactionLock}; diff --git a/executors/src/solana_executor/rpc_cache.rs b/executors/src/solana_executor/rpc_cache.rs new file mode 100644 index 0000000..4375c58 --- /dev/null +++ b/executors/src/solana_executor/rpc_cache.rs @@ -0,0 +1,75 @@ +use engine_core::execution_options::solana::SolanaChainId; +use moka::future::Cache; +use solana_client::nonblocking::rpc_client::RpcClient; +use std::{sync::Arc, time::Duration}; +use tracing::info; + +/// Cache key for RPC clients +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct RpcCacheKey { + pub chain_id: SolanaChainId, + pub rpc_url: String, +} + +/// Solana RPC client cache with connection pooling +/// +/// This cache maintains RpcClient instances per Solana cluster, +/// with the underlying ConnectionCache providing TCP connection reuse +#[derive(Clone)] +pub struct SolanaRpcCache { + cache: Cache>, + urls: SolanaRpcUrls, +} + +#[derive(Clone)] +pub struct SolanaRpcUrls { + pub devnet: String, + pub mainnet: String, + pub local: String, +} + +impl SolanaRpcCache { + /// Create a new RPC cache with specified TTL and max capacity + pub fn new(urls: SolanaRpcUrls) -> Self { + let cache = Cache::new(3); + + Self { cache, urls } + } + + /// Get or create an RPC client for the given cluster + pub async fn get_or_create(&self, chain_id: SolanaChainId) -> Arc { + let rpc_url = match chain_id { + SolanaChainId::SolanaDevnet => self.urls.devnet.clone(), + SolanaChainId::SolanaMainnet => self.urls.mainnet.clone(), + SolanaChainId::SolanaLocal => self.urls.local.clone(), + }; + + let key = RpcCacheKey { + chain_id: chain_id.clone(), + rpc_url: rpc_url.clone(), + }; + + self.cache + .get_with(key.clone(), async move { + info!( + chain_id = ?chain_id, + rpc_url = ?rpc_url, + "Creating new Solana RPC client with connection cache" + ); + + // Create RPC client + Arc::new(RpcClient::new(rpc_url.clone())) + }) + .await + } + + /// Get the number of cached clients + pub fn len(&self) -> u64 { + self.cache.entry_count() + } + + /// Check if the cache is empty + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} diff --git a/executors/src/solana_executor/storage.rs b/executors/src/solana_executor/storage.rs new file mode 100644 index 0000000..8a88302 --- /dev/null +++ b/executors/src/solana_executor/storage.rs @@ -0,0 +1,435 @@ +use serde::{Deserialize, Serialize}; +use serde_with::{DisplayFromStr, serde_as}; +use solana_sdk::{hash::Hash, signature::Signature}; +use twmq::{ + redis, + redis::{AsyncCommands, aio::ConnectionManager}, +}; + +/// Represents a single attempt to send a Solana transaction +/// This is stored in Redis BEFORE sending to prevent duplicate transactions +#[serde_as] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SolanaTransactionAttempt { + /// The transaction signature + #[serde_as(as = "DisplayFromStr")] + pub signature: Signature, + /// The blockhash used for this attempt + #[serde_as(as = "DisplayFromStr")] + pub blockhash: Hash, + /// The block height at which this blockhash expires + pub blockhash_last_valid_height: u64, + /// Timestamp when the transaction was sent (milliseconds) + pub sent_at: u64, + /// Resubmission attempt number (1 = first submission, 2 = first resubmission, etc.) + pub submission_attempt_number: u32, +} + +impl SolanaTransactionAttempt { + pub fn new( + signature: Signature, + blockhash: Hash, + blockhash_last_valid_height: u64, + submission_attempt_number: u32, + ) -> Self { + Self { + signature, + blockhash, + blockhash_last_valid_height, + sent_at: crate::metrics::current_timestamp_ms(), + submission_attempt_number, + } + } +} + +/// Represents a lock held on a transaction +/// When dropped, the lock is automatically released +pub struct TransactionLock { + redis: ConnectionManager, + lock_key: String, + lock_value: String, +} + +impl TransactionLock { + /// Check if we still hold the lock + pub async fn still_held(&self) -> Result { + let current_value: Option = self.redis.clone().get(&self.lock_key).await?; + Ok(current_value.as_deref() == Some(&self.lock_value)) + } + + /// Explicitly release the lock + pub async fn release(self) -> Result<(), redis::RedisError> { + // Use Lua script to atomically check and delete only if we own the lock + let script = r#" + if redis.call("get", KEYS[1]) == ARGV[1] then + return redis.call("del", KEYS[1]) + else + return 0 + end + "#; + + let _: i32 = redis::Script::new(script) + .key(&self.lock_key) + .arg(&self.lock_value) + .invoke_async(&mut self.redis.clone()) + .await?; + + Ok(()) + } +} + +impl Drop for TransactionLock { + fn drop(&mut self) { + // Best effort release on drop (fire and forget) + let redis = self.redis.clone(); + let lock_key = self.lock_key.clone(); + let lock_value = self.lock_value.clone(); + + tokio::spawn(async move { + let script = r#" + if redis.call("get", KEYS[1]) == ARGV[1] then + return redis.call("del", KEYS[1]) + else + return 0 + end + "#; + + let _: Result = redis::Script::new(script) + .key(&lock_key) + .arg(&lock_value) + .invoke_async(&mut redis.clone()) + .await; + }); + } +} + +#[derive(Debug, Clone, thiserror::Error)] +pub enum LockError { + #[error("Failed to acquire lock: another worker is processing this transaction")] + AlreadyLocked, + + #[error("Redis error: {0}")] + RedisError(String), +} + +impl From for LockError { + fn from(e: redis::RedisError) -> Self { + LockError::RedisError(e.to_string()) + } +} + +/// Storage for Solana transaction attempts +/// Provides atomic operations to prevent duplicate transactions +pub struct SolanaTransactionStorage { + redis: ConnectionManager, + namespace: Option, +} + +impl SolanaTransactionStorage { + pub fn new(redis: ConnectionManager, namespace: Option) -> Self { + Self { redis, namespace } + } + + /// Get the Redis key for a transaction's attempt + fn attempt_key(&self, transaction_id: &str) -> String { + match &self.namespace { + Some(ns) => format!("{ns}:solana_tx_attempt:{transaction_id}"), + None => format!("solana_tx_attempt:{transaction_id}"), + } + } + + /// Get the Redis key for a transaction's lock + fn lock_key(&self, transaction_id: &str) -> String { + match &self.namespace { + Some(ns) => format!("{ns}:solana_tx_lock:{transaction_id}"), + None => format!("solana_tx_lock:{transaction_id}"), + } + } + + /// Try to acquire a distributed lock on a transaction + /// + /// Returns Ok(Some(lock)) if lock acquired + /// Returns Ok(None) if lock is held by another worker + /// Returns Err on Redis errors + pub async fn try_acquire_lock( + &self, + transaction_id: &str, + ) -> Result { + let lock_key = self.lock_key(transaction_id); + // Use a unique value (random UUID) so we can safely release only our lock + let lock_value = uuid::Uuid::new_v4().to_string(); + + // Try to acquire lock with SET NX EX (set if not exists with expiry) + // Lock expires after 120 seconds to prevent deadlocks from crashes + let result: Option = redis::cmd("SET") + .arg(&lock_key) + .arg(&lock_value) + .arg("NX") // Only set if key doesn't exist + .arg("EX") + .arg(120) // Expire after 120 seconds + .arg("GET") // Return old value if any + .query_async(&mut self.redis.clone()) + .await?; + + if result.is_some() { + // Another worker holds the lock + Err(LockError::AlreadyLocked) + } else { + // We acquired the lock! + Ok(TransactionLock { + redis: self.redis.clone(), + lock_key, + lock_value, + }) + } + } + + /// Store an attempt ATOMICALLY before sending to RPC (must hold lock) + /// This is critical to prevent duplicate transactions from crashes + /// Uses Lua script to atomically check lock and store attempt + /// + /// Returns Ok(true) if stored successfully (first time) + /// Returns Ok(false) if attempt already exists (duplicate prevention) + /// Returns Err if lock was lost or other error occurred + pub async fn store_attempt_if_not_exists( + &self, + transaction_id: &str, + attempt: &SolanaTransactionAttempt, + lock: &TransactionLock, + ) -> Result { + let attempt_key = self.attempt_key(transaction_id); + let json = serde_json::to_string(attempt).map_err(|e| { + redis::RedisError::from((redis::ErrorKind::TypeError, "serialization", e.to_string())) + })?; + + // Lua script to atomically verify lock and store attempt + let script = redis::Script::new( + r#" + local lock_key = KEYS[1] + local lock_value = ARGV[1] + local attempt_key = KEYS[2] + local attempt_json = ARGV[2] + local expiry = tonumber(ARGV[3]) + + -- Check if we still hold the lock + local current_lock = redis.call('GET', lock_key) + if current_lock ~= lock_value then + return redis.error_reply('lock lost') + end + + -- Try to store attempt if it doesn't exist + local result = redis.call('SET', attempt_key, attempt_json, 'NX', 'EX', expiry) + if result then + return 1 + else + return 0 + end + "#, + ); + + let result: Result = script + .key(&lock.lock_key) + .key(&attempt_key) + .arg(&lock.lock_value) + .arg(&json) + .arg(600) // 10 minutes expiry + .invoke_async(&mut self.redis.clone()) + .await; + + match result { + Ok(1) => Ok(true), + Ok(0) => Ok(false), + Ok(_) => Err(redis::RedisError::from(( + redis::ErrorKind::IoError, + "unexpected result", + "Unexpected result from Lua script".to_string(), + ))), + Err(e) => { + if e.to_string().contains("lock lost") { + Err(redis::RedisError::from(( + redis::ErrorKind::IoError, + "lock lost", + "Lost lock while storing attempt".to_string(), + ))) + } else { + Err(e) + } + } + } + } + + /// Get the current attempt for a transaction (must hold lock) + /// Uses Lua script to atomically verify lock and read attempt + pub async fn get_attempt( + &self, + transaction_id: &str, + lock: &TransactionLock, + ) -> Result, redis::RedisError> { + let attempt_key = self.attempt_key(transaction_id); + + // Lua script to atomically verify lock and get attempt + let script = redis::Script::new( + r#" + local lock_key = KEYS[1] + local lock_value = ARGV[1] + local attempt_key = KEYS[2] + + -- Check if we still hold the lock + local current_lock = redis.call('GET', lock_key) + if current_lock ~= lock_value then + return redis.error_reply('lock lost') + end + + -- Get the attempt + return redis.call('GET', attempt_key) + "#, + ); + + let result: Result, redis::RedisError> = script + .key(&lock.lock_key) + .key(&attempt_key) + .arg(&lock.lock_value) + .invoke_async(&mut self.redis.clone()) + .await; + + match result { + Ok(Some(json)) => { + let attempt = serde_json::from_str(&json).map_err(|e| { + redis::RedisError::from(( + redis::ErrorKind::TypeError, + "deserialization", + e.to_string(), + )) + })?; + Ok(Some(attempt)) + } + Ok(None) => Ok(None), + Err(e) => { + if e.to_string().contains("lock lost") { + Err(redis::RedisError::from(( + redis::ErrorKind::IoError, + "lock lost", + "Lost lock while reading attempt".to_string(), + ))) + } else { + Err(e) + } + } + } + } + + /// Update an existing attempt (for retry scenarios, must hold lock) + /// This will overwrite the existing attempt + /// Uses Lua script to atomically verify lock and update attempt + pub async fn update_attempt( + &self, + transaction_id: &str, + attempt: &SolanaTransactionAttempt, + lock: &TransactionLock, + ) -> Result<(), redis::RedisError> { + let attempt_key = self.attempt_key(transaction_id); + let json = serde_json::to_string(attempt).map_err(|e| { + redis::RedisError::from((redis::ErrorKind::TypeError, "serialization", e.to_string())) + })?; + + // Lua script to atomically verify lock and update attempt + let script = redis::Script::new( + r#" + local lock_key = KEYS[1] + local lock_value = ARGV[1] + local attempt_key = KEYS[2] + local attempt_json = ARGV[2] + local expiry = tonumber(ARGV[3]) + + -- Check if we still hold the lock + local current_lock = redis.call('GET', lock_key) + if current_lock ~= lock_value then + return redis.error_reply('lock lost') + end + + -- Update the attempt + redis.call('SETEX', attempt_key, expiry, attempt_json) + return 1 + "#, + ); + + let result: Result = script + .key(&lock.lock_key) + .key(&attempt_key) + .arg(&lock.lock_value) + .arg(&json) + .arg(600) // 10 minutes expiry + .invoke_async(&mut self.redis.clone()) + .await; + + match result { + Ok(_) => Ok(()), + Err(e) => { + if e.to_string().contains("lock lost") { + Err(redis::RedisError::from(( + redis::ErrorKind::IoError, + "lock lost", + "Lost lock while updating attempt".to_string(), + ))) + } else { + Err(e) + } + } + } + } + + /// Delete an attempt after successful confirmation or permanent failure + pub async fn delete_attempt(&self, transaction_id: &str) -> Result<(), redis::RedisError> { + let key = self.attempt_key(transaction_id); + self.redis.clone().del::<_, ()>(&key).await?; + Ok(()) + } + + /// Check if an attempt exists without retrieving it + pub async fn has_attempt(&self, transaction_id: &str) -> Result { + let key = self.attempt_key(transaction_id); + let exists: bool = self.redis.clone().exists(&key).await?; + Ok(exists) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn mock_attempt() -> SolanaTransactionAttempt { + use std::str::FromStr; + SolanaTransactionAttempt { + signature: solana_sdk::signature::Signature::from_str( + "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7" + ).unwrap(), + blockhash: "EkSnNWid2cvwEVnVx9aBqawnmiCNiDgp3gUdkDPTKN1N".parse().unwrap(), + blockhash_last_valid_height: 123456789, + sent_at: 1234567890000, + submission_attempt_number: 1, + } + } + + #[test] + fn test_attempt_serialization() { + let attempt = mock_attempt(); + let json = serde_json::to_string(&attempt).unwrap(); + let deserialized: SolanaTransactionAttempt = serde_json::from_str(&json).unwrap(); + + assert_eq!(attempt.signature, deserialized.signature); + assert_eq!(attempt.blockhash, deserialized.blockhash); + assert_eq!( + attempt.submission_attempt_number, + deserialized.submission_attempt_number + ); + } + + #[test] + fn test_attempt_parse_signature() { + let attempt = mock_attempt(); + assert_eq!( + attempt.signature.to_string(), + "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7" + ); + } +} diff --git a/executors/src/solana_executor/worker.rs b/executors/src/solana_executor/worker.rs new file mode 100644 index 0000000..85280eb --- /dev/null +++ b/executors/src/solana_executor/worker.rs @@ -0,0 +1,961 @@ +use base64::Engine; +use engine_core::{ + credentials::SigningCredential, + error::{EngineError, SolanaRpcErrorToEngineError}, + execution_options::{solana::{SolanaPriorityFee, SolanaTransactionOptions}, WebhookOptions}, + signer::SolanaSigner, +}; +use engine_solana_core::{transaction::{InstructionDataEncoding, SolanaTransaction}, SolanaInstructionData}; +use serde::{Deserialize, Serialize}; +use solana_client::{ + nonblocking::rpc_client::RpcClient, + rpc_config::{RpcSendTransactionConfig, RpcTransactionConfig}, +}; +use solana_commitment_config::{CommitmentConfig, CommitmentLevel}; +use solana_sdk::{ + hash::Hash, + pubkey::Pubkey, + transaction::VersionedTransaction, +}; +use solana_transaction_status::{ + EncodedTransactionWithStatusMeta, UiTransactionEncoding +}; +use spl_memo_interface::instruction::build_memo; +use std::{sync::Arc, time::Duration}; +use tracing::{error, info, warn}; +use twmq::{ + DurableExecution, FailHookData, NackHookData, Queue, SuccessHookData, UserCancellable, + error::TwmqError, + hooks::TransactionContext, + job::{BorrowedJob, JobError, JobResult, RequeuePosition, ToJobError}, +}; + +use crate::{ + solana_executor::{ + rpc_cache::SolanaRpcCache, + storage::{LockError, SolanaTransactionAttempt, SolanaTransactionStorage, TransactionLock}, + }, + transaction_registry::TransactionRegistry, + webhook::{ + WebhookJobHandler, + envelope::{ExecutorStage, HasTransactionMetadata, HasWebhookOptions, WebhookCapable}, + }, +}; + +const CONFIRMATION_RETRY_DELAY: Duration = Duration::from_millis(200); +const NETWORK_ERROR_RETRY_DELAY: Duration = Duration::from_secs(2); +const MAX_SEND_ATTEMPTS_WITHOUT_TRANSACTION: u32 = 500; + +// ========== JOB DATA ========== +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct SolanaExecutorJobData { + pub transaction_id: String, + pub transaction: SolanaTransactionOptions, + pub signing_credential: SigningCredential, + #[serde(default)] + pub webhook_options: Vec, +} + +impl HasWebhookOptions for SolanaExecutorJobData { + fn webhook_options(&self) -> Vec { + self.webhook_options.clone() + } +} + +impl HasTransactionMetadata for SolanaExecutorJobData { + fn transaction_id(&self) -> String { + self.transaction_id.clone() + } +} + +// ========== SUCCESS RESULT ========== +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct SolanaExecutorResult { + pub transaction_id: String, + pub signature: String, + pub signer_address: String, + pub chain_id: String, + pub submission_attempt_number: u32, + pub slot: u64, + pub block_time: Option, + pub transaction: EncodedTransactionWithStatusMeta, +} + +// ========== ERROR TYPES ========== +#[derive(Serialize, Deserialize, Debug, Clone, thiserror::Error)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE", tag = "errorCode")] +pub enum SolanaExecutorError { + #[error("Failed to build transaction: {inner_error}")] + #[serde(rename_all = "camelCase")] + TransactionBuildFailed { inner_error: String }, + + #[error("Failed to sign transaction: {inner_error}")] + #[serde(rename_all = "camelCase")] + SigningFailed { inner_error: EngineError }, + + #[error("Failed to send transaction: {inner_error}")] + #[serde(rename_all = "camelCase")] + SendFailed { inner_error: EngineError }, + + #[error("Transaction failed on-chain: {reason}")] + #[serde(rename_all = "camelCase")] + TransactionFailed { reason: String }, + + #[error("RPC error: {inner_error}")] + #[serde(rename_all = "camelCase")] + RpcError { inner_error: EngineError }, + + #[error("Failed to get priority fees: {inner_error}")] + #[serde(rename_all = "camelCase")] + PriorityFeeError { inner_error: EngineError }, + + #[error("Blockhash expired, retrying with new blockhash (resubmission {submission_attempt_number})")] + #[serde(rename_all = "camelCase")] + BlockhashExpired { submission_attempt_number: u32 }, + + #[error("Max retries exceeded: {max_retries}")] + #[serde(rename_all = "camelCase")] + MaxRetriesExceeded { max_retries: u32 }, + + #[error("Transaction sent successfully")] + #[serde(rename_all = "camelCase")] + TransactionSent { + /// The signature of the sent transaction + signature: String, + /// Resubmission attempt number of this transaction + submission_attempt_number: u32, + }, + + #[error("Transaction not yet confirmed")] + #[serde(rename_all = "camelCase")] + NotYetConfirmed { + /// The signature of the transaction we're waiting for + signature: String, + }, + + #[error("Failed to acquire lock: another worker is processing")] + #[serde(rename_all = "camelCase")] + LockHeldByAnotherWorker, + + #[error("Lost lock during execution")] + LockLost, + + #[error("Internal error: {message}")] + InternalError { message: String }, + + #[error("Transaction cancelled by user")] + UserCancelled, +} + +impl From for SolanaExecutorError { + fn from(e: LockError) -> Self { + match e { + LockError::AlreadyLocked => SolanaExecutorError::LockHeldByAnotherWorker, + LockError::RedisError(msg) => SolanaExecutorError::InternalError { message: msg }, + } + } +} + +impl From for SolanaExecutorError { + fn from(error: TwmqError) -> Self { + SolanaExecutorError::InternalError { + message: format!("Queue error: {error}"), + } + } +} + +impl UserCancellable for SolanaExecutorError { + fn user_cancelled() -> Self { + SolanaExecutorError::UserCancelled + } +} + +impl SolanaExecutorError { + /// Check if this is actually a successful send (not an error) + pub fn is_send_success(&self) -> bool { + matches!(self, SolanaExecutorError::TransactionSent { .. }) + } + + /// Get the signature if this error contains one + pub fn signature(&self) -> Option<&str> { + match self { + SolanaExecutorError::TransactionSent { signature, .. } => Some(signature.as_str()), + SolanaExecutorError::NotYetConfirmed { signature } => Some(signature.as_str()), + _ => None, + } + } +} + +// ========== HANDLER ========== +pub struct SolanaExecutorJobHandler { + pub solana_signer: Arc, + pub rpc_cache: Arc, + pub storage: Arc, + pub webhook_queue: Arc>, + pub transaction_registry: Arc, +} + +impl ExecutorStage for SolanaExecutorJobHandler { + fn stage_name() -> &'static str { + "solana_executor" + } + + fn executor_name() -> &'static str { + "solana_executor" + } +} + +impl WebhookCapable for SolanaExecutorJobHandler { + fn webhook_queue(&self) -> &Arc> { + &self.webhook_queue + } +} + +impl DurableExecution for SolanaExecutorJobHandler { + type Output = SolanaExecutorResult; + type ErrorData = SolanaExecutorError; + type JobData = SolanaExecutorJobData; + + #[tracing::instrument( + skip(self, job), + fields( + transaction_id = job.job.id, + stage = Self::stage_name() + ) + )] + async fn process( + &self, + job: &BorrowedJob, + ) -> JobResult { + // let queued_at_ms = job.job.created_at * 1000; + let data = &job.job.data; + let transaction_id = &data.transaction_id; + + info!("Starting to process Solana transaction"); + + // Try to acquire lock - NACK if another worker has it + let lock = self + .storage + .try_acquire_lock(transaction_id) + .await + .map_err(|e| match e { + LockError::AlreadyLocked => { + info!(transaction_id = %transaction_id, "Another worker holds lock, nacking"); + SolanaExecutorError::LockHeldByAnotherWorker + .nack(Some(CONFIRMATION_RETRY_DELAY), RequeuePosition::Last) + } + LockError::RedisError(msg) => { + SolanaExecutorError::InternalError { message: msg }.fail() + } + })?; + + info!(transaction_id = %transaction_id, "Acquired lock"); + + let rpc_client = self.rpc_cache.get_or_create(data.transaction.execution_options.chain_id).await; + + let result = self.execute_transaction(&rpc_client, data, &lock, job.job.attempts).await; + + if let Err(e) = lock.release().await { + warn!(transaction_id = %transaction_id, error = ?e, "Failed to release lock"); + } + + result + } + + + + async fn on_success( + &self, + job: &BorrowedJob, + success_data: SuccessHookData<'_, Self::Output>, + tx: &mut TransactionContext<'_>, + ) { + let transaction_id = &job.job.data.transaction_id; + info!( + transaction_id = %transaction_id, + signature = %success_data.result.signature, + slot = success_data.result.slot, + "Transaction confirmed" + ); + + let _ = self.storage.delete_attempt(transaction_id).await; + self.transaction_registry + .add_remove_command(tx.pipeline(), transaction_id); + + if let Err(e) = self.queue_success_webhook(job, success_data, tx) { + error!( + transaction_id = %transaction_id, + error = ?e, + "Failed to queue success webhook" + ); + } + } + + async fn on_fail( + &self, + job: &BorrowedJob, + fail_data: FailHookData<'_, Self::ErrorData>, + tx: &mut TransactionContext<'_>, + ) { + let transaction_id = &job.job.data.transaction_id; + error!( + transaction_id = %transaction_id, + error = ?fail_data.error, + "Transaction permanently failed" + ); + + let _ = self.storage.delete_attempt(transaction_id).await; + self.transaction_registry + .add_remove_command(tx.pipeline(), transaction_id); + + if let Err(e) = self.queue_fail_webhook(job, fail_data, tx) { + error!( + transaction_id = %transaction_id, + error = ?e, + "Failed to queue fail webhook" + ); + } + } + + async fn on_nack( + &self, + job: &BorrowedJob, + nack_data: NackHookData<'_, Self::ErrorData>, + tx: &mut TransactionContext<'_>, + ) { + let transaction_id = &job.job.data.transaction_id; + + match nack_data.error { + // Special case: TransactionSent is actually a success, send success webhook with stage="send" + SolanaExecutorError::TransactionSent { signature, submission_attempt_number } => { + info!( + transaction_id = %transaction_id, + signature = %signature, + submission_attempt_number = submission_attempt_number, + "Transaction sent to RPC" + ); + + #[derive(serde::Serialize, Clone)] + #[serde(rename_all = "camelCase")] + struct TransactionSentPayload { + signature: String, + submission_attempt_number: u32, + } + + let payload = TransactionSentPayload { + signature: signature.clone(), + submission_attempt_number: *submission_attempt_number, + }; + + if let Err(e) = self.queue_webhook_with_custom_payload( + job, + payload, + crate::webhook::envelope::StageEvent::Success, + "send", + tx, + ) { + error!( + transaction_id = %transaction_id, + error = ?e, + "Failed to queue send webhook" + ); + } + } + + // Don't send webhook for NotYetConfirmed - silent retry + SolanaExecutorError::NotYetConfirmed { .. } => {} + + // For all other errors (network errors, RPC errors, etc.), send nack webhook + _ => { + warn!( + transaction_id = %transaction_id, + error = ?nack_data.error, + "Retrying after error" + ); + if let Err(e) = self.queue_nack_webhook(job, nack_data, tx) { + error!( + transaction_id = %transaction_id, + error = ?e, + "Failed to queue nack webhook" + ); + } + } + } + } +} + +impl SolanaExecutorJobHandler { + /// Helper to convert Solana RPC errors with context + fn to_engine_solana_error(&self, e: &solana_client::client_error::ClientError, chain_id: &str) -> EngineError { + e.to_engine_solana_error(chain_id) + } + + /// Get priority fee at a given percentile + /// ALWAYS NACK on error - this is a network operation that can always be retried + async fn get_percentile_compute_unit_price( + &self, + rpc_client: &RpcClient, + writable_accounts: &[Pubkey], + percentile: u8, + chain_id: &str, + ) -> JobResult { + let mut fee_history = rpc_client + .get_recent_prioritization_fees(writable_accounts) + .await + .map_err(|e| { + warn!( + chain_id = %chain_id, + error = %e, + "Failed to get priority fees" + ); + let engine_error = self.to_engine_solana_error(&e, chain_id,); + SolanaExecutorError::PriorityFeeError { + inner_error: engine_error, + } + .nack(Some(NETWORK_ERROR_RETRY_DELAY), RequeuePosition::Last) + })?; + + fee_history.sort_by_key(|a| a.prioritization_fee); + let percentile_index = ((percentile as f64 / 100.0) * fee_history.len() as f64).round() as usize; + + let result = if percentile_index < fee_history.len() { + fee_history[percentile_index].prioritization_fee + } else { + // Fallback to max if index out of bounds + let fallback = fee_history.last().map(|f| f.prioritization_fee).unwrap_or(0); + warn!( + chain_id = %chain_id, + percentile_index = percentile_index, + history_len = fee_history.len(), + "Priority fee percentile out of bounds" + ); + fallback + }; + + Ok(result) + } + + fn get_writable_accounts( + &self, + transaction: &SolanaTransactionOptions, + ) -> Vec { + transaction + .instructions + .iter() + .flat_map(|i| { + i.accounts + .iter() + .filter(|a| a.is_writable) + .map(|a| a.pubkey) + }) + .collect() + } + + /// Compile a Solana transaction with priority fees and memo instruction + /// ALWAYS NACK on error - network operations can be retried + /// + /// Adds a memo instruction with the transaction_id to ensure unique signatures + /// even when rapidly resubmitting with the same blockhash + async fn compile_transaction( + &self, + transaction: &SolanaTransactionOptions, + rpc_client: &RpcClient, + recent_blockhash: Hash, + chain_id: &str, + transaction_id: &str, + ) -> JobResult { + let compute_unit_price = if let Some(price_config) = &transaction.execution_options.priority_fee { + let price = match price_config { + SolanaPriorityFee::Auto => { + self.get_percentile_compute_unit_price( + rpc_client, + &self.get_writable_accounts(transaction), + 75, + chain_id, + ) + .await? + } + SolanaPriorityFee::Manual { micro_lamports_per_unit } => { + *micro_lamports_per_unit + } + SolanaPriorityFee::Percentile { percentile } => { + self.get_percentile_compute_unit_price( + rpc_client, + &self.get_writable_accounts(transaction), + *percentile, + chain_id, + ) + .await? + } + }; + Some(price) + } else { + None + }; + + // Add memo instruction with transaction_id for unique signatures + let memo_data = format!("thirdweb-engine:{}", transaction_id); + let memo_ix = build_memo(&spl_memo_interface::v3::id(), memo_data.as_bytes(), &[]); + + let mut instructions = transaction.instructions.clone(); + let memo_data_base64 = base64::engine::general_purpose::STANDARD.encode(memo_data.as_bytes()); + instructions.push(SolanaInstructionData { + program_id: memo_ix.program_id, + accounts: vec![], + data: memo_data_base64, + encoding: InstructionDataEncoding::Base64, + }); + + let solana_transaction = SolanaTransaction { + instructions, + compute_unit_price, + compute_unit_limit: transaction.execution_options.compute_unit_limit, + recent_blockhash, + }; + + let versioned_tx = solana_transaction + .to_versioned_transaction(transaction.execution_options.signer_address, recent_blockhash) + .map_err(|e| { + error!( + transaction_id = %transaction_id, + error = %e, + "Failed to build transaction" + ); + SolanaExecutorError::TransactionBuildFailed { + inner_error: e.to_string(), + } + .fail() + })?; + + Ok(versioned_tx) + } + + + /// Main execution flow: + /// 1. Fetch last attempt from storage + /// 2. If attempt exists, check if confirmed + /// 3. If confirmed, return success + /// 4. If not confirmed, check if blockhash valid + /// 5. If blockhash invalid or no attempt exists, send new transaction + /// 6. Retry until limit + async fn execute_transaction( + &self, + rpc_client: &RpcClient, + job_data: &SolanaExecutorJobData, + lock: &TransactionLock, + job_attempt_number: u32, + ) -> JobResult { + let transaction_id = &job_data.transaction_id; + let chain_id_str = &job_data.transaction.execution_options.chain_id; + let signer_address = job_data.transaction.execution_options.signer_address; + let commitment_level = job_data.transaction.execution_options.commitment.to_commitment_level(); + let commitment = CommitmentConfig { commitment: commitment_level }; + let max_retries = job_data.transaction.execution_options.max_blockhash_retries; + + // Verify we still hold the lock + self.verify_lock(lock, transaction_id).await?; + + // Step 1: Fetch last attempt from storage + let stored_attempt = self + .storage + .get_attempt(transaction_id, lock) + .await + .map_err(|e| self.handle_redis_error(e, transaction_id))?; + + if let Some(attempt) = &stored_attempt { + // Step 2: Attempt exists, check confirmation status + let signature = attempt.signature; + let submission_attempt_number = attempt.submission_attempt_number; + + // Step 3: Check if transaction is confirmed + match rpc_client.get_signature_status_with_commitment(&signature, commitment).await { + Ok(Some(Ok(()))) => { + // Transaction confirmed! Fetch full transaction details + info!( + transaction_id = %transaction_id, + signature = %signature, + submission_attempt_number = submission_attempt_number, + "Transaction confirmed, fetching details" + ); + + let transaction_details = rpc_client + .get_transaction_with_config(&signature, RpcTransactionConfig { + encoding: Some(UiTransactionEncoding::Json), + commitment: Some(commitment), + max_supported_transaction_version: Some(0), + }) + .await + .map_err(|e| { + error!( + transaction_id = %transaction_id, + signature = %signature, + error = %e, + "Failed to fetch transaction details" + ); + let engine_error = self.to_engine_solana_error(&e, chain_id_str.as_str()); + SolanaExecutorError::RpcError { + inner_error: engine_error, + } + .nack(Some(NETWORK_ERROR_RETRY_DELAY), RequeuePosition::Last) + })?; + + return Ok(SolanaExecutorResult { + transaction_id: transaction_id.clone(), + signature: signature.to_string(), + signer_address: signer_address.to_string(), + chain_id: chain_id_str.as_str().to_string(), + submission_attempt_number, + slot: transaction_details.slot, + block_time: transaction_details.block_time, + transaction: transaction_details.transaction, + }); + } + Ok(Some(Err(tx_error))) => { + // Transaction failed on-chain - permanent failure + error!( + transaction_id = %transaction_id, + signature = %signature, + error = ?tx_error, + submission_attempt_number = submission_attempt_number, + "Transaction failed on-chain (permanent failure)" + ); + let _ = self.storage.delete_attempt(transaction_id).await; + return Err(SolanaExecutorError::TransactionFailed { + reason: format!("{:?}", tx_error), + } + .fail()); + } + Ok(None) => { + // Step 4: Not confirmed yet, check if blockhash is still valid + let time_since_sent = crate::metrics::current_timestamp_ms().saturating_sub(attempt.sent_at); + let min_wait_before_resubmit_ms = 30_000; // Wait at least 30 seconds before resubmitting + + if time_since_sent < min_wait_before_resubmit_ms { + // Haven't waited long enough - keep checking + return Err(SolanaExecutorError::NotYetConfirmed { + signature: signature.to_string(), + } + .nack(Some(CONFIRMATION_RETRY_DELAY), RequeuePosition::Last)); + } + + // Check blockhash validity + match rpc_client.is_blockhash_valid(&attempt.blockhash, CommitmentConfig { commitment: CommitmentLevel::Finalized }).await { + Ok(true) => { + // Blockhash still valid, not confirmed yet - retry + return Err(SolanaExecutorError::NotYetConfirmed { + signature: signature.to_string(), + } + .nack(Some(CONFIRMATION_RETRY_DELAY), RequeuePosition::Last)); + } + Ok(false) => { + // Blockhash expired, need to resubmit + warn!( + transaction_id = %transaction_id, + signature = %signature, + submission_attempt_number = submission_attempt_number, + "Blockhash expired, resubmitting" + ); + + // Check if we've exceeded max retries + if submission_attempt_number > max_retries { + error!( + transaction_id = %transaction_id, + submission_attempt_number = submission_attempt_number, + max_retries = max_retries, + "Max retries exceeded" + ); + let _ = self.storage.delete_attempt(transaction_id).await; + return Err(SolanaExecutorError::MaxRetriesExceeded { + max_retries, + } + .fail()); + } + + // Delete old attempt and resubmit + self.storage + .delete_attempt(transaction_id) + .await + .map_err(|e| { + error!( + transaction_id = %transaction_id, + error = %e, + "Failed to delete old attempt" + ); + SolanaExecutorError::InternalError { + message: format!("Failed to delete old attempt: {e}"), + } + .fail() + })?; + } + Err(e) => { + // RPC error checking blockhash + warn!( + transaction_id = %transaction_id, + error = %e, + "RPC error checking blockhash" + ); + let engine_error = self.to_engine_solana_error(&e, chain_id_str.as_str()); + return Err(SolanaExecutorError::RpcError { + inner_error: engine_error, + } + .nack(Some(NETWORK_ERROR_RETRY_DELAY), RequeuePosition::Last)); + } + } + } + Err(e) => { + // RPC error getting signature status + warn!( + transaction_id = %transaction_id, + error = %e, + "RPC error getting signature status" + ); + let engine_error = self.to_engine_solana_error(&e, chain_id_str.as_str()); + return Err(SolanaExecutorError::RpcError { + inner_error: engine_error, + } + .nack(Some(NETWORK_ERROR_RETRY_DELAY), RequeuePosition::Last)); + } + } + } + + // Step 5: No attempt exists or blockhash expired - send new transaction + let submission_attempt_number = stored_attempt.as_ref().map(|a| a.submission_attempt_number + 1).unwrap_or(1); + + // Check job attempt limits + if stored_attempt.is_none() && job_attempt_number > MAX_SEND_ATTEMPTS_WITHOUT_TRANSACTION { + error!( + transaction_id = %transaction_id, + job_attempt_number = job_attempt_number, + "Max send attempts exceeded" + ); + return Err(SolanaExecutorError::MaxRetriesExceeded { + max_retries: MAX_SEND_ATTEMPTS_WITHOUT_TRANSACTION + }.fail()); + } + + if submission_attempt_number > max_retries + 1 { + error!( + transaction_id = %transaction_id, + submission_attempt_number = submission_attempt_number, + max_retries = max_retries, + "Max resubmission retries exceeded" + ); + return Err(SolanaExecutorError::MaxRetriesExceeded { max_retries }.fail()); + } + + info!( + transaction_id = %transaction_id, + submission_attempt_number = submission_attempt_number, + "Sending transaction" + ); + + let (recent_blockhash, last_valid_height) = rpc_client + .get_latest_blockhash_with_commitment(commitment) + .await + .map_err(|e| { + error!( + transaction_id = %transaction_id, + error = %e, + "Failed to get blockhash" + ); + let engine_error = self.to_engine_solana_error(&e, chain_id_str.as_str()); + SolanaExecutorError::RpcError { + inner_error: engine_error, + } + .nack(Some(NETWORK_ERROR_RETRY_DELAY), RequeuePosition::Last) + })?; + + // Compile and sign transaction + let versioned_tx = self + .compile_transaction( + &job_data.transaction, + rpc_client, + recent_blockhash, + chain_id_str.as_str(), + transaction_id, + ) + .await?; + + let signed_tx = self + .solana_signer + .sign_transaction(versioned_tx, signer_address, &job_data.signing_credential) + .await + .map_err(|e| { + error!( + transaction_id = %transaction_id, + error = ?e, + "Failed to sign transaction" + ); + SolanaExecutorError::SigningFailed { inner_error: e }.fail() + })?; + + let signature = signed_tx.signatures[0]; + + // Store attempt BEFORE sending to prevent duplicates + let attempt = SolanaTransactionAttempt::new( + signature, + recent_blockhash, + last_valid_height, + submission_attempt_number, + ); + + let stored = self + .storage + .store_attempt_if_not_exists(transaction_id, &attempt, lock) + .await + .map_err(|e| self.handle_redis_error(e, transaction_id))?; + + if !stored { + warn!( + transaction_id = %transaction_id, + "Attempt already stored (race condition)" + ); + return Err(SolanaExecutorError::InternalError { + message: "Attempt already stored".to_string(), + } + .nack(Some(CONFIRMATION_RETRY_DELAY), RequeuePosition::Last)); + } + + // Send transaction to RPC + let config = RpcSendTransactionConfig { + skip_preflight: false, + preflight_commitment: Some(commitment_level), + ..Default::default() + }; + + match rpc_client.send_transaction_with_config(&signed_tx, config).await { + Ok(_) => { + info!( + transaction_id = %transaction_id, + signature = %signature, + submission_attempt_number = submission_attempt_number, + "Transaction sent to RPC" + ); + // Return TransactionSent "error" which will trigger a send success webhook + Err(SolanaExecutorError::TransactionSent { + signature: signature.to_string(), + submission_attempt_number, + } + .nack(Some(CONFIRMATION_RETRY_DELAY), RequeuePosition::Last)) + } + Err(e) => { + error!( + transaction_id = %transaction_id, + signature = %signature, + error = %e, + "Failed to send transaction" + ); + let engine_error = self.to_engine_solana_error(&e, chain_id_str.as_str()); + + let is_retryable = is_send_error_retryable(&engine_error); + if is_retryable { + // Network error or temporary issue - retry + Err(SolanaExecutorError::SendFailed { + inner_error: engine_error, + } + .nack(Some(NETWORK_ERROR_RETRY_DELAY), RequeuePosition::Last)) + } else { + // Permanent error - fail + let _ = self.storage.delete_attempt(transaction_id).await; + Err(SolanaExecutorError::SendFailed { + inner_error: engine_error, + } + .fail()) + } + } + } + } + + async fn verify_lock( + &self, + lock: &TransactionLock, + transaction_id: &str, + ) -> JobResult<(), SolanaExecutorError> { + match lock.still_held().await { + Ok(true) => Ok(()), + Ok(false) => { + warn!(transaction_id = %transaction_id, "Lost lock"); + Err(SolanaExecutorError::LockLost + .nack(Some(CONFIRMATION_RETRY_DELAY), RequeuePosition::Last)) + } + Err(e) => Err(SolanaExecutorError::InternalError { + message: format!("Failed to check lock: {e}"), + } + .fail()), + } + } + + fn handle_redis_error( + &self, + error: twmq::redis::RedisError, + transaction_id: &str, + ) -> JobError { + if error.to_string().contains("lock lost") { + warn!(transaction_id = %transaction_id, "Lock lost during Redis operation"); + SolanaExecutorError::LockLost.nack(Some(CONFIRMATION_RETRY_DELAY), RequeuePosition::Last) + } else { + SolanaExecutorError::InternalError { + message: format!("Redis error: {error}"), + } + .fail() + } + } +} + +/// Determines if a Solana send error should be retried +/// Network errors and temporary issues should be retried +/// Bad transactions (invalid signature, insufficient funds, etc.) should not be retried +fn is_send_error_retryable(error: &EngineError) -> bool { + use engine_core::error::{SolanaRpcErrorKind, SolanaRpcResponseErrorData}; + + match error { + EngineError::SolanaRpcError { kind, .. } => match kind { + // Network/IO errors are always retryable + SolanaRpcErrorKind::Io { .. } => true, + SolanaRpcErrorKind::Reqwest { .. } => true, + + // RPC errors need more inspection + SolanaRpcErrorKind::RpcError { data, message, .. } => { + // Check if it's a preflight failure + if let SolanaRpcResponseErrorData::SendTransactionPreflightFailure { .. } = data { + return false; + } + + // Check message for permanent errors + let msg_lower = message.to_lowercase(); + if msg_lower.contains("invalid signature") + || msg_lower.contains("signature verification failed") + || msg_lower.contains("insufficient funds") + || msg_lower.contains("account not found") + || msg_lower.contains("transaction already processed") + || msg_lower.contains("invalid account") + { + return false; + } + + true + } + + // Transaction errors are permanent + SolanaRpcErrorKind::TransactionError { .. } => false, + + // Signing errors are permanent + SolanaRpcErrorKind::SigningError { .. } => false, + + // JSON/parse errors might be temporary + SolanaRpcErrorKind::SerdeJson { .. } => true, + + // Custom errors - check message + SolanaRpcErrorKind::Custom { message } => { + !message.to_lowercase().contains("invalid") + } + + // Unknown errors - be conservative and retry + SolanaRpcErrorKind::Unknown { .. } => true, + } + // Other engine errors - be conservative and retry + _ => true, + } +} diff --git a/executors/src/webhook/envelope.rs b/executors/src/webhook/envelope.rs index b043173..ef02d50 100644 --- a/executors/src/webhook/envelope.rs +++ b/executors/src/webhook/envelope.rs @@ -133,6 +133,20 @@ pub trait WebhookCapable: DurableExecution + ExecutorStage { success_data: SuccessHookData<'_, Self::Output>, tx: &mut TransactionContext<'_>, ) -> Result<(), TwmqError> + where + Self::JobData: HasWebhookOptions, + Self::Output: Serialize + Clone, + { + self.queue_success_webhook_with_stage(job, success_data, tx, None) + } + + fn queue_success_webhook_with_stage( + &self, + job: &BorrowedJob, + success_data: SuccessHookData<'_, Self::Output>, + tx: &mut TransactionContext<'_>, + stage_override: Option<&str>, + ) -> Result<(), TwmqError> where Self::JobData: HasWebhookOptions, Self::Output: Serialize + Clone, @@ -140,12 +154,15 @@ pub trait WebhookCapable: DurableExecution + ExecutorStage { let webhook_options = job.job.data.webhook_options(); for w in webhook_options { + let stage_name = stage_override + .unwrap_or_else(|| Self::stage_name()) + .to_string(); let envelope = WebhookNotificationEnvelope { notification_id: Uuid::new_v4().to_string(), transaction_id: job.job.transaction_id(), timestamp: chrono::Utc::now().timestamp().try_into().unwrap(), executor_name: Self::executor_name().to_string(), - stage_name: Self::stage_name().to_string(), + stage_name: stage_name.clone(), event_type: StageEvent::Success, payload: SerializableSuccessData { result: success_data.result.clone(), @@ -160,6 +177,74 @@ pub trait WebhookCapable: DurableExecution + ExecutorStage { Ok(()) } + /// Queue a webhook with custom payload and stage + /// This allows sending any serializable data as the webhook payload + fn queue_webhook_with_custom_payload( + &self, + job: &BorrowedJob, + payload: P, + event_type: StageEvent, + stage_override: &str, + tx: &mut TransactionContext<'_>, + ) -> Result<(), TwmqError> + where + Self::JobData: HasWebhookOptions, + { + let webhook_options = job.job.data.webhook_options(); + + for w in webhook_options { + let envelope = WebhookNotificationEnvelope { + notification_id: Uuid::new_v4().to_string(), + transaction_id: job.job.transaction_id(), + timestamp: chrono::Utc::now().timestamp().try_into().unwrap(), + executor_name: Self::executor_name().to_string(), + stage_name: stage_override.to_string(), + event_type: event_type.clone(), + payload: payload.clone(), + delivery_target_url: Some(w.url.clone()), + user_metadata: w.user_metadata.clone(), + }; + + let webhook_payload = crate::webhook::WebhookJobPayload { + url: w.url.clone(), + body: serde_json::to_string(&envelope)?, + headers: Some( + [ + ("Content-Type".to_string(), "application/json".to_string()), + ( + "User-Agent".to_string(), + format!("{}/{}", Self::executor_name(), stage_override), + ), + ] + .into_iter() + .collect(), + ), + hmac_secret: w.secret.clone(), + http_method: Some("POST".to_string()), + }; + + let mut webhook_job = self.webhook_queue().clone().job(webhook_payload); + webhook_job.options.id = format!( + "{}_{}_webhook", + job.job.transaction_id(), + envelope.notification_id + ); + + tx.queue_job(webhook_job)?; + + tracing::info!( + transaction_id = job.job.transaction_id(), + executor = Self::executor_name(), + stage = stage_override, + event = ?event_type, + notification_id = envelope.notification_id, + "Queued webhook notification with custom payload" + ); + } + + Ok(()) + } + fn queue_nack_webhook( &self, job: &BorrowedJob, diff --git a/server/Cargo.toml b/server/Cargo.toml index 002c8f7..648ebd9 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -5,10 +5,10 @@ edition = "2024" [dependencies] alloy = { workspace = true, features = ["serde"] } -axum = { version = "0.8.4", features = ["macros"] } -config = "0.15.11" -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.140" +axum = { workspace = true, features = ["macros"] } +config = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } vault-sdk = { workspace = true } vault-types = { workspace = true } engine-core = { path = "../core" } @@ -16,33 +16,33 @@ engine-aa-core = { path = "../aa-core" } engine-executors = { path = "../executors" } twmq = { path = "../twmq" } thirdweb-core = { path = "../thirdweb-core" } -tokio = { version = "1.44.2", features = ["full"] } -tower-http = { version = "0.6.2", features = ["cors", "trace"] } -tracing = "0.1.41" -anyhow = "1.0.98" -tracing-subscriber = { version = "0.3.19", features = ["env-filter", "json"] } -rand = "0.9.1" -futures = "0.3.31" -serde-bool = "0.1.3" -aide = { version = "0.14.2", features = [ +tokio = { workspace = true, features = ["full"] } +tower-http = { workspace = true, features = ["cors", "trace"] } +tracing = { workspace = true } +anyhow = { workspace = true } +tracing-subscriber = { workspace = true, features = ["env-filter", "json"] } +rand = { workspace = true } +futures = { workspace = true } +serde-bool = { workspace = true } +aide = { workspace = true, features = [ "axum", "axum-json", "macros", "scalar", ] } -schemars = "0.8.22" -utoipa = { version = "5.4.0", features = [ +schemars = { workspace = true } +utoipa = { workspace = true, features = [ "macros", "chrono", "uuid", "axum_extras", "preserve_order", ] } -utoipa-axum = "0.2.0" -utoipa-scalar = { version = "0.3.0", features = ["axum"] } -serde_with = "3.14.0" -aws-arn = "0.3.1" -moka = { version = "0.12.10", features = ["future"] } +utoipa-axum = { workspace = true } +utoipa-scalar = { workspace = true, features = ["axum"] } +serde_with = { workspace = true } +aws-arn = { workspace = true } +moka = { workspace = true } engine-eip7702-core = { path = "../eip7702-core" } -prometheus = "0.13.4" -thiserror = "2.0.12" \ No newline at end of file +prometheus = { workspace = true } +thiserror = { workspace = true } \ No newline at end of file diff --git a/server/configuration/server_base.yaml b/server/configuration/server_base.yaml index d16ca41..7006d1a 100644 --- a/server/configuration/server_base.yaml +++ b/server/configuration/server_base.yaml @@ -16,11 +16,20 @@ thirdweb: redis: url: redis://localhost:6379 +solana: + devnet: + http_url: https://api.devnet.solana.com + ws_url: wss://api.devnet.solana.com + mainnet: + http_url: https://api.mainnet-beta.solana.com + ws_url: wss://api.mainnet-beta.solana.com + queue: webhook_workers: 100 external_bundler_send_workers: 100 userop_confirm_workers: 100 eoa_executor_workers: 100 + solana_executor_workers: 100 local_concurrency: 100 polling_interval_ms: 100 lease_duration_seconds: 600 diff --git a/server/src/config.rs b/server/src/config.rs index 813f01a..50584e8 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -9,6 +9,28 @@ pub struct EngineConfig { pub thirdweb: ThirdwebConfig, pub queue: QueueConfig, pub redis: RedisConfig, + pub solana: SolanaConfig, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct SolanaConfig { + pub devnet: SolanRpcConfigData, + pub mainnet: SolanRpcConfigData, + #[serde(default = "default_local_rpc_config")] + pub local: SolanRpcConfigData, +} + +fn default_local_rpc_config() -> SolanRpcConfigData { + SolanRpcConfigData { + http_url: "http://127.0.0.1:8899".to_string(), + ws_url: "ws://127.0.0.1:8900".to_string(), + } +} + +#[derive(Debug, Clone, Deserialize)] +pub struct SolanRpcConfigData { + pub http_url: String, + pub ws_url: String, } #[derive(Debug, Clone, Deserialize)] @@ -18,13 +40,14 @@ pub struct QueueConfig { pub external_bundler_send_workers: usize, pub userop_confirm_workers: usize, pub eoa_executor_workers: usize, + pub solana_executor_workers: usize, pub execution_namespace: Option, pub local_concurrency: usize, pub polling_interval_ms: u64, pub lease_duration_seconds: u64, - + #[serde(default)] pub monitoring: MonitoringConfig, } @@ -42,7 +65,7 @@ impl Default for MonitoringConfig { Self { eoa_send_degradation_threshold_seconds: 10, // 10 seconds eoa_confirmation_degradation_threshold_seconds: 120, // 2 minutes - eoa_stuck_threshold_seconds: 600, // 10 minutes + eoa_stuck_threshold_seconds: 600, // 10 minutes } } } diff --git a/server/src/execution_router/mod.rs b/server/src/execution_router/mod.rs index 7e070bc..244039a 100644 --- a/server/src/execution_router/mod.rs +++ b/server/src/execution_router/mod.rs @@ -27,6 +27,7 @@ use engine_executors::{ confirm::UserOpConfirmationHandler, send::{ExternalBundlerSendHandler, ExternalBundlerSendJobData}, }, + solana_executor::worker::{SolanaExecutorJobData, SolanaExecutorJobHandler}, transaction_registry::TransactionRegistry, webhook::WebhookJobHandler, }; @@ -49,6 +50,7 @@ pub struct ExecutionRouter { pub eoa_executor_queue: Arc>>, pub eip7702_send_queue: Arc>>, pub eip7702_confirm_queue: Arc>>, + pub solana_executor_queue: Arc>, pub transaction_registry: Arc, pub vault_client: Arc, pub chains: Arc, @@ -499,4 +501,61 @@ impl ExecutionRouter { Ok(()) } + + pub async fn execute_solana( + &self, + request: engine_core::execution_options::solana::SendSolanaTransactionRequest, + signing_credential: SigningCredential, + ) -> Result { + use engine_core::execution_options::solana::{QueuedSolanaTransactionResponse, SolanaTransactionOptions}; + + let transaction_id = request.idempotency_key.clone(); + let chain_id = request.execution_options.chain_id; + let signer_address = request.execution_options.signer_address; + + let transaction = SolanaTransactionOptions { + instructions: request.instructions, + execution_options: request.execution_options, + }; + + let job_data = SolanaExecutorJobData { + transaction_id: transaction_id.clone(), + transaction, + signing_credential, + webhook_options: request.webhook_options, + }; + + // Register transaction in registry first + self.transaction_registry + .set_transaction_queue(&transaction_id, "solana_executor") + .await + .map_err(|e| EngineError::InternalError { + message: format!("Failed to register transaction: {e}"), + })?; + + // Create job with transaction ID as the job ID for idempotency + self.solana_executor_queue + .clone() + .job(job_data) + .with_id(&transaction_id) + .push() + .await + .map_err(|e| EngineError::InternalError { + message: format!("Failed to queue Solana transaction: {e}"), + })?; + + tracing::debug!( + transaction_id = %transaction_id, + chain_id = %chain_id.as_str(), + signer = %signer_address, + queue = "solana_executor", + "Solana job queued successfully" + ); + + Ok(QueuedSolanaTransactionResponse { + transaction_id, + chain_id, + signer_address: signer_address.to_string(), + }) + } } diff --git a/server/src/http/error.rs b/server/src/http/error.rs index edaa18b..f8ceb3e 100644 --- a/server/src/http/error.rs +++ b/server/src/http/error.rs @@ -1,5 +1,5 @@ use axum::{Json, http::StatusCode, response::IntoResponse}; -use engine_core::error::{ContractInteractionErrorKind, EngineError, RpcErrorKind}; +use engine_core::error::{ContractInteractionErrorKind, EngineError, RpcErrorKind, SolanaRpcErrorKind}; use serde_json::json; // Extension trait that lets you pair an error with a status code @@ -84,6 +84,19 @@ impl ApiEngineError { EngineError::InternalError { .. } => StatusCode::INTERNAL_SERVER_ERROR, EngineError::ThirdwebError { .. } => StatusCode::INTERNAL_SERVER_ERROR, EngineError::AwsKmsSignerError { .. } => StatusCode::BAD_GATEWAY, + EngineError::SolanaRpcError { kind, .. } => match kind { + SolanaRpcErrorKind::Io { .. } => StatusCode::BAD_GATEWAY, + SolanaRpcErrorKind::Reqwest { status, .. } => { + status.map(|s| StatusCode::from_u16(s).unwrap_or(StatusCode::BAD_GATEWAY)) + .unwrap_or(StatusCode::BAD_GATEWAY) + } + SolanaRpcErrorKind::RpcError { .. } => StatusCode::BAD_GATEWAY, + SolanaRpcErrorKind::SerdeJson { .. } => StatusCode::BAD_GATEWAY, + SolanaRpcErrorKind::TransactionError { .. } => StatusCode::BAD_REQUEST, + SolanaRpcErrorKind::SigningError { .. } => StatusCode::INTERNAL_SERVER_ERROR, + SolanaRpcErrorKind::Custom { .. } => StatusCode::BAD_GATEWAY, + SolanaRpcErrorKind::Unknown { .. } => StatusCode::SERVICE_UNAVAILABLE, + }, } } } diff --git a/server/src/http/routes/mod.rs b/server/src/http/routes/mod.rs index 9f84ca8..697ad06 100644 --- a/server/src/http/routes/mod.rs +++ b/server/src/http/routes/mod.rs @@ -5,5 +5,6 @@ pub mod contract_write; pub mod sign_message; pub mod sign_typed_data; +pub mod solana_transaction; pub mod transaction; pub mod transaction_write; diff --git a/server/src/http/routes/solana_transaction.rs b/server/src/http/routes/solana_transaction.rs new file mode 100644 index 0000000..f823945 --- /dev/null +++ b/server/src/http/routes/solana_transaction.rs @@ -0,0 +1,68 @@ +use axum::{ + debug_handler, + extract::State, + http::StatusCode, + response::{IntoResponse, Json}, +}; +use engine_core::execution_options::solana::{ + QueuedSolanaTransactionResponse, SendSolanaTransactionRequest, +}; + +use crate::http::{ + error::ApiEngineError, + extractors::{EngineJson, SigningCredentialsExtractor}, + server::EngineServerState, + types::SuccessResponse, +}; + +#[utoipa::path( + post, + operation_id = "sendSolanaTransaction", + path = "/solana/transaction", + tag = "Solana", + request_body(content = SendSolanaTransactionRequest, description = "Solana transaction request", content_type = "application/json"), + responses( + (status = 202, description = "Solana transaction queued successfully", body = SuccessResponse, content_type = "application/json"), + ), + params( + ("x-vault-access-token" = Option, Header, description = "Vault access token"), + ) +)] +/// Send Solana Transaction +/// +/// Execute a Solana transaction with custom instructions +#[debug_handler] +pub async fn send_solana_transaction( + State(state): State, + SigningCredentialsExtractor(signing_credential): SigningCredentialsExtractor, + EngineJson(request): EngineJson, +) -> Result { + let transaction_id = request.idempotency_key.clone(); + let chain_id = request.execution_options.chain_id; + let signer_address = request.execution_options.signer_address; + + tracing::info!( + transaction_id = %transaction_id, + chain_id = %chain_id.as_str(), + signer = %signer_address, + "Processing Solana transaction request" + ); + + let response = state + .execution_router + .execute_solana(request, signing_credential) + .await + .map_err(ApiEngineError)?; + + tracing::info!( + transaction_id = %transaction_id, + chain_id = %chain_id.as_str(), + "Solana transaction queued successfully" + ); + + Ok(( + StatusCode::ACCEPTED, + Json(SuccessResponse::new(response)), + )) +} + diff --git a/server/src/http/server.rs b/server/src/http/server.rs index 4598ec4..ac3b196 100644 --- a/server/src/http/server.rs +++ b/server/src/http/server.rs @@ -63,6 +63,9 @@ impl EngineServer { .routes(routes!( crate::http::routes::transaction_write::write_transaction )) + .routes(routes!( + crate::http::routes::solana_transaction::send_solana_transaction + )) .routes(routes!( crate::http::routes::transaction::cancel_transaction )) diff --git a/server/src/main.rs b/server/src/main.rs index 4b7f9be..42a22d2 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -74,6 +74,7 @@ async fn main() -> anyhow::Result<()> { let queue_manager = QueueManager::new( redis_client.clone(), &config.queue, + &config.solana, chains.clone(), signer.clone(), eoa_signer.clone(), @@ -104,6 +105,7 @@ async fn main() -> anyhow::Result<()> { eoa_executor_queue: queue_manager.eoa_executor_queue.clone(), eip7702_send_queue: queue_manager.eip7702_send_queue.clone(), eip7702_confirm_queue: queue_manager.eip7702_confirm_queue.clone(), + solana_executor_queue: queue_manager.solana_executor_queue.clone(), transaction_registry: queue_manager.transaction_registry.clone(), vault_client: Arc::new(vault_client.clone()), chains: chains.clone(), diff --git a/server/src/queue/manager.rs b/server/src/queue/manager.rs index e0435de..09447c8 100644 --- a/server/src/queue/manager.rs +++ b/server/src/queue/manager.rs @@ -2,8 +2,8 @@ use std::{sync::Arc, time::Duration}; use alloy::transports::http::reqwest; -use engine_core::error::EngineError; use engine_core::credentials::KmsClientCache; +use engine_core::error::EngineError; use engine_executors::{ eip7702_executor::{confirm::Eip7702ConfirmationHandler, send::Eip7702SendHandler}, eoa::{EoaExecutorJobHandler, authorization_cache::EoaAuthorizationCache}, @@ -12,6 +12,10 @@ use engine_executors::{ deployment::{RedisDeploymentCache, RedisDeploymentLock}, send::ExternalBundlerSendHandler, }, + solana_executor::{ + rpc_cache::SolanaRpcCache, storage::SolanaTransactionStorage, + worker::SolanaExecutorJobHandler, + }, transaction_registry::TransactionRegistry, webhook::{WebhookJobHandler, WebhookRetryConfig}, }; @@ -26,6 +30,7 @@ pub struct QueueManager { pub eoa_executor_queue: Arc>>, pub eip7702_send_queue: Arc>>, pub eip7702_confirm_queue: Arc>>, + pub solana_executor_queue: Arc>, pub transaction_registry: Arc, } @@ -47,6 +52,7 @@ impl QueueManager { pub async fn new( redis_client: twmq::redis::Client, queue_config: &QueueConfig, + solana_config: &crate::config::SolanaConfig, chain_service: Arc, userop_signer: Arc, eoa_signer: Arc, @@ -179,6 +185,11 @@ impl QueueManager { .await? .arc(); + let solana_signer = Arc::new(engine_core::signer::SolanaSigner::new( + userop_signer.vault_client.clone(), + userop_signer.iaw_client.clone(), + )); + // Create send queues let send_handler = ExternalBundlerSendHandler { chain_service: chain_service.clone(), @@ -261,6 +272,39 @@ impl QueueManager { .await? .arc(); + // Create Solana executor queue + let solana_executor_queue_name = + get_queue_name_for_namespace(&queue_config.execution_namespace, "solana_executor"); + let mut solana_executor_queue_opts = base_queue_opts.clone(); + solana_executor_queue_opts.local_concurrency = queue_config.solana_executor_workers; + + let solana_rpc_urls = engine_executors::solana_executor::rpc_cache::SolanaRpcUrls { + devnet: solana_config.devnet.http_url.clone(), + mainnet: solana_config.mainnet.http_url.clone(), + local: solana_config.local.http_url.clone(), + }; + let solana_rpc_cache = Arc::new(SolanaRpcCache::new(solana_rpc_urls)); + let solana_storage = SolanaTransactionStorage::new( + redis_client.get_connection_manager().await?, + queue_config.execution_namespace.clone(), + ); + let solana_executor_handler = SolanaExecutorJobHandler { + solana_signer, + rpc_cache: solana_rpc_cache, + storage: Arc::new(solana_storage), + webhook_queue: webhook_queue.clone(), + transaction_registry: transaction_registry.clone(), + }; + + let solana_executor_queue = Queue::builder() + .name(solana_executor_queue_name) + .options(solana_executor_queue_opts) + .handler(solana_executor_handler) + .redis_client(redis_client.clone()) + .build() + .await? + .arc(); + Ok(Self { webhook_queue, external_bundler_send_queue, @@ -268,6 +312,7 @@ impl QueueManager { eoa_executor_queue, eip7702_send_queue, eip7702_confirm_queue, + solana_executor_queue, transaction_registry, }) } @@ -300,14 +345,19 @@ impl QueueManager { tracing::info!("Starting EIP-7702 confirmation worker"); let eip7702_confirm_worker = self.eip7702_confirm_queue.work(); + // Start Solana executor workers + tracing::info!("Starting Solana executor worker"); + let solana_executor_worker = self.solana_executor_queue.work(); + tracing::info!( - "Started {} webhook workers, {} send workers, {} confirm workers, {} eoa workers, {} EIP-7702 send workers, {} EIP-7702 confirm workers", + "Started {} webhook workers, {} send workers, {} confirm workers, {} eoa workers, {} EIP-7702 send workers, {} EIP-7702 confirm workers, {} solana workers", queue_config.webhook_workers, queue_config.external_bundler_send_workers, queue_config.userop_confirm_workers, queue_config.eoa_executor_workers, queue_config.external_bundler_send_workers, // Reusing same config for now - queue_config.userop_confirm_workers // Reusing same config for now + queue_config.userop_confirm_workers, // Reusing same config for now + queue_config.solana_executor_workers, ); ShutdownHandle::with_worker(webhook_worker) @@ -316,6 +366,7 @@ impl QueueManager { .and_worker(eoa_executor_worker) .and_worker(eip7702_send_worker) .and_worker(eip7702_confirm_worker) + .and_worker(solana_executor_worker) } /// Get queue statistics for monitoring diff --git a/solana-core/Cargo.toml b/solana-core/Cargo.toml new file mode 100644 index 0000000..0ee9dee --- /dev/null +++ b/solana-core/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "engine-solana-core" +version = "0.1.0" +edition = "2024" + +[dependencies] +solana-sdk = { workspace = true } +solana-transaction-status = { workspace = true } +solana-compute-budget-interface = { workspace = true } +serde = { workspace = true } +serde_with = { workspace = true } +serde_json = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } +hex = { workspace = true } +base64 = { workspace = true } +utoipa = { workspace = true, features = [ + "macros", + "chrono", + "uuid", + "axum_extras", + "preserve_order", +] } diff --git a/solana-core/src/lib.rs b/solana-core/src/lib.rs new file mode 100644 index 0000000..cb167da --- /dev/null +++ b/solana-core/src/lib.rs @@ -0,0 +1,3 @@ +pub mod transaction; + +pub use transaction::{SolanaInstructionData, SolanaTransaction}; diff --git a/solana-core/src/transaction.rs b/solana-core/src/transaction.rs new file mode 100644 index 0000000..7ba67da --- /dev/null +++ b/solana-core/src/transaction.rs @@ -0,0 +1,195 @@ +use base64::{Engine, engine::general_purpose::STANDARD as Base64Engine}; +use serde::{Deserialize, Serialize}; +use serde_with::{DisplayFromStr, serde_as}; +use solana_compute_budget_interface::ComputeBudgetInstruction; +use solana_sdk::{ + instruction::Instruction, + message::{AccountMeta, VersionedMessage, v0}, + pubkey::Pubkey, + transaction::VersionedTransaction, +}; + +/// Solana instruction data provided by the user +/// This is a simplified representation that will be converted to a proper Instruction +#[serde_as] +#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct SolanaInstructionData { + /// Program ID to invoke + #[serde_as(as = "DisplayFromStr")] + #[schema(value_type = PubkeyDef)] + pub program_id: Pubkey, + + /// Account keys that will be passed to the program + pub accounts: Vec, + + /// Instruction data (hex-encoded or base64) + pub data: String, + pub encoding: InstructionDataEncoding, +} + +#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] +pub enum InstructionDataEncoding { + #[serde(rename = "hex")] + Hex, + #[serde(rename = "base64")] + Base64, +} + +#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] +pub struct PubkeyDef(pub String); + +/// Account metadata for Solana instructions +#[serde_as] +#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct SolanaAccountMeta { + /// Public key of the account + #[serde_as(as = "DisplayFromStr")] + #[schema(value_type = PubkeyDef)] + pub pubkey: Pubkey, + + /// Whether the account should sign the transaction + pub is_signer: bool, + + /// Whether the account is writable + pub is_writable: bool, +} + +impl SolanaAccountMeta { + pub fn to_account_meta(&self) -> AccountMeta { + match self.is_writable { + true => AccountMeta::new(self.pubkey, self.is_signer), + false => AccountMeta::new_readonly(self.pubkey, self.is_signer), + } + } +} + +/// Complete resolved Solana transaction request +#[serde_as] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SolanaTransaction { + /// List of instructions to execute in this transaction + pub instructions: Vec, + + /// Optional recent blockhash (if not provided, will be fetched) + #[serde_as(as = "DisplayFromStr")] + pub recent_blockhash: solana_sdk::hash::Hash, + + /// Compute budget limit (compute units) + /// If not provided, the transaction will use default compute budget + #[serde(skip_serializing_if = "Option::is_none")] + pub compute_unit_limit: Option, + + /// Compute budget price (micro-lamports per compute unit) + /// This is the priority fee - higher values increase transaction priority + #[serde(skip_serializing_if = "Option::is_none")] + pub compute_unit_price: Option, +} + +impl SolanaInstructionData { + pub fn get_data_bytes(&self) -> Result, SolanaTransactionError> { + match self.encoding { + // try to decode both prefixed and non-prefixed hex + InstructionDataEncoding::Hex => { + let s = self + .data + .strip_prefix("0x") + .or_else(|| self.data.strip_prefix("0X")) + .unwrap_or(self.data.as_str()); + hex::decode(s).map_err(|e| SolanaTransactionError::InvalidData { + error: e.to_string(), + }) + } + InstructionDataEncoding::Base64 => { + Ok(Base64Engine.decode(&self.data).map_err(|e| { + SolanaTransactionError::InvalidData { + error: e.to_string(), + } + })?) + } + } + } + + pub fn to_instruction(&self) -> Result { + let data_bytes = self.get_data_bytes()?; + Ok(Instruction { + program_id: self.program_id, + accounts: self + .accounts + .iter() + .map(|acc| acc.to_account_meta()) + .collect(), + data: data_bytes, + }) + } +} + +impl SolanaTransaction { + /// Build a VersionedTransaction from this transaction data + pub fn to_versioned_transaction( + &self, + payer: Pubkey, + recent_blockhash: solana_sdk::hash::Hash, + ) -> Result { + let mut instructions: Vec = Vec::new(); + + // Add compute budget instructions if specified + if let Some(limit) = self.compute_unit_limit { + let compute_limit_ix = ComputeBudgetInstruction::set_compute_unit_limit(limit); + instructions.push(compute_limit_ix); + } + + if let Some(price) = self.compute_unit_price { + let compute_price_ix = ComputeBudgetInstruction::set_compute_unit_price(price); + instructions.push(compute_price_ix); + } + + for inst in self.instructions.iter() { + instructions.push(inst.to_instruction()?); + } + + // Build message based on whether we have lookup tables + // Use legacy v0 message without lookup tables + let message = v0::Message::try_compile( + &payer, + &instructions, + &[], // No address lookup tables + recent_blockhash, + ) + .map_err(|e| SolanaTransactionError::MessageCompilationFailed { + error: e.to_string(), + })?; + + let message = VersionedMessage::V0(message); + + let num_signatures = message.header().num_required_signatures as usize; + let signatures = vec![solana_sdk::signature::Signature::default(); num_signatures]; + + // Create unsigned transaction + Ok(VersionedTransaction { + signatures, + message, + }) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum SolanaTransactionError { + #[error("Invalid pubkey for {field}: {value} - {error}")] + InvalidPubkey { + field: String, + value: String, + error: String, + }, + + #[error("Invalid instruction data: {error}")] + InvalidData { error: String }, + + #[error("Failed to compile message: {error}")] + MessageCompilationFailed { error: String }, + + #[error("Invalid blockhash: {error}")] + InvalidBlockhash { error: String }, +} diff --git a/thirdweb-core/Cargo.toml b/thirdweb-core/Cargo.toml index cb12a75..014637b 100644 --- a/thirdweb-core/Cargo.toml +++ b/thirdweb-core/Cargo.toml @@ -12,12 +12,12 @@ alloy = { workspace = true, features = [ "eip712", ] } engine-aa-types = { path = "../aa-types" } -moka = { version = "0.12.10", features = ["future"] } -reqwest = "0.12.18" -schemars = "0.8.22" -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.140" -thiserror = "2.0.12" -tracing = "0.1.41" -url = "2.5.4" -utoipa = "5.4.0" +moka = { workspace = true } +reqwest = { workspace = true } +schemars = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } +url = { workspace = true } +utoipa = { workspace = true } diff --git a/twmq/Cargo.toml b/twmq/Cargo.toml index 8aa87d5..9d40caa 100644 --- a/twmq/Cargo.toml +++ b/twmq/Cargo.toml @@ -4,20 +4,20 @@ version = "0.1.0" edition = "2024" [dependencies] -redis = { version = "0.31.0", features = ["tokio-comp", "connection-manager"] } -utoipa = "5.4.0" -nanoid = "0.4.0" -serde = { version = "1.0.219", features = ["derive"] } -serde_json = "1.0.140" -tokio = { version = "1.45.0", features = ["macros"] } -thiserror = "2.0.12" -tracing = "0.1.41" -tracing-subscriber = { version = "0.3.19", features = ["env-filter", "fmt"] } -futures = "0.3.31" +redis = { workspace = true } +utoipa = { workspace = true } +nanoid = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +tokio = { workspace = true, features = ["macros"] } +thiserror = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } +futures = { workspace = true } [dev-dependencies] -tokio = { version = "1.45.0", features = ["full"] } -criterion = { version = "0.6", features = ["html_reports", "async_tokio"] } +tokio = { workspace = true, features = ["full"] } +criterion = { workspace = true } rand = "0.8" [[bench]] diff --git a/twmq/src/job.rs b/twmq/src/job.rs index 42c5b66..d137eb1 100644 --- a/twmq/src/job.rs +++ b/twmq/src/job.rs @@ -184,8 +184,11 @@ pub struct Job { pub id: String, pub data: T, pub attempts: u32, + /// Timestamp in seconds pub created_at: u64, + /// Timestamp in seconds pub processed_at: Option, + /// Timestamp in seconds pub finished_at: Option, }