diff --git a/CHANGELOG.md b/CHANGELOG.md index 431385dc08..ead5ae75a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ The minor version will be incremented upon a breaking change and the patch versi - lang: Add missing pubkey const generation ([#3677](https://github.com/solana-foundation/anchor/pull/3677)). - cli: Add the Minimum Supported Rust Version (MSRV) to the Rust template, since an arbitrary compiler version isn't supported ([#3873](https://github.com/solana-foundation/anchor/pull/3873)). - cli: Add `hooks` section to `Anchor.toml` ([#3862](https://github.com/solana-foundation/anchor/pull/3862)). +- cli: Add `anchor program *` commands to replace `anchor deploy` and `anchor upgrade` & introduce new commands as well. ([#3963](https://github.com/solana-foundation/anchor/pull/3963)). ### Fixes diff --git a/Cargo.lock b/Cargo.lock index 3f5b5b9233..c2b63c36aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,6 +12,15 @@ dependencies = [ "regex", ] +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -56,16 +65,38 @@ dependencies = [ [[package]] name = "agave-feature-set" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a2c365c0245cbb8959de725fc2b44c754b673fdf34c9a7f9d4a25c35a7bf1" +checksum = "81071c030078429f000741da9ea84e34c432614f1b64dba741e1a572beeece3b" dependencies = [ "ahash", "solana-epoch-schedule 2.2.1", - "solana-hash 2.3.0", - "solana-pubkey 2.4.0", + "solana-hash 2.2.1", + "solana-pubkey 2.2.1", "solana-sha256-hasher 2.3.0", - "solana-svm-feature-set", +] + +[[package]] +name = "agave-precompiles" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6420f308338bae6455ef30532aba71aefbe9ec6cb69ce9db7d0801e37d2998fd" +dependencies = [ + "agave-feature-set", + "bincode", + "digest 0.10.7", + "ed25519-dalek 1.0.1", + "lazy_static", + "libsecp256k1", + "openssl", + "sha3", + "solana-ed25519-program", + "solana-message 2.2.1", + "solana-precompile-error", + "solana-pubkey 2.2.1", + "solana-sdk-ids 2.2.1", + "solana-secp256k1-program", + "solana-secp256r1-program", ] [[package]] @@ -183,24 +214,35 @@ dependencies = [ "chrono", "clap 4.5.17", "clap_complete", + "console", "dirs", + "ed25519-dalek 2.2.0", "flate2", "heck 0.4.1", "pathdiff", "portpicker", "regex", - "reqwest 0.11.27", + "reqwest", + "rustls-webpki 0.102.8", "semver", "serde", "serde_json", "shellexpand", "solana-cli-config", + "solana-client", "solana-faucet", + "solana-loader-v3-interface 5.0.0", + "solana-packet", + "solana-pubsub-client", "solana-rpc-client", + "solana-rpc-client-api", "solana-sdk", + "solana-sdk-ids 2.2.1", "solana-signer", "syn 1.0.109", "tar", + "tempfile", + "tiny-bip39 2.0.0", "toml 0.7.8", "walkdir", ] @@ -278,7 +320,7 @@ dependencies = [ "solana-cpi 2.2.1", "solana-define-syscall 2.3.0", "solana-feature-gate-interface", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-instructions-sysvar 2.2.2", "solana-invoke", "solana-loader-v3-interface 3.0.0", @@ -288,10 +330,10 @@ dependencies = [ "solana-program-memory 2.3.1", "solana-program-option 2.2.1", "solana-program-pack 2.2.1", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", "solana-system-interface 1.0.0", - "solana-sysvar 2.3.0", + "solana-sysvar 2.2.2", "solana-sysvar-id 2.2.1", "thiserror 1.0.66", ] @@ -327,10 +369,10 @@ dependencies = [ "spl-associated-token-account", "spl-memo", "spl-pod", - "spl-token", - "spl-token-2022", - "spl-token-group-interface", - "spl-token-metadata-interface", + "spl-token 8.0.0", + "spl-token-2022 8.0.1", + "spl-token-group-interface 0.6.0", + "spl-token-metadata-interface 0.7.0", ] [[package]] @@ -350,6 +392,12 @@ dependencies = [ "thiserror 1.0.66", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -419,9 +467,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "ark-bn254" @@ -464,7 +512,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools 0.10.5", - "num-bigint", + "num-bigint 0.4.6", "num-traits", "paste", "rustc_version", @@ -487,7 +535,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", @@ -516,7 +564,7 @@ dependencies = [ "ark-serialize-derive", "ark-std", "digest 0.10.7", - "num-bigint", + "num-bigint 0.4.6", ] [[package]] @@ -552,6 +600,62 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[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.66", + "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.3" @@ -567,21 +671,26 @@ dependencies = [ ] [[package]] -name = "async-trait" -version = "0.1.89" +name = "async-lock" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", + "event-listener 5.4.1", + "event-listener-strategy", + "pin-project-lite", ] [[package]] -name = "atomic-waker" -version = "1.1.2" +name = "async-trait" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] [[package]] name = "atty" @@ -589,7 +698,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -611,12 +720,27 @@ dependencies = [ "clap 4.5.17", "clap_complete", "dirs", - "reqwest 0.11.27", + "reqwest", "semver", "serde", "tempfile", ] +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base16ct" version = "0.2.0" @@ -629,6 +753,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +[[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" @@ -664,11 +794,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" dependencies = [ - "serde_core", + "serde", ] [[package]] @@ -746,7 +876,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -819,22 +949,22 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.24.0" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.2" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -845,9 +975,19 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "caps" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b" +dependencies = [ + "libc", + "thiserror 1.0.66", +] [[package]] name = "cargo_toml" @@ -870,6 +1010,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -890,20 +1036,22 @@ checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -971,7 +1119,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -986,6 +1134,38 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console" version = "0.15.11" @@ -995,7 +1175,7 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.2", + "unicode-width 0.2.1", "windows-sys 0.59.0", ] @@ -1084,6 +1264,25 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.19" @@ -1177,14 +1376,14 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] name = "darling" -version = "0.21.3" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -1192,27 +1391,40 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.21.3" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", - "syn 2.0.107", + "strsim 0.10.0", + "syn 2.0.104", ] [[package]] name = "darling_macro" -version = "0.21.3" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.107", + "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]] @@ -1231,6 +1443,29 @@ 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.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivation-path" version = "0.2.0" @@ -1330,9 +1565,38 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "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 = "eager" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe71d579d1812060163dff96056261deb5bf6729b100fa2e36a68b9649ba3d3" + [[package]] name = "ecdsa" version = "0.16.9" @@ -1356,6 +1620,16 @@ dependencies = [ "signature 1.6.4", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature 2.2.0", +] + [[package]] name = "ed25519-dalek" version = "1.0.1" @@ -1363,13 +1637,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek 3.2.0", - "ed25519", + "ed25519 1.5.3", "rand 0.7.3", "serde", "sha2 0.9.9", "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "ed25519-dalek-bip32" version = "0.2.0" @@ -1377,7 +1665,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" dependencies = [ "derivation-path", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "hmac 0.12.1", "sha2 0.10.8", ] @@ -1422,6 +1710,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-iterator" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd242f399be1da0a5354aa462d57b4ab2b4ee0683cc552f7c007d2d12d36e94" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03cdc46ec28bd728e67540c528013c6a10eb69a02eb31078a1bda695438cbfb8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "env_logger" version = "0.9.3" @@ -1462,6 +1770,33 @@ dependencies = [ "libc", ] +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.1", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.0.1" @@ -1622,7 +1957,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -1637,6 +1972,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" @@ -1703,28 +2044,54 @@ dependencies = [ ] [[package]] -name = "group" -version = "0.13.0" +name = "gimli" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] -name = "h2" -version = "0.3.21" +name = "governor" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" +dependencies = [ + "cfg-if", + "dashmap", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http 0.2.12", + "http", "indexmap 1.9.3", "slab", "tokio", @@ -1732,6 +2099,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1747,6 +2123,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.0" @@ -1783,6 +2165,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hidapi" version = "2.6.3" @@ -1796,6 +2184,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "histogram" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cb882ccb290b8646e554b157ab0b71e64e8d5bef775cd66b6531e52d302669" + [[package]] name = "hmac" version = "0.8.1" @@ -1828,20 +2222,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http" -version = "1.3.1" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -1855,38 +2238,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", - "http 0.2.12", - "pin-project-lite", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http 1.3.1", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http 1.3.1", - "http-body 1.0.1", + "http", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.10.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -1911,8 +2271,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 0.2.12", - "http-body 0.4.5", + "http", + "http-body", "httparse", "httpdate", "itoa", @@ -1924,27 +2284,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "http 1.3.1", - "http-body 1.0.1", - "httparse", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - [[package]] name = "hyper-rustls" version = "0.24.1" @@ -1952,52 +2291,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ "futures-util", - "http 0.2.12", - "hyper 0.14.27", + "http", + "hyper", "rustls 0.21.12", "tokio", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http 1.3.1", - "hyper 1.7.0", - "hyper-util", - "rustls 0.23.23", - "rustls-pki-types", - "tokio", - "tokio-rustls 0.26.2", - "tower-service", - "webpki-roots 1.0.3", -] - -[[package]] -name = "hyper-util" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "hyper 1.7.0", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2 0.6.1", - "tokio", - "tower-service", - "tracing", + "tokio-rustls", ] [[package]] @@ -2117,9 +2415,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.1.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ "idna_adapter", "smallvec", @@ -2148,9 +2446,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown 0.15.0", @@ -2165,7 +2463,7 @@ dependencies = [ "console", "number_prefix", "portable-atomic", - "unicode-width 0.2.2", + "unicode-width 0.2.1", "web-time", ] @@ -2179,20 +2477,21 @@ dependencies = [ ] [[package]] -name = "ipnet" -version = "2.11.0" +name = "io-uring" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] [[package]] -name = "iri-string" -version = "0.7.8" +name = "ipnet" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" -dependencies = [ - "memchr", - "serde", -] +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is_terminal_polyfill" @@ -2224,6 +2523,26 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine 4.6.7", + "jni-sys", + "log", + "thiserror 1.0.66", + "walkdir", +] + +[[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.32" @@ -2235,9 +2554,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -2272,16 +2591,6 @@ dependencies = [ "signature 2.2.0", ] -[[package]] -name = "kaigan" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba15de5aeb137f0f65aa3bf82187647f1285abfe5b20c80c2c37f7007ad519a" -dependencies = [ - "borsh 0.10.3", - "serde", -] - [[package]] name = "keccak" version = "0.1.4" @@ -2305,9 +2614,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libsecp256k1" @@ -2381,9 +2690,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" @@ -2437,6 +2746,12 @@ dependencies = [ "unicase", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -2448,13 +2763,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2470,6 +2785,66 @@ dependencies = [ "thiserror 1.0.66", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +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 = "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" @@ -2480,6 +2855,22 @@ 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.3.3" @@ -2499,7 +2890,7 @@ checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -2511,6 +2902,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +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" @@ -2520,6 +2934,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi 0.5.2", + "libc", +] + [[package]] name = "num_enum" version = "0.7.3" @@ -2538,7 +2962,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -2547,6 +2971,24 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +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.20.2" @@ -2561,11 +3003,11 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.74" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "cfg-if", "foreign-types", "libc", @@ -2582,21 +3024,43 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] -name = "openssl-sys" -version = "0.9.110" +name = "openssl-probe" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-src" +version = "300.5.2+3.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4" dependencies = [ "cc", - "libc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "openssl-src", "pkg-config", "vcpkg", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.1" @@ -2650,12 +3114,40 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac 0.12.1", +] + +[[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 = "percent-encoding" 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 = "pin-project-lite" version = "0.2.13" @@ -2713,13 +3205,19 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" dependencies = [ "zerovec", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2762,53 +3260,72 @@ 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.0+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", - "rustls 0.23.23", - "socket2 0.5.7", - "thiserror 1.0.66", + "rustc-hash 2.1.1", + "rustls 0.23.32", + "socket2 0.5.10", + "thiserror 2.0.14", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom 0.2.10", "rand 0.8.5", "ring 0.17.8", - "rustc-hash 2.0.0", - "rustls 0.23.23", + "rustc-hash 2.1.1", + "rustls 0.23.32", + "rustls-pki-types", + "rustls-platform-verifier", "slab", - "thiserror 1.0.66", + "thiserror 2.0.14", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.6" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e346e016eacfff12233c243718197ca12f148c84e1e84268a896699b41c71780" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.7", + "socket2 0.6.0", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2891,6 +3408,35 @@ dependencies = [ "rand_core 0.5.1", ] +[[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.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -2955,16 +3501,17 @@ version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ + "async-compression", "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", "h2", - "http 0.2.12", - "http-body 0.4.5", - "hyper 0.14.27", - "hyper-rustls 0.24.1", + "http", + "http-body", + "hyper", + "hyper-rustls", "ipnet", "js-sys", "log", @@ -2974,14 +3521,15 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.12", - "rustls-pemfile", + "rustls-pemfile 1.0.3", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", + "sync_wrapper", "system-configuration", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls", + "tokio-util", "tower-service", "url", "wasm-bindgen", @@ -2991,61 +3539,19 @@ dependencies = [ "winreg", ] -[[package]] -name = "reqwest" -version = "0.12.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" -dependencies = [ - "async-compression", - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "http-body-util", - "hyper 1.7.0", - "hyper-rustls 0.27.7", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls 0.23.23", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 1.0.2", - "tokio", - "tokio-rustls 0.26.2", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 1.0.3", -] - [[package]] name = "reqwest-middleware" -version = "0.4.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57f17d28a6e6acfe1733fe24bcd30774d13bffa4b8a22535b4c8c98423088d4e" +checksum = "5a735987236a8e238bf0296c7e351b999c188ccc11477f311b82b55c93984216" dependencies = [ "anyhow", "async-trait", - "http 1.3.1", - "reqwest 0.12.24", + "http", + "reqwest", "serde", + "task-local-extensions", "thiserror 1.0.66", - "tower-service", ] [[package]] @@ -3090,13 +3596,13 @@ dependencies = [ [[package]] name = "rpassword" -version = "7.4.0" +version = "7.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" dependencies = [ "libc", "rtoolbox", - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -3109,6 +3615,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -3117,9 +3629,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -3130,13 +3642,22 @@ dependencies = [ "semver", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "0.38.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", @@ -3157,18 +3678,31 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.23" +version = "0.23.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" dependencies = [ "once_cell", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.8", + "rustls-webpki 0.103.7", "subtle", "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.3" @@ -3178,11 +3712,51 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c7dc240fec5517e6c4eab3310438636cfe6391dfc345ba013109909a90d136" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls 0.23.32", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki 0.102.8", + "security-framework", + "security-framework-sys", + "webpki-root-certs 0.26.11", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" @@ -3205,6 +3779,17 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustls-webpki" +version = "0.103.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -3226,6 +3811,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -3256,19 +3850,42 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "num-bigint 0.4.6", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" -version = "1.0.27" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ - "serde_core", "serde_derive", ] @@ -3290,37 +3907,27 @@ dependencies = [ "serde", ] -[[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.228" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", "ryu", "serde", - "serde_core", ] [[package]] @@ -3346,24 +3953,25 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.15.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" dependencies = [ - "serde_core", + "serde", + "serde_derive", "serde_with_macros", ] [[package]] name = "serde_with_macros" -version = "3.15.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -3372,7 +3980,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.10.0", "itoa", "ryu", "serde", @@ -3519,9 +4127,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3529,12 +4137,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -3549,60 +4157,56 @@ dependencies = [ "serde_derive", "solana-account-info 2.3.0", "solana-clock 2.2.2", - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", - "solana-sysvar 2.3.0", + "solana-sysvar 2.2.2", ] [[package]] name = "solana-account-decoder" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba71c97fa4d85ce4a1e0e79044ad0406c419382be598c800202903a7688ce71a" +checksum = "d06df58936cc05b03429a2eba8ef2c4bb6c85fe21d2478ea059e7ef6abd0f5c6" dependencies = [ "Inflector", "base64 0.22.1", "bincode", "bs58", "bv", + "lazy_static", "serde", "serde_derive", "serde_json", "solana-account", "solana-account-decoder-client-types", - "solana-address-lookup-table-interface 2.2.2", "solana-clock 2.2.2", - "solana-config-program-client", + "solana-config-program", "solana-epoch-schedule 2.2.1", "solana-fee-calculator 2.2.1", - "solana-instruction 2.3.0", - "solana-loader-v3-interface 5.0.0", + "solana-instruction 2.2.1", "solana-nonce 2.2.1", - "solana-program-option 2.2.1", + "solana-program 2.2.1", "solana-program-pack 2.2.1", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-rent 2.2.1", "solana-sdk-ids 2.2.1", "solana-slot-hashes 2.2.1", "solana-slot-history 2.2.1", - "solana-stake-interface", - "solana-sysvar 2.3.0", - "solana-vote-interface", - "spl-generic-token", - "spl-token", - "spl-token-2022", - "spl-token-group-interface", - "spl-token-metadata-interface", - "thiserror 2.0.17", + "solana-sysvar 2.2.2", + "spl-token 7.0.0", + "spl-token-2022 7.0.0", + "spl-token-group-interface 0.5.0", + "spl-token-metadata-interface 0.6.0", + "thiserror 2.0.14", "zstd", ] [[package]] name = "solana-account-decoder-client-types" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5519e8343325b707f17fbed54fcefb325131b692506d0af9e08a539d15e4f8cf" +checksum = "ee049978c5bbbb5213e4f0e924a76d98d89c771220539bd411b392bf4e3d373a" dependencies = [ "base64 0.22.1", "bs58", @@ -3610,7 +4214,7 @@ dependencies = [ "serde_derive", "serde_json", "solana-account", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "zstd", ] @@ -3624,7 +4228,7 @@ dependencies = [ "serde", "solana-program-error 2.2.2", "solana-program-memory 2.3.1", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", ] [[package]] @@ -3657,7 +4261,7 @@ dependencies = [ "solana-atomic-u64 3.0.0", "solana-define-syscall 3.0.0", "solana-program-error 3.0.0", - "solana-sanitize 3.0.1", + "solana-sanitize 3.0.0", "solana-sha256-hasher 3.0.0", ] @@ -3672,8 +4276,8 @@ dependencies = [ "serde", "serde_derive", "solana-clock 2.2.2", - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", "solana-slot-hashes 2.2.1", ] @@ -3714,7 +4318,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75db7f2bbac3e62cfd139065d15bcda9e2428883ba61fc8d27ccb251081e7567" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "solana-define-syscall 2.3.0", ] @@ -3725,7 +4329,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30c80fb6d791b3925d5ec4bf23a7c169ef5090c013059ec3ed7d0b2c04efa085" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "solana-define-syscall 3.0.0", ] @@ -3738,7 +4342,7 @@ checksum = "19a3787b8cf9c9fe3dd360800e8b70982b9e5a8af9e11c354b6665dd4a003adc" dependencies = [ "bincode", "serde", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", ] [[package]] @@ -3749,7 +4353,7 @@ checksum = "a1a0801e25a1b31a14494fc80882a036be0ffd290efc4c2d640bfcca120a4672" dependencies = [ "blake3", "solana-define-syscall 2.3.0", - "solana-hash 2.3.0", + "solana-hash 2.2.1", "solana-sanitize 2.2.1", ] @@ -3776,7 +4380,7 @@ dependencies = [ "ark-serialize", "bytemuck", "solana-define-syscall 2.3.0", - "thiserror 2.0.17", + "thiserror 2.0.14", ] [[package]] @@ -3800,9 +4404,9 @@ dependencies = [ [[package]] name = "solana-clap-utils" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82129ae5aaddbc61a36b917d65ffd2c0cac32e0f12bc2ac0ae87634ef5891c05" +checksum = "65d599be9bb385d0299d781693992038be2cdfef436fd423526f2786e09a6654" dependencies = [ "chrono", "clap 2.34.0", @@ -3811,29 +4415,30 @@ dependencies = [ "solana-cluster-type", "solana-commitment-config", "solana-derivation-path", - "solana-hash 2.3.0", + "solana-hash 2.2.1", "solana-keypair", - "solana-message 2.4.0", - "solana-native-token 2.2.2", + "solana-message 2.2.1", + "solana-native-token 2.2.1", "solana-presigner", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-remote-wallet", "solana-seed-phrase", "solana-signature", "solana-signer", - "thiserror 2.0.17", - "tiny-bip39", + "thiserror 2.0.14", + "tiny-bip39 0.8.2", "uriparse", "url", ] [[package]] name = "solana-cli-config" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239f68bfb6aff6e8b24dc94e871a3cf41daac4ffd82d296e8ed8fb552b89a30e" +checksum = "e4b1c2a6082108ed6665306987253d6fd8cb1f4d1e5c5ea627676f53c63697aa" dependencies = [ "dirs-next", + "lazy_static", "serde", "serde_derive", "serde_yaml", @@ -3842,6 +4447,52 @@ dependencies = [ "url", ] +[[package]] +name = "solana-client" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dceb55e02d2aed1aaadfec71e87c005512a5904ab88349cfe3fe7e61ea404d" +dependencies = [ + "async-trait", + "bincode", + "dashmap", + "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 2.2.1", + "solana-instruction 2.2.1", + "solana-keypair", + "solana-measure", + "solana-message 2.2.1", + "solana-pubkey 2.2.1", + "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-thin-client", + "solana-time-utils", + "solana-tpu-client", + "solana-transaction", + "solana-transaction-error 2.2.1", + "solana-udp-client", + "thiserror 2.0.14", + "tokio", +] + [[package]] name = "solana-client-traits" version = "2.2.1" @@ -3851,11 +4502,11 @@ dependencies = [ "solana-account", "solana-commitment-config", "solana-epoch-info", - "solana-hash 2.3.0", - "solana-instruction 2.3.0", + "solana-hash 2.2.1", + "solana-instruction 2.2.1", "solana-keypair", - "solana-message 2.4.0", - "solana-pubkey 2.4.0", + "solana-message 2.2.1", + "solana-pubkey 2.2.1", "solana-signature", "solana-signer", "solana-system-interface 1.0.0", @@ -3897,7 +4548,7 @@ checksum = "7ace9fea2daa28354d107ea879cff107181d85cd4e0f78a2bedb10e1a428c97e" dependencies = [ "serde", "serde_derive", - "solana-hash 2.3.0", + "solana-hash 2.2.1", ] [[package]] @@ -3910,6 +4561,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "solana-compute-budget" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03fd2a55373ce9218600d8711177026f351e7bdcfca46552e3bef25c9f75c90c" +dependencies = [ + "solana-fee-structure", + "solana-program-entrypoint 2.3.0", +] + [[package]] name = "solana-compute-budget-interface" version = "2.2.2" @@ -3919,34 +4580,69 @@ dependencies = [ "borsh 1.5.7", "serde", "serde_derive", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-sdk-ids 2.2.1", ] [[package]] -name = "solana-config-program-client" -version = "0.0.2" +name = "solana-config-program" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53aceac36f105fd4922e29b4f0c1f785b69d7b3e7e387e384b8985c8e0c3595e" +checksum = "08601f0bcaed81e90d0ed2aaeee6b774cc05bfd5e14220ce03974f0687c6b77f" dependencies = [ "bincode", - "borsh 0.10.3", - "kaigan", + "chrono", "serde", - "solana-program 2.3.0", + "serde_derive", + "solana-account", + "solana-bincode", + "solana-instruction 2.2.1", + "solana-log-collector", + "solana-packet", + "solana-program-runtime", + "solana-pubkey 2.2.1", + "solana-sdk-ids 2.2.1", + "solana-short-vec 2.2.1", + "solana-stake-interface", + "solana-system-interface 1.0.0", + "solana-transaction-context", ] [[package]] -name = "solana-cpi" -version = "2.2.1" +name = "solana-connection-cache" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dc71126edddc2ba014622fc32d0f5e2e78ec6c5a1e0eb511b85618c09e9ea11" +checksum = "80f94fde6567c2ff6dbda9ab5e08d954ed459d925071e2df5755491d5121c6ee" dependencies = [ - "solana-account-info 2.3.0", + "async-trait", + "bincode", + "crossbeam-channel", + "futures-util", + "indexmap 2.10.0", + "log", + "rand 0.8.5", + "rayon", + "solana-keypair", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-time-utils", + "solana-transaction-error 2.2.1", + "thiserror 2.0.14", + "tokio", +] + +[[package]] +name = "solana-cpi" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc71126edddc2ba014622fc32d0f5e2e78ec6c5a1e0eb511b85618c09e9ea11" +dependencies = [ + "solana-account-info 2.3.0", "solana-define-syscall 2.3.0", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-stable-layout 2.2.1", ] @@ -3966,16 +4662,16 @@ dependencies = [ [[package]] name = "solana-curve25519" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae4261b9a8613d10e77ac831a8fa60b6fa52b9b103df46d641deff9f9812a23" +checksum = "411f8a1fcbc3781bdc6ee3457bb6275eab8a7bd05154f1e73ecfdbba508d1ea4" dependencies = [ "bytemuck", "bytemuck_derive", "curve25519-dalek 4.1.3", "solana-define-syscall 2.3.0", "subtle", - "thiserror 2.0.17", + "thiserror 2.0.14", ] [[package]] @@ -4018,9 +4714,9 @@ checksum = "a1feafa1691ea3ae588f99056f4bdd1293212c7ece28243d7da257c443e84753" dependencies = [ "bytemuck", "bytemuck_derive", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "solana-feature-set", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-precompile-error", "solana-sdk-ids 2.2.1", ] @@ -4043,7 +4739,7 @@ checksum = "86b575d3dd323b9ea10bb6fe89bf6bf93e249b215ba8ed7f68f1a3633f384db7" dependencies = [ "serde", "serde_derive", - "solana-hash 2.3.0", + "solana-hash 2.2.1", "solana-sdk-ids 2.2.1", "solana-sdk-macro 2.2.1", "solana-sysvar-id 2.2.1", @@ -4070,8 +4766,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c5fd2662ae7574810904585fd443545ed2b568dbd304b25a31e79ccc76e81b" dependencies = [ "siphasher", - "solana-hash 2.3.0", - "solana-pubkey 2.4.0", + "solana-hash 2.2.1", + "solana-pubkey 2.2.1", ] [[package]] @@ -4120,15 +4816,15 @@ dependencies = [ "serde_derive", "solana-address-lookup-table-interface 2.2.2", "solana-clock 2.2.2", - "solana-hash 2.3.0", - "solana-instruction 2.3.0", + "solana-hash 2.2.1", + "solana-instruction 2.2.1", "solana-keccak-hasher 2.2.1", - "solana-message 2.4.0", + "solana-message 2.2.1", "solana-nonce 2.2.1", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", "solana-system-interface 1.0.0", - "thiserror 2.0.17", + "thiserror 2.0.14", ] [[package]] @@ -4144,19 +4840,19 @@ dependencies = [ "solana-hash 3.0.0", "solana-instruction 3.0.0", "solana-keccak-hasher 3.0.0", - "solana-message 3.0.1", + "solana-message 3.0.0", "solana-nonce 3.0.0", "solana-pubkey 3.0.0", "solana-sdk-ids 3.0.0", "solana-system-interface 2.0.0", - "thiserror 2.0.17", + "thiserror 2.0.14", ] [[package]] name = "solana-faucet" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47dc1a523a416877264515915badd47d79fdf245e76e81bcea695f464e77c747" +checksum = "0b234a64a7aec617ee6af788242a1374d7d1797058dcdfd60c744d99313bbe95" dependencies = [ "bincode", "clap 2.34.0", @@ -4166,22 +4862,22 @@ dependencies = [ "serde_derive", "solana-clap-utils", "solana-cli-config", - "solana-hash 2.3.0", - "solana-instruction 2.3.0", + "solana-hash 2.2.1", + "solana-instruction 2.2.1", "solana-keypair", "solana-logger", - "solana-message 2.4.0", + "solana-message 2.2.1", "solana-metrics", - "solana-native-token 2.2.2", + "solana-native-token 2.2.1", "solana-packet", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-signer", "solana-system-interface 1.0.0", "solana-system-transaction", "solana-transaction", "solana-version", "spl-memo", - "thiserror 2.0.17", + "thiserror 2.0.14", "tokio", ] @@ -4196,9 +4892,9 @@ dependencies = [ "serde_derive", "solana-account", "solana-account-info 2.3.0", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-rent 2.2.1", "solana-sdk-ids 2.2.1", "solana-system-interface 1.0.0", @@ -4213,8 +4909,8 @@ dependencies = [ "ahash", "lazy_static", "solana-epoch-schedule 2.2.1", - "solana-hash 2.3.0", - "solana-pubkey 2.4.0", + "solana-hash 2.2.1", + "solana-pubkey 2.2.1", "solana-sha256-hasher 2.3.0", ] @@ -4248,8 +4944,8 @@ checksum = "33adf673581c38e810bf618f745bf31b683a0a4a4377682e6aaac5d9a058dd4e" dependencies = [ "serde", "serde_derive", - "solana-message 2.4.0", - "solana-native-token 2.2.2", + "solana-message 2.2.1", + "solana-native-token 2.2.1", ] [[package]] @@ -4268,12 +4964,12 @@ dependencies = [ "solana-cluster-type", "solana-epoch-schedule 2.2.1", "solana-fee-calculator 2.2.1", - "solana-hash 2.3.0", + "solana-hash 2.2.1", "solana-inflation", "solana-keypair", "solana-logger", "solana-poh-config", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-rent 2.2.1", "solana-sdk-ids 2.2.1", "solana-sha256-hasher 2.3.0", @@ -4294,14 +4990,14 @@ dependencies = [ [[package]] name = "solana-hash" -version = "2.3.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b96e9f0300fa287b545613f007dfe20043d7812bee255f418c1eb649c93b63" +checksum = "cf7bcb14392900fe02e4e34e90234fbf0c673d4e327888410ba99fa2ba0f4e99" dependencies = [ "borsh 1.5.7", + "bs58", "bytemuck", "bytemuck_derive", - "five8", "js-sys", "serde", "serde_derive", @@ -4323,7 +5019,7 @@ dependencies = [ "serde", "serde_derive", "solana-atomic-u64 3.0.0", - "solana-sanitize 3.0.1", + "solana-sanitize 3.0.0", ] [[package]] @@ -4336,11 +5032,21 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "solana-inline-spl" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14191c3b9de9f62e9ffb81ae53b3aecade34aebcb22d695e93fea70b1cec8cbe" +dependencies = [ + "bytemuck", + "solana-pubkey 2.2.1", +] + [[package]] name = "solana-instruction" -version = "2.3.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47298e2ce82876b64f71e9d13a46bc4b9056194e7f9937ad3084385befa50885" +checksum = "9ce496a475e5062ba5de97215ab39d9c358f9c9df4bb7f3a45a1f1a8bd9065ed" dependencies = [ "bincode", "borsh 1.5.7", @@ -4350,7 +5056,7 @@ dependencies = [ "serde", "serde_derive", "solana-define-syscall 2.3.0", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "wasm-bindgen", ] @@ -4385,11 +5091,11 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0e85a6fad5c2d0c4f5b91d34b8ca47118fc593af706e523cdbedf846a954f57" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "solana-account-info 2.3.0", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-sanitize 2.2.1", "solana-sdk-ids 2.2.1", "solana-serialize-utils 2.2.1", @@ -4402,15 +5108,15 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ddf67876c541aa1e21ee1acae35c95c6fbc61119814bfef70579317a5e26955" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.9.1", "solana-account-info 3.0.0", "solana-instruction 3.0.0", "solana-instruction-error", "solana-program-error 3.0.0", "solana-pubkey 3.0.0", - "solana-sanitize 3.0.1", + "solana-sanitize 3.0.0", "solana-sdk-ids 3.0.0", - "solana-serialize-utils 3.1.0", + "solana-serialize-utils 3.0.0", "solana-sysvar-id 3.0.0", ] @@ -4422,7 +5128,7 @@ checksum = "58f5693c6de226b3626658377168b0184e94e8292ff16e3d31d4766e65627565" dependencies = [ "solana-account-info 2.3.0", "solana-define-syscall 2.3.0", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-program-entrypoint 2.3.0", "solana-stable-layout 2.2.1", ] @@ -4435,7 +5141,7 @@ checksum = "c7aeb957fbd42a451b99235df4942d96db7ef678e8d5061ef34c9b34cae12f79" dependencies = [ "sha3", "solana-define-syscall 2.3.0", - "solana-hash 2.3.0", + "solana-hash 2.2.1", "solana-sanitize 2.2.1", ] @@ -4457,11 +5163,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dbb7042c2e0c561afa07242b2099d55c57bd1b1da3b6476932197d84e15e3e4" dependencies = [ "bs58", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "ed25519-dalek-bip32", "rand 0.7.3", "solana-derivation-path", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-seed-derivable", "solana-seed-phrase", "solana-signature", @@ -4504,8 +5210,8 @@ dependencies = [ "serde", "serde_bytes", "serde_derive", - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", ] @@ -4518,8 +5224,8 @@ dependencies = [ "serde", "serde_bytes", "serde_derive", - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", "solana-system-interface 1.0.0", ] @@ -4533,8 +5239,8 @@ dependencies = [ "serde", "serde_bytes", "serde_derive", - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", "solana-system-interface 1.0.0", ] @@ -4548,12 +5254,21 @@ dependencies = [ "serde", "serde_bytes", "serde_derive", - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", "solana-system-interface 1.0.0", ] +[[package]] +name = "solana-log-collector" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45eaf7f6fabf1e2b8fb3eb39e177ea8a1dcf63b1f4f97c5e27829d94c47a018" +dependencies = [ + "log", +] + [[package]] name = "solana-logger" version = "2.3.1" @@ -4567,11 +5282,17 @@ dependencies = [ "signal-hook", ] +[[package]] +name = "solana-measure" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb393cb30a8f55c91e981eafd392091bb960def07e76531aa37b47cb8a0b2c3c" + [[package]] name = "solana-message" -version = "2.4.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1796aabce376ff74bf89b78d268fa5e683d7d7a96a0a4e4813ec34de49d5314b" +checksum = "268486ba8a294ed22a4d7c1ec05f540c3dbe71cfa7c6c54b6d4d13668d895678" dependencies = [ "bincode", "blake3", @@ -4579,9 +5300,9 @@ dependencies = [ "serde", "serde_derive", "solana-bincode", - "solana-hash 2.3.0", - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-hash 2.2.1", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", "solana-sanitize 2.2.1", "solana-sdk-ids 2.2.1", "solana-short-vec 2.2.1", @@ -4592,17 +5313,17 @@ dependencies = [ [[package]] name = "solana-message" -version = "3.0.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85666605c9fd727f865ed381665db0a8fc29f984a030ecc1e40f43bfb2541623" +checksum = "2c33e9fa7871147ac3235a7320386afa2dc64bbb21ca3cf9d79a6f6827313176" dependencies = [ "lazy_static", "serde", "serde_derive", - "solana-address", "solana-hash 3.0.0", "solana-instruction 3.0.0", - "solana-sanitize 3.0.1", + "solana-pubkey 3.0.0", + "solana-sanitize 3.0.0", "solana-sdk-ids 3.0.0", "solana-short-vec 3.0.0", "solana-transaction-error 3.0.0", @@ -4610,18 +5331,20 @@ dependencies = [ [[package]] name = "solana-metrics" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0375159d8460f423d39e5103dcff6e07796a5ec1850ee1fcfacfd2482a8f34b5" +checksum = "bc52d31466c3ee771115bc8f8280ad669190c861acccc2a16fd7a8b268c6c24f" dependencies = [ "crossbeam-channel", "gethostname", + "lazy_static", "log", - "reqwest 0.12.24", + "reqwest", + "solana-clock 2.2.2", "solana-cluster-type", "solana-sha256-hasher 2.3.0", "solana-time-utils", - "thiserror 2.0.17", + "thiserror 2.0.14", ] [[package]] @@ -4644,9 +5367,9 @@ dependencies = [ [[package]] name = "solana-native-token" -version = "2.2.2" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307fb2f78060995979e9b4f68f833623565ed4e55d3725f100454ce78a99a1a3" +checksum = "33e9de00960197412e4be3902a6cd35e60817c511137aca6c34c66cd5d4017ec" [[package]] name = "solana-native-token" @@ -4654,6 +5377,28 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae8dd4c280dca9d046139eb5b7a5ac9ad10403fbd64964c7d7571214950d758f" +[[package]] +name = "solana-net-utils" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267dec3a40e0ed0613cfb31b3e7489a16c52495a3afe482b284868a98b3680c0" +dependencies = [ + "anyhow", + "bincode", + "bytes", + "crossbeam-channel", + "itertools 0.12.1", + "log", + "nix", + "rand 0.8.5", + "serde", + "serde_derive", + "socket2 0.5.10", + "solana-serde", + "tokio", + "url", +] + [[package]] name = "solana-nonce" version = "2.2.1" @@ -4663,8 +5408,8 @@ dependencies = [ "serde", "serde_derive", "solana-fee-calculator 2.2.1", - "solana-hash 2.3.0", - "solana-pubkey 2.4.0", + "solana-hash 2.2.1", + "solana-pubkey 2.2.1", "solana-sha256-hasher 2.3.0", ] @@ -4687,7 +5432,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde971a20b8dbf60144d6a84439dda86b5466e00e2843091fe731083cda614da" dependencies = [ "solana-account", - "solana-hash 2.3.0", + "solana-hash 2.2.1", "solana-nonce 2.2.1", "solana-sdk-ids 2.2.1", ] @@ -4699,9 +5444,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b526398ade5dea37f1f147ce55dae49aa017a5d7326606359b0445ca8d946581" dependencies = [ "num_enum", - "solana-hash 2.3.0", + "solana-hash 2.2.1", "solana-packet", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-sanitize 2.2.1", "solana-sha256-hasher 2.3.0", "solana-signature", @@ -4715,13 +5460,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "004f2d2daf407b3ec1a1ca5ec34b3ccdfd6866dd2d3c7d0715004a96e4b6d127" dependencies = [ "bincode", - "bitflags 2.10.0", + "bitflags 2.9.1", "cfg_eval", "serde", "serde_derive", "serde_with", ] +[[package]] +name = "solana-perf" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c4b8180165c7240f919305079b3d3de447b790290fcc623744e350210e73d7" +dependencies = [ + "ahash", + "bincode", + "bv", + "caps", + "curve25519-dalek 4.1.3", + "dlopen2", + "fnv", + "lazy_static", + "libc", + "log", + "nix", + "rand 0.8.5", + "rayon", + "serde", + "solana-hash 2.2.1", + "solana-message 2.2.1", + "solana-metrics", + "solana-packet", + "solana-pubkey 2.2.1", + "solana-rayon-threadlimit", + "solana-sdk-ids 2.2.1", + "solana-short-vec 2.2.1", + "solana-signature", + "solana-time-utils", +] + [[package]] name = "solana-poh-config" version = "2.2.1" @@ -4751,9 +5528,9 @@ dependencies = [ "lazy_static", "solana-ed25519-program", "solana-feature-set", - "solana-message 2.4.0", + "solana-message 2.2.1", "solana-precompile-error", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", "solana-secp256k1-program", "solana-secp256r1-program", @@ -4765,16 +5542,16 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81a57a24e6a4125fc69510b6774cd93402b943191b6cddad05de7281491c90fe" dependencies = [ - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-signature", "solana-signer", ] [[package]] name = "solana-program" -version = "2.3.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98eca145bd3545e2fbb07166e895370576e47a00a7d824e325390d33bf467210" +checksum = "586469467e93ceb79048f8d8e3a619bf61d05396ee7de95cb40280301a589d05" dependencies = [ "bincode", "blake3", @@ -4788,7 +5565,7 @@ dependencies = [ "lazy_static", "log", "memoffset", - "num-bigint", + "num-bigint 0.4.6", "num-derive 0.4.0", "num-traits", "rand 0.8.5", @@ -4811,24 +5588,24 @@ dependencies = [ "solana-example-mocks 2.2.1", "solana-feature-gate-interface", "solana-fee-calculator 2.2.1", - "solana-hash 2.3.0", - "solana-instruction 2.3.0", + "solana-hash 2.2.1", + "solana-instruction 2.2.1", "solana-instructions-sysvar 2.2.2", "solana-keccak-hasher 2.2.1", "solana-last-restart-slot 2.2.1", "solana-loader-v2-interface", - "solana-loader-v3-interface 5.0.0", + "solana-loader-v3-interface 3.0.0", "solana-loader-v4-interface", - "solana-message 2.4.0", + "solana-message 2.2.1", "solana-msg 2.2.1", - "solana-native-token 2.2.2", + "solana-native-token 2.2.1", "solana-nonce 2.2.1", "solana-program-entrypoint 2.3.0", "solana-program-error 2.2.2", "solana-program-memory 2.3.1", "solana-program-option 2.2.1", "solana-program-pack 2.2.1", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-rent 2.2.1", "solana-sanitize 2.2.1", "solana-sdk-ids 2.2.1", @@ -4843,10 +5620,10 @@ dependencies = [ "solana-stable-layout 2.2.1", "solana-stake-interface", "solana-system-interface 1.0.0", - "solana-sysvar 2.3.0", + "solana-sysvar 2.2.2", "solana-sysvar-id 2.2.1", "solana-vote-interface", - "thiserror 2.0.17", + "thiserror 2.0.14", "wasm-bindgen", ] @@ -4877,7 +5654,7 @@ dependencies = [ "solana-last-restart-slot 3.0.0", "solana-msg 3.0.0", "solana-native-token 3.0.0", - "solana-program-entrypoint 3.1.0", + "solana-program-entrypoint 3.0.0", "solana-program-error 3.0.0", "solana-program-memory 3.0.0", "solana-program-option 3.0.0", @@ -4887,7 +5664,7 @@ dependencies = [ "solana-sdk-ids 3.0.0", "solana-secp256k1-recover 3.0.0", "solana-serde-varint 3.0.0", - "solana-serialize-utils 3.1.0", + "solana-serialize-utils 3.0.0", "solana-sha256-hasher 3.0.0", "solana-short-vec 3.0.0", "solana-slot-hashes 3.0.0", @@ -4906,14 +5683,14 @@ dependencies = [ "solana-account-info 2.3.0", "solana-msg 2.2.1", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", ] [[package]] name = "solana-program-entrypoint" -version = "3.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6557cf5b5e91745d1667447438a1baa7823c6086e4ece67f8e6ebfa7a8f72660" +checksum = "bb61aaf3bf54b69721fbaadb0942cfd41f608cf279e514c1362264a24e469a9e" dependencies = [ "solana-account-info 3.0.0", "solana-define-syscall 3.0.0", @@ -4933,9 +5710,9 @@ dependencies = [ "serde", "serde_derive", "solana-decode-error", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-msg 2.2.1", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", ] [[package]] @@ -4997,18 +5774,58 @@ dependencies = [ "solana-program-error 3.0.0", ] +[[package]] +name = "solana-program-runtime" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05ec6893c130c9937b0fe042ee5ea91bda76320809ffc5953ddaa54d4bec279" +dependencies = [ + "agave-feature-set", + "agave-precompiles", + "base64 0.22.1", + "bincode", + "enum-iterator", + "itertools 0.12.1", + "log", + "percentage", + "rand 0.8.5", + "serde", + "solana-account", + "solana-clock 2.2.2", + "solana-compute-budget", + "solana-epoch-rewards 2.2.1", + "solana-epoch-schedule 2.2.1", + "solana-hash 2.2.1", + "solana-instruction 2.2.1", + "solana-last-restart-slot 2.2.1", + "solana-log-collector", + "solana-measure", + "solana-pubkey 2.2.1", + "solana-rent 2.2.1", + "solana-sbpf", + "solana-sdk-ids 2.2.1", + "solana-slot-hashes 2.2.1", + "solana-stable-layout 2.2.1", + "solana-sysvar 2.2.2", + "solana-sysvar-id 2.2.1", + "solana-timings", + "solana-transaction-context", + "solana-type-overrides", + "thiserror 2.0.14", +] + [[package]] name = "solana-pubkey" -version = "2.4.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b62adb9c3261a052ca1f999398c388f1daf558a1b492f60a6d9e64857db4ff1" +checksum = "40db1ff5a0f8aea2c158d78ab5f2cf897848964251d1df42fef78efd3c85b863" dependencies = [ "borsh 0.10.3", "borsh 1.5.7", + "bs58", "bytemuck", "bytemuck_derive", "curve25519-dalek 4.1.3", - "five8", "five8_const", "getrandom 0.2.10", "js-sys", @@ -5035,24 +5852,24 @@ dependencies = [ [[package]] name = "solana-pubsub-client" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d18a7476e1d2e8df5093816afd8fffee94fbb6e442d9be8e6bd3e85f88ce8d5c" +checksum = "383c6655590f07c920cecb03d700711294e26e60f315ed288ebc17a726b16bae" dependencies = [ "crossbeam-channel", "futures-util", - "http 0.2.12", "log", + "reqwest", "semver", "serde", "serde_derive", "serde_json", "solana-account-decoder-client-types", "solana-clock 2.2.2", - "solana-pubkey 2.4.0", - "solana-rpc-client-types", + "solana-pubkey 2.2.1", + "solana-rpc-client-api", "solana-signature", - "thiserror 2.0.17", + "thiserror 2.0.14", "tokio", "tokio-stream", "tokio-tungstenite", @@ -5060,6 +5877,37 @@ dependencies = [ "url", ] +[[package]] +name = "solana-quic-client" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a44b18672259dda0cb41471c2e8b0b71820bb36fcc6441c6a7cae5198936851" +dependencies = [ + "async-lock", + "async-trait", + "futures", + "itertools 0.12.1", + "lazy_static", + "log", + "quinn", + "quinn-proto", + "rustls 0.23.32", + "solana-connection-cache", + "solana-keypair", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-pubkey 2.2.1", + "solana-quic-definitions", + "solana-rpc-client-api", + "solana-signer", + "solana-streamer", + "solana-tls-utils", + "solana-transaction-error 2.2.1", + "thiserror 2.0.14", + "tokio", +] + [[package]] name = "solana-quic-definitions" version = "2.3.1" @@ -5069,11 +5917,21 @@ dependencies = [ "solana-keypair", ] +[[package]] +name = "solana-rayon-threadlimit" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8341bde7c49878587bf4823679dcbe7f46595612359caabab0550c1beea4c17f" +dependencies = [ + "lazy_static", + "num_cpus", +] + [[package]] name = "solana-remote-wallet" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f42662ecdff5cc2db0116730c83a6d6218db01f29750467ce2161675415acad6" +checksum = "4bece1535da0bc9c7febc7cffbfe6dd6a24aa2c2b4e6b05e30329661e9252d64" dependencies = [ "console", "dialoguer", @@ -5086,10 +5944,10 @@ dependencies = [ "semver", "solana-derivation-path", "solana-offchain-message", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-signature", "solana-signer", - "thiserror 2.0.17", + "thiserror 2.0.14", "uriparse", ] @@ -5121,9 +5979,9 @@ dependencies = [ [[package]] name = "solana-rent-collector" -version = "2.3.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "127e6dfa51e8c8ae3aa646d8b2672bc4ac901972a338a9e1cd249e030564fb9d" +checksum = "7c1e19f5d5108b0d824244425e43bc78bbb9476e2199e979b0230c9f632d3bf4" dependencies = [ "serde", "serde_derive", @@ -5131,7 +5989,7 @@ dependencies = [ "solana-clock 2.2.2", "solana-epoch-schedule 2.2.1", "solana-genesis-config", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-rent 2.2.1", "solana-sdk-ids 2.2.1", ] @@ -5142,7 +6000,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f6f9113c6003492e74438d1288e30cffa8ccfdc2ef7b49b9e816d8034da18cd" dependencies = [ - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-reward-info", ] @@ -5154,7 +6012,7 @@ checksum = "e4b22ea19ca2a3f28af7cd047c914abf833486bf7a7c4a10fc652fff09b385b1" dependencies = [ "lazy_static", "solana-feature-set", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", ] @@ -5170,18 +6028,17 @@ dependencies = [ [[package]] name = "solana-rpc-client" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8d3161ac0918178e674c1f7f1bfac40de3e7ed0383bd65747d63113c156eaeb" +checksum = "01ba3b0d458380c045675b1f55b472501c0ddf7f20fb87a71e73a0bda281f584" dependencies = [ "async-trait", "base64 0.22.1", "bincode", "bs58", - "futures", "indicatif", "log", - "reqwest 0.12.24", + "reqwest", "reqwest-middleware", "semver", "serde", @@ -5194,66 +6051,65 @@ dependencies = [ "solana-epoch-info", "solana-epoch-schedule 2.2.1", "solana-feature-gate-interface", - "solana-hash 2.3.0", - "solana-instruction 2.3.0", - "solana-message 2.4.0", - "solana-pubkey 2.4.0", + "solana-hash 2.2.1", + "solana-instruction 2.2.1", + "solana-message 2.2.1", + "solana-pubkey 2.2.1", "solana-rpc-client-api", "solana-signature", "solana-transaction", "solana-transaction-error 2.2.1", "solana-transaction-status-client-types", "solana-version", - "solana-vote-interface", "tokio", ] [[package]] name = "solana-rpc-client-api" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dbc138685c79d88a766a8fd825057a74ea7a21e1dd7f8de275ada899540fff7" +checksum = "9084cfe53a41e3dc322cda81a98c7f162c96113cc82ff508d5f580253e5fffde" dependencies = [ "anyhow", + "base64 0.22.1", + "bs58", "jsonrpc-core", - "reqwest 0.12.24", + "reqwest", "reqwest-middleware", + "semver", "serde", "serde_derive", "serde_json", + "solana-account", "solana-account-decoder-client-types", "solana-clock 2.2.2", - "solana-rpc-client-types", + "solana-commitment-config", + "solana-fee-calculator 2.2.1", + "solana-inflation", + "solana-inline-spl", + "solana-pubkey 2.2.1", "solana-signer", "solana-transaction-error 2.2.1", "solana-transaction-status-client-types", - "thiserror 2.0.17", + "solana-version", + "thiserror 2.0.14", ] [[package]] -name = "solana-rpc-client-types" -version = "2.3.13" +name = "solana-rpc-client-nonce-utils" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea428a81729255d895ea47fba9b30fd4dacbfe571a080448121bd0592751676" +checksum = "fe9002ac145168b0701d949817e5ad7ed5b732e441835d6a3b3658480666ece0" dependencies = [ - "base64 0.22.1", - "bs58", - "semver", - "serde", - "serde_derive", - "serde_json", "solana-account", - "solana-account-decoder-client-types", - "solana-clock 2.2.2", "solana-commitment-config", - "solana-fee-calculator 2.2.1", - "solana-inflation", - "solana-pubkey 2.4.0", - "solana-transaction-error 2.2.1", - "solana-transaction-status-client-types", - "solana-version", - "spl-generic-token", - "thiserror 2.0.17", + "solana-hash 2.2.1", + "solana-message 2.2.1", + "solana-nonce 2.2.1", + "solana-pubkey 2.2.1", + "solana-rpc-client", + "solana-sdk-ids 2.2.1", + "thiserror 2.0.14", ] [[package]] @@ -5264,15 +6120,32 @@ checksum = "61f1bc1357b8188d9c4a3af3fc55276e56987265eb7ad073ae6f8180ee54cecf" [[package]] name = "solana-sanitize" -version = "3.0.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf09694a0fc14e5ffb18f9b7b7c0f15ecb6eac5b5610bf76a1853459d19daf9" +checksum = "927e833259588ac8f860861db0f6e2668c3cc46d917798ade116858960acfe8a" + +[[package]] +name = "solana-sbpf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6aed9fa0b4791538896be288fb5ccb2ab9f558ca0fe1ff28dfd3046fbdb5c5" +dependencies = [ + "byteorder", + "combine 3.8.1", + "hash32", + "libc", + "log", + "rand 0.8.5", + "rustc-demangle", + "thiserror 1.0.66", + "winapi", +] [[package]] name = "solana-sdk" -version = "2.3.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cc0e4a7635b902791c44b6581bfb82f3ada32c5bc0929a64f39fe4bb384c86a" +checksum = "e8af90d2ce445440e0548fa4a5f96fe8b265c22041a68c942012ffadd029667d" dependencies = [ "bincode", "bs58", @@ -5296,10 +6169,10 @@ dependencies = [ "solana-genesis-config", "solana-hard-forks", "solana-inflation", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-keypair", - "solana-message 2.4.0", - "solana-native-token 2.2.2", + "solana-message 2.2.1", + "solana-native-token 2.2.1", "solana-nonce-account", "solana-offchain-message", "solana-packet", @@ -5307,9 +6180,9 @@ dependencies = [ "solana-precompile-error", "solana-precompiles", "solana-presigner", - "solana-program 2.3.0", + "solana-program 2.2.1", "solana-program-memory 2.3.1", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-quic-definitions", "solana-rent-collector", "solana-rent-debits", @@ -5335,7 +6208,7 @@ dependencies = [ "solana-transaction-context", "solana-transaction-error 2.2.1", "solana-validator-exit", - "thiserror 2.0.17", + "thiserror 2.0.14", "wasm-bindgen", ] @@ -5345,7 +6218,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c5d8b9cc68d5c88b062a33e23a6466722467dde0035152d8fb1afbcdf350a5f" dependencies = [ - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", ] [[package]] @@ -5366,7 +6239,7 @@ dependencies = [ "bs58", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -5378,14 +6251,14 @@ dependencies = [ "bs58", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] name = "solana-secp256k1-program" -version = "2.2.3" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f19833e4bc21558fe9ec61f239553abe7d05224347b57d65c2218aeeb82d6149" +checksum = "a0a1caa972414cc78122c32bdae65ac5fe89df7db598585a5cde19d16a20280a" dependencies = [ "bincode", "digest 0.10.7", @@ -5394,10 +6267,9 @@ dependencies = [ "serde_derive", "sha3", "solana-feature-set", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-precompile-error", "solana-sdk-ids 2.2.1", - "solana-signature", ] [[package]] @@ -5409,7 +6281,7 @@ dependencies = [ "borsh 1.5.7", "libsecp256k1", "solana-define-syscall 2.3.0", - "thiserror 2.0.17", + "thiserror 2.0.14", ] [[package]] @@ -5420,19 +6292,19 @@ checksum = "394a4470477d66296af5217970a905b1c5569032a7732c367fb69e5666c8607e" dependencies = [ "k256", "solana-define-syscall 3.0.0", - "thiserror 2.0.17", + "thiserror 2.0.14", ] [[package]] name = "solana-secp256r1-program" -version = "2.2.4" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce0ae46da3071a900f02d367d99b2f3058fe2e90c5062ac50c4f20cfedad8f0f" +checksum = "cf903cbdc36a161533812f90acfccdb434ed48982bd5dd71f3217930572c4a80" dependencies = [ "bytemuck", "openssl", "solana-feature-set", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-precompile-error", "solana-sdk-ids 2.2.1", ] @@ -5496,20 +6368,20 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "817a284b63197d2b27afdba829c5ab34231da4a9b4e763466a003c40ca4f535e" dependencies = [ - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", "solana-sanitize 2.2.1", ] [[package]] name = "solana-serialize-utils" -version = "3.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e41dd8feea239516c623a02f0a81c2367f4b604d7965237fed0751aeec33ed" +checksum = "e7665da4f6e07b58c93ef6aaf9fb6a923fd11b0922ffc53ba74c3cadfa490f26" dependencies = [ "solana-instruction-error", "solana-pubkey 3.0.0", - "solana-sanitize 3.0.1", + "solana-sanitize 3.0.0", ] [[package]] @@ -5520,7 +6392,7 @@ checksum = "5aa3feb32c28765f6aa1ce8f3feac30936f16c5c3f7eb73d63a5b8f6f8ecdc44" dependencies = [ "sha2 0.10.8", "solana-define-syscall 2.3.0", - "solana-hash 2.3.0", + "solana-hash 2.2.1", ] [[package]] @@ -5559,7 +6431,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afd3db0461089d1ad1a78d9ba3f15b563899ca2386351d38428faa5350c60a98" dependencies = [ "solana-hard-forks", - "solana-hash 2.3.0", + "solana-hash 2.2.1", "solana-sha256-hasher 2.3.0", ] @@ -5569,7 +6441,7 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64c8ec8e657aecfc187522fc67495142c12f35e55ddeca8698edbb738b8dbd8c" dependencies = [ - "ed25519-dalek", + "ed25519-dalek 1.0.1", "five8", "rand 0.8.5", "serde", @@ -5584,7 +6456,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c41991508a4b02f021c1342ba00bcfa098630b213726ceadc7cb032e051975b" dependencies = [ - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-signature", "solana-transaction-error 2.2.1", ] @@ -5597,7 +6469,7 @@ checksum = "0c8691982114513763e88d04094c9caa0376b867a29577939011331134c301ce" dependencies = [ "serde", "serde_derive", - "solana-hash 2.3.0", + "solana-hash 2.2.1", "solana-sdk-ids 2.2.1", "solana-sysvar-id 2.2.1", ] @@ -5647,8 +6519,8 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f14f7d02af8f2bc1b5efeeae71bc1c2b7f0f65cd75bcc7d8180f2c762a57f54" dependencies = [ - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", ] [[package]] @@ -5675,18 +6547,59 @@ dependencies = [ "solana-clock 2.2.2", "solana-cpi 2.2.1", "solana-decode-error", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-system-interface 1.0.0", "solana-sysvar-id 2.2.1", ] [[package]] -name = "solana-svm-feature-set" -version = "2.3.13" +name = "solana-streamer" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f24b836eb4d74ec255217bdbe0f24f64a07adeac31aca61f334f91cd4a3b1d5" +checksum = "1bc585497895fa00c6d99ae48fca502e1e8503ce60c697923c9b72543e070d77" +dependencies = [ + "async-channel", + "bytes", + "crossbeam-channel", + "dashmap", + "futures", + "futures-util", + "governor", + "histogram", + "indexmap 2.10.0", + "itertools 0.12.1", + "libc", + "log", + "nix", + "pem", + "percentage", + "quinn", + "quinn-proto", + "rand 0.8.5", + "rustls 0.23.32", + "smallvec", + "socket2 0.5.10", + "solana-keypair", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-packet", + "solana-perf", + "solana-pubkey 2.2.1", + "solana-quic-definitions", + "solana-signature", + "solana-signer", + "solana-time-utils", + "solana-tls-utils", + "solana-transaction-error 2.2.1", + "solana-transaction-metrics-tracker", + "thiserror 2.0.14", + "tokio", + "tokio-util", + "x509-parser", +] [[package]] name = "solana-system-interface" @@ -5699,8 +6612,8 @@ dependencies = [ "serde", "serde_derive", "solana-decode-error", - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", "wasm-bindgen", ] @@ -5722,10 +6635,10 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bd98a25e5bcba8b6be8bcbb7b84b24c2a6a8178d7fb0e3077a916855ceba91a" dependencies = [ - "solana-hash 2.3.0", + "solana-hash 2.2.1", "solana-keypair", - "solana-message 2.4.0", - "solana-pubkey 2.4.0", + "solana-message 2.2.1", + "solana-pubkey 2.2.1", "solana-signer", "solana-system-interface 1.0.0", "solana-transaction", @@ -5733,9 +6646,9 @@ dependencies = [ [[package]] name = "solana-sysvar" -version = "2.3.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8c3595f95069f3d90f275bb9bd235a1973c4d059028b0a7f81baca2703815db" +checksum = "d50c92bc019c590f5e42c61939676e18d14809ed00b2a59695dd5c67ae72c097" dependencies = [ "base64 0.22.1", "bincode", @@ -5750,14 +6663,14 @@ dependencies = [ "solana-epoch-rewards 2.2.1", "solana-epoch-schedule 2.2.1", "solana-fee-calculator 2.2.1", - "solana-hash 2.3.0", - "solana-instruction 2.3.0", + "solana-hash 2.2.1", + "solana-instruction 2.2.1", "solana-instructions-sysvar 2.2.2", "solana-last-restart-slot 2.2.1", "solana-program-entrypoint 2.3.0", "solana-program-error 2.2.2", "solana-program-memory 2.3.1", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-rent 2.2.1", "solana-sanitize 2.2.1", "solana-sdk-ids 2.2.1", @@ -5790,7 +6703,7 @@ dependencies = [ "solana-hash 3.0.0", "solana-instruction 3.0.0", "solana-last-restart-slot 3.0.0", - "solana-program-entrypoint 3.1.0", + "solana-program-entrypoint 3.0.0", "solana-program-error 3.0.0", "solana-program-memory 3.0.0", "solana-pubkey 3.0.0", @@ -5808,7 +6721,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5762b273d3325b047cfda250787f8d796d781746860d5d0a746ee29f3e8812c1" dependencies = [ - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", ] @@ -5822,12 +6735,99 @@ dependencies = [ "solana-sdk-ids 3.0.0", ] +[[package]] +name = "solana-thin-client" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5952370de8754900c00ba204fdb99fc284f251d3c36413a81b0abf1424b1addd" +dependencies = [ + "bincode", + "log", + "rayon", + "solana-account", + "solana-client-traits", + "solana-clock 2.2.2", + "solana-commitment-config", + "solana-connection-cache", + "solana-epoch-info", + "solana-hash 2.2.1", + "solana-instruction 2.2.1", + "solana-keypair", + "solana-message 2.2.1", + "solana-pubkey 2.2.1", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-signature", + "solana-signer", + "solana-system-interface 1.0.0", + "solana-transaction", + "solana-transaction-error 2.2.1", +] + [[package]] name = "solana-time-utils" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af261afb0e8c39252a04d026e3ea9c405342b08c871a2ad8aa5448e068c784c" +[[package]] +name = "solana-timings" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1939a33d02905beaa4d657e9ade9cfcac6f9676a4a6178829a617503d45ab3b8" +dependencies = [ + "eager", + "enum-iterator", + "solana-pubkey 2.2.1", +] + +[[package]] +name = "solana-tls-utils" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3699c06092641439f1679d4e015153c504abb660f7cc71eba4836d8cfc41649f" +dependencies = [ + "rustls 0.23.32", + "solana-keypair", + "solana-pubkey 2.2.1", + "solana-signer", + "x509-parser", +] + +[[package]] +name = "solana-tpu-client" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc020d22da059676305eb384e6b5d5ace59d54b5b3248fa3fd90341a69a5b94" +dependencies = [ + "async-trait", + "bincode", + "futures-util", + "indexmap 2.10.0", + "indicatif", + "log", + "rayon", + "solana-client-traits", + "solana-clock 2.2.2", + "solana-commitment-config", + "solana-connection-cache", + "solana-epoch-info", + "solana-measure", + "solana-message 2.2.1", + "solana-net-utils", + "solana-pubkey 2.2.1", + "solana-pubsub-client", + "solana-quic-definitions", + "solana-rpc-client", + "solana-rpc-client-api", + "solana-signature", + "solana-signer", + "solana-transaction", + "solana-transaction-error 2.2.1", + "thiserror 2.0.14", + "tokio", +] + [[package]] name = "solana-transaction" version = "2.2.2" @@ -5839,12 +6839,12 @@ dependencies = [ "serde_derive", "solana-bincode", "solana-feature-set", - "solana-hash 2.3.0", - "solana-instruction 2.3.0", + "solana-hash 2.2.1", + "solana-instruction 2.2.1", "solana-keypair", - "solana-message 2.4.0", + "solana-message 2.2.1", "solana-precompiles", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-sanitize 2.2.1", "solana-sdk-ids 2.2.1", "solana-short-vec 2.2.1", @@ -5857,19 +6857,18 @@ dependencies = [ [[package]] name = "solana-transaction-context" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a312304361987a85b2ef2293920558e6612876a639dd1309daf6d0d59ef2fe" +checksum = "37a936e58ff94d222e333216a26dbef4ca63ca01332cb0a997c2138a7a59b8dd" dependencies = [ "bincode", "serde", "serde_derive", "solana-account", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-instructions-sysvar 2.2.2", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-rent 2.2.1", - "solana-sdk-ids 2.2.1", ] [[package]] @@ -5880,7 +6879,7 @@ checksum = "222a9dc8fdb61c6088baab34fc3a8b8473a03a7a5fd404ed8dd502fa79b67cb1" dependencies = [ "serde", "serde_derive", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-sanitize 2.2.1", ] @@ -5891,14 +6890,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4222065402340d7e6aec9dc3e54d22992ddcf923d91edcd815443c2bfca3144a" dependencies = [ "solana-instruction-error", - "solana-sanitize 3.0.1", + "solana-sanitize 3.0.0", +] + +[[package]] +name = "solana-transaction-metrics-tracker" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aada6ddf01855aae8d8ab81616357e0e726e3a083d6eefd35087bf84758f6bd3" +dependencies = [ + "base64 0.22.1", + "bincode", + "lazy_static", + "log", + "rand 0.8.5", + "solana-packet", + "solana-perf", + "solana-short-vec 2.2.1", + "solana-signature", ] [[package]] name = "solana-transaction-status-client-types" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f1d7c2387c35850848212244d2b225847666cb52d3bd59a5c409d2c300303d" +checksum = "37272b1efc4dc10be4a2feddf3fa93fabb9ec5ee5d50f1204a0abfa4340768a9" dependencies = [ "base64 0.22.1", "bincode", @@ -5908,13 +6924,39 @@ dependencies = [ "serde_json", "solana-account-decoder-client-types", "solana-commitment-config", - "solana-message 2.4.0", + "solana-message 2.2.1", "solana-reward-info", "solana-signature", "solana-transaction", "solana-transaction-context", "solana-transaction-error 2.2.1", - "thiserror 2.0.17", + "thiserror 2.0.14", +] + +[[package]] +name = "solana-type-overrides" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae6235135ae4e449c38b1910f14935679821ee4f3dd32aa3826831694b0e50e" +dependencies = [ + "lazy_static", + "rand 0.8.5", +] + +[[package]] +name = "solana-udp-client" +version = "2.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16807822d271ee9ddfd7bd54a4c8b2ac40bcde10a9c65c9f430c351fc396e813" +dependencies = [ + "async-trait", + "solana-connection-cache", + "solana-keypair", + "solana-net-utils", + "solana-streamer", + "solana-transaction-error 2.2.1", + "thiserror 2.0.14", + "tokio", ] [[package]] @@ -5925,12 +6967,11 @@ checksum = "7bbf6d7a3c0b28dd5335c52c0e9eae49d0ae489a8f324917faf0ded65a812c1d" [[package]] name = "solana-version" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3324d46c7f7b7f5d34bf7dc71a2883bdc072c7b28ca81d0b2167ecec4cf8da9f" +checksum = "a609298041c26bc7903c797e46fef22714a2f07ed4120fbf9432f6a615a49752" dependencies = [ "agave-feature-set", - "rand 0.8.5", "semver", "serde", "serde_derive", @@ -5940,9 +6981,9 @@ dependencies = [ [[package]] name = "solana-vote-interface" -version = "2.2.6" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b80d57478d6599d30acc31cc5ae7f93ec2361a06aefe8ea79bc81739a08af4c3" +checksum = "ef4f08746f154458f28b98330c0d55cb431e2de64ee4b8efc98dcbe292e0672b" dependencies = [ "bincode", "num-derive 0.4.0", @@ -5951,9 +6992,9 @@ dependencies = [ "serde_derive", "solana-clock 2.2.2", "solana-decode-error", - "solana-hash 2.3.0", - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-hash 2.2.1", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", "solana-rent 2.2.1", "solana-sdk-ids 2.2.1", "solana-serde-varint 2.2.2", @@ -5964,9 +7005,9 @@ dependencies = [ [[package]] name = "solana-zk-sdk" -version = "2.3.13" +version = "2.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b9fc6ec37d16d0dccff708ed1dd6ea9ba61796700c3bb7c3b401973f10f63b" +checksum = "c70bffb28540a216443ba302ab017d18a0e03f5300772929db79608870ee1c6e" dependencies = [ "aes-gcm-siv", "base64 0.22.1", @@ -5976,6 +7017,7 @@ dependencies = [ "curve25519-dalek 4.1.3", "itertools 0.12.1", "js-sys", + "lazy_static", "merlin", "num-derive 0.4.0", "num-traits", @@ -5985,15 +7027,15 @@ dependencies = [ "serde_json", "sha3", "solana-derivation-path", - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", "solana-sdk-ids 2.2.1", "solana-seed-derivable", "solana-seed-phrase", "solana-signature", "solana-signer", "subtle", - "thiserror 2.0.17", + "thiserror 2.0.14", "wasm-bindgen", "zeroize", ] @@ -6010,6 +7052,15 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[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" @@ -6029,11 +7080,11 @@ dependencies = [ "borsh 1.5.7", "num-derive 0.4.0", "num-traits", - "solana-program 2.3.0", + "solana-program 2.2.1", "spl-associated-token-account-client", - "spl-token", - "spl-token-2022", - "thiserror 2.0.17", + "spl-token 8.0.0", + "spl-token-2022 8.0.1", + "thiserror 2.0.14", ] [[package]] @@ -6042,8 +7093,8 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f8349dbcbe575f354f9a533a21f272f3eb3808a49e2fdc1c34393b88ba76cb" dependencies = [ - "solana-instruction 2.3.0", - "solana-pubkey 2.4.0", + "solana-instruction 2.2.1", + "solana-pubkey 2.2.1", ] [[package]] @@ -6066,7 +7117,7 @@ checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" dependencies = [ "quote", "spl-discriminator-syn", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -6078,10 +7129,23 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.107", + "syn 2.0.104", "thiserror 1.0.66", ] +[[package]] +name = "spl-elgamal-registry" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce0f668975d2b0536e8a8fd60e56a05c467f06021dae037f1d0cfed0de2e231d" +dependencies = [ + "bytemuck", + "solana-program 2.2.1", + "solana-zk-sdk", + "spl-pod", + "spl-token-confidential-transfer-proof-extraction 0.2.1", +] + [[package]] name = "spl-elgamal-registry" version = "0.2.0" @@ -6091,28 +7155,18 @@ dependencies = [ "bytemuck", "solana-account-info 2.3.0", "solana-cpi 2.2.1", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-msg 2.2.1", "solana-program-entrypoint 2.3.0", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-rent 2.2.1", "solana-sdk-ids 2.2.1", "solana-system-interface 1.0.0", - "solana-sysvar 2.3.0", + "solana-sysvar 2.2.2", "solana-zk-sdk", "spl-pod", - "spl-token-confidential-transfer-proof-extraction", -] - -[[package]] -name = "spl-generic-token" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "741a62a566d97c58d33f9ed32337ceedd4e35109a686e31b1866c5dfa56abddc" -dependencies = [ - "bytemuck", - "solana-pubkey 2.4.0", + "spl-token-confidential-transfer-proof-extraction 0.3.0", ] [[package]] @@ -6122,11 +7176,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f09647c0974e33366efeb83b8e2daebb329f0420149e74d3a4bd2c08cf9f7cb" dependencies = [ "solana-account-info 2.3.0", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-msg 2.2.1", "solana-program-entrypoint 2.3.0", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", ] [[package]] @@ -6144,9 +7198,22 @@ dependencies = [ "solana-msg 2.2.1", "solana-program-error 2.2.2", "solana-program-option 2.2.1", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-zk-sdk", - "thiserror 2.0.17", + "thiserror 2.0.14", +] + +[[package]] +name = "spl-program-error" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d39b5186f42b2b50168029d81e58e800b690877ef0b30580d107659250da1d1" +dependencies = [ + "num-derive 0.4.0", + "num-traits", + "solana-program 2.2.1", + "spl-program-error-derive 0.4.1", + "thiserror 1.0.66", ] [[package]] @@ -6160,8 +7227,20 @@ dependencies = [ "solana-decode-error", "solana-msg 2.2.1", "solana-program-error 2.2.2", - "spl-program-error-derive", - "thiserror 2.0.17", + "spl-program-error-derive 0.5.0", + "thiserror 2.0.14", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d375dd76c517836353e093c2dbb490938ff72821ab568b545fd30ab3256b3e" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.104", ] [[package]] @@ -6173,7 +7252,29 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.8", - "syn 2.0.107", + "syn 2.0.104", +] + +[[package]] +name = "spl-tlv-account-resolution" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd99ff1e9ed2ab86e3fd582850d47a739fec1be9f4661cba1782d3a0f26805f3" +dependencies = [ + "bytemuck", + "num-derive 0.4.0", + "num-traits", + "solana-account-info 2.3.0", + "solana-decode-error", + "solana-instruction 2.2.1", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "solana-pubkey 2.2.1", + "spl-discriminator", + "spl-pod", + "spl-program-error 0.6.0", + "spl-type-length-value 0.7.0", + "thiserror 1.0.66", ] [[package]] @@ -6187,15 +7288,30 @@ dependencies = [ "num-traits", "solana-account-info 2.3.0", "solana-decode-error", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-msg 2.2.1", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "spl-discriminator", "spl-pod", - "spl-program-error", - "spl-type-length-value", - "thiserror 2.0.17", + "spl-program-error 0.7.0", + "spl-type-length-value 0.8.0", + "thiserror 2.0.14", +] + +[[package]] +name = "spl-token" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed320a6c934128d4f7e54fe00e16b8aeaecf215799d060ae14f93378da6dc834" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.4.0", + "num-traits", + "num_enum", + "solana-program 2.2.1", + "thiserror 1.0.66", ] [[package]] @@ -6212,18 +7328,46 @@ dependencies = [ "solana-account-info 2.3.0", "solana-cpi 2.2.1", "solana-decode-error", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-msg 2.2.1", "solana-program-entrypoint 2.3.0", "solana-program-error 2.2.2", "solana-program-memory 2.3.1", "solana-program-option 2.2.1", "solana-program-pack 2.2.1", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-rent 2.2.1", "solana-sdk-ids 2.2.1", - "solana-sysvar 2.3.0", - "thiserror 2.0.17", + "solana-sysvar 2.2.2", + "thiserror 2.0.14", +] + +[[package]] +name = "spl-token-2022" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9048b26b0df0290f929ff91317c83db28b3ef99af2b3493dd35baa146774924c" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.4.0", + "num-traits", + "num_enum", + "solana-program 2.2.1", + "solana-security-txt", + "solana-zk-sdk", + "spl-elgamal-registry 0.1.1", + "spl-memo", + "spl-pod", + "spl-token 7.0.0", + "spl-token-confidential-transfer-ciphertext-arithmetic 0.2.1", + "spl-token-confidential-transfer-proof-extraction 0.2.1", + "spl-token-confidential-transfer-proof-generation 0.3.0", + "spl-token-group-interface 0.5.0", + "spl-token-metadata-interface 0.6.0", + "spl-transfer-hook-interface 0.9.0", + "spl-type-length-value 0.7.0", + "thiserror 2.0.14", ] [[package]] @@ -6241,40 +7385,52 @@ dependencies = [ "solana-clock 2.2.2", "solana-cpi 2.2.1", "solana-decode-error", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-msg 2.2.1", - "solana-native-token 2.2.2", + "solana-native-token 2.2.1", "solana-program-entrypoint 2.3.0", "solana-program-error 2.2.2", "solana-program-memory 2.3.1", "solana-program-option 2.2.1", "solana-program-pack 2.2.1", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "solana-rent 2.2.1", "solana-sdk-ids 2.2.1", "solana-security-txt", "solana-system-interface 1.0.0", - "solana-sysvar 2.3.0", + "solana-sysvar 2.2.2", "solana-zk-sdk", - "spl-elgamal-registry", + "spl-elgamal-registry 0.2.0", "spl-memo", "spl-pod", - "spl-token", - "spl-token-confidential-transfer-ciphertext-arithmetic", - "spl-token-confidential-transfer-proof-extraction", - "spl-token-confidential-transfer-proof-generation", - "spl-token-group-interface", - "spl-token-metadata-interface", - "spl-transfer-hook-interface", - "spl-type-length-value", - "thiserror 2.0.17", + "spl-token 8.0.0", + "spl-token-confidential-transfer-ciphertext-arithmetic 0.3.0", + "spl-token-confidential-transfer-proof-extraction 0.3.0", + "spl-token-confidential-transfer-proof-generation 0.4.0", + "spl-token-group-interface 0.6.0", + "spl-token-metadata-interface 0.7.0", + "spl-transfer-hook-interface 0.10.0", + "spl-type-length-value 0.8.0", + "thiserror 2.0.14", ] [[package]] name = "spl-token-confidential-transfer-ciphertext-arithmetic" -version = "0.3.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cddd52bfc0f1c677b41493dafa3f2dbbb4b47cf0990f08905429e19dc8289b35" +checksum = "170378693c5516090f6d37ae9bad2b9b6125069be68d9acd4865bbe9fc8499fd" +dependencies = [ + "base64 0.22.1", + "bytemuck", + "solana-curve25519", + "solana-zk-sdk", +] + +[[package]] +name = "spl-token-confidential-transfer-ciphertext-arithmetic" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94ab20faf7b5edaa79acd240e0f21d5a2ef936aa99ed98f698573a2825b299c4" dependencies = [ "base64 0.22.1", "bytemuck", @@ -6282,54 +7438,119 @@ dependencies = [ "solana-zk-sdk", ] +[[package]] +name = "spl-token-confidential-transfer-proof-extraction" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff2d6a445a147c9d6dd77b8301b1e116c8299601794b558eafa409b342faf96" +dependencies = [ + "bytemuck", + "solana-curve25519", + "solana-program 2.2.1", + "solana-zk-sdk", + "spl-pod", + "thiserror 2.0.14", +] + [[package]] name = "spl-token-confidential-transfer-proof-extraction" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe2629860ff04c17bafa9ba4bed8850a404ecac81074113e1f840dbd0ebb7bd6" +checksum = "fe2629860ff04c17bafa9ba4bed8850a404ecac81074113e1f840dbd0ebb7bd6" +dependencies = [ + "bytemuck", + "solana-account-info 2.3.0", + "solana-curve25519", + "solana-instruction 2.2.1", + "solana-instructions-sysvar 2.2.2", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "solana-pubkey 2.2.1", + "solana-sdk-ids 2.2.1", + "solana-zk-sdk", + "spl-pod", + "thiserror 2.0.14", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-generation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3597628b0d2fe94e7900fd17cdb4cfbb31ee35c66f82809d27d86e44b2848b" +dependencies = [ + "curve25519-dalek 4.1.3", + "solana-zk-sdk", + "thiserror 2.0.14", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-generation" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae5b124840d4aed474cef101d946a798b806b46a509ee4df91021e1ab1cef3ef" +dependencies = [ + "curve25519-dalek 4.1.3", + "solana-zk-sdk", + "thiserror 2.0.14", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d595667ed72dbfed8c251708f406d7c2814a3fa6879893b323d56a10bedfc799" dependencies = [ "bytemuck", - "solana-account-info 2.3.0", - "solana-curve25519", - "solana-instruction 2.3.0", - "solana-instructions-sysvar 2.2.2", + "num-derive 0.4.0", + "num-traits", + "solana-decode-error", + "solana-instruction 2.2.1", "solana-msg 2.2.1", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", - "solana-sdk-ids 2.2.1", - "solana-zk-sdk", + "solana-pubkey 2.2.1", + "spl-discriminator", "spl-pod", - "thiserror 2.0.17", + "thiserror 1.0.66", ] [[package]] -name = "spl-token-confidential-transfer-proof-generation" -version = "0.4.1" +name = "spl-token-group-interface" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa27b9174bea869a7ebf31e0be6890bce90b1a4288bc2bbf24bd413f80ae3fde" +checksum = "5597b4cd76f85ce7cd206045b7dc22da8c25516573d42d267c8d1fd128db5129" dependencies = [ - "curve25519-dalek 4.1.3", - "solana-zk-sdk", - "thiserror 2.0.17", + "bytemuck", + "num-derive 0.4.0", + "num-traits", + "solana-decode-error", + "solana-instruction 2.2.1", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "solana-pubkey 2.2.1", + "spl-discriminator", + "spl-pod", + "thiserror 2.0.14", ] [[package]] -name = "spl-token-group-interface" +name = "spl-token-metadata-interface" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5597b4cd76f85ce7cd206045b7dc22da8c25516573d42d267c8d1fd128db5129" +checksum = "dfb9c89dbc877abd735f05547dcf9e6e12c00c11d6d74d8817506cab4c99fdbb" dependencies = [ - "bytemuck", + "borsh 1.5.7", "num-derive 0.4.0", "num-traits", + "solana-borsh 2.2.1", "solana-decode-error", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", "solana-msg 2.2.1", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "spl-discriminator", "spl-pod", - "thiserror 2.0.17", + "spl-type-length-value 0.7.0", + "thiserror 1.0.66", ] [[package]] @@ -6343,14 +7564,39 @@ dependencies = [ "num-traits", "solana-borsh 2.2.1", "solana-decode-error", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "solana-pubkey 2.2.1", + "spl-discriminator", + "spl-pod", + "spl-type-length-value 0.8.0", + "thiserror 2.0.14", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa7503d52107c33c88e845e1351565050362c2314036ddf19a36cd25137c043" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.4.0", + "num-traits", + "solana-account-info 2.3.0", + "solana-cpi 2.2.1", + "solana-decode-error", + "solana-instruction 2.2.1", "solana-msg 2.2.1", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", + "solana-pubkey 2.2.1", "spl-discriminator", "spl-pod", - "spl-type-length-value", - "thiserror 2.0.17", + "spl-program-error 0.6.0", + "spl-tlv-account-resolution 0.9.0", + "spl-type-length-value 0.7.0", + "thiserror 1.0.66", ] [[package]] @@ -6366,16 +7612,34 @@ dependencies = [ "solana-account-info 2.3.0", "solana-cpi 2.2.1", "solana-decode-error", - "solana-instruction 2.3.0", + "solana-instruction 2.2.1", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "solana-pubkey 2.2.1", + "spl-discriminator", + "spl-pod", + "spl-program-error 0.7.0", + "spl-tlv-account-resolution 0.10.0", + "spl-type-length-value 0.8.0", + "thiserror 2.0.14", +] + +[[package]] +name = "spl-type-length-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba70ef09b13af616a4c987797870122863cba03acc4284f226a4473b043923f9" +dependencies = [ + "bytemuck", + "num-derive 0.4.0", + "num-traits", + "solana-account-info 2.3.0", + "solana-decode-error", "solana-msg 2.2.1", "solana-program-error 2.2.2", - "solana-pubkey 2.4.0", "spl-discriminator", "spl-pod", - "spl-program-error", - "spl-tlv-account-resolution", - "spl-type-length-value", - "thiserror 2.0.17", + "thiserror 1.0.66", ] [[package]] @@ -6393,14 +7657,14 @@ dependencies = [ "solana-program-error 2.2.2", "spl-discriminator", "spl-pod", - "thiserror 2.0.17", + "thiserror 2.0.14", ] [[package]] name = "stable_deref_trait" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "strsim" @@ -6408,6 +7672,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.1" @@ -6433,9 +7703,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.107" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -6449,12 +7719,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] -name = "sync_wrapper" -version = "1.0.2" +name = "synstructure" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "futures-core", + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", ] [[package]] @@ -6465,7 +7738,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -6500,6 +7773,15 @@ dependencies = [ "xattr", ] +[[package]] +name = "task-local-extensions" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba323866e5d033818e3240feeb9f7db2c4296674e4d9e16b97b7bf8f490434e8" +dependencies = [ + "pin-utils", +] + [[package]] name = "tempfile" version = "3.8.0" @@ -6542,11 +7824,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.14", ] [[package]] @@ -6557,18 +7839,49 @@ checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", ] [[package]] @@ -6590,6 +7903,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tiny-bip39" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a30fd743a02bf35236f6faf99adb03089bb77e91c998dac2c2ad76bb424f668c" +dependencies = [ + "once_cell", + "pbkdf2 0.12.2", + "rand 0.8.5", + "rustc-hash 1.1.0", + "sha2 0.10.8", + "thiserror 1.0.66", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + [[package]] name = "tinystr" version = "0.8.1" @@ -6617,30 +7947,33 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.48.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ + "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.1", + "slab", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -6653,16 +7986,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" -dependencies = [ - "rustls 0.23.23", - "tokio", -] - [[package]] name = "tokio-stream" version = "0.1.17" @@ -6684,23 +8007,22 @@ dependencies = [ "log", "rustls 0.21.12", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls", "tungstenite", "webpki-roots 0.25.2", ] [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -6751,7 +8073,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", @@ -6764,7 +8086,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", @@ -6777,55 +8099,16 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.10.0", "toml_datetime", "winnow", ] -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper 1.0.2", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" -dependencies = [ - "bitflags 2.10.0", - "bytes", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - [[package]] name = "tower-service" -version = "0.3.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" @@ -6834,6 +8117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", + "log", "pin-project-lite", "tracing-core", ] @@ -6862,7 +8146,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 0.2.12", + "http", "httparse", "log", "rand 0.8.5", @@ -6918,9 +8202,15 @@ checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "unicode-width" -version = "0.2.2" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" + +[[package]] +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" @@ -6932,6 +8222,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 = "unsafe-libyaml" version = "0.2.11" @@ -7007,6 +8306,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "walkdir" version = "2.4.0" @@ -7040,28 +8345,27 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", - "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.104" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", "wasm-bindgen-shared", ] @@ -7079,9 +8383,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7089,22 +8393,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] @@ -7129,6 +8433,24 @@ 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" @@ -7144,15 +8466,6 @@ version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" -[[package]] -name = "webpki-roots" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "winapi" version = "0.3.9" @@ -7193,6 +8506,12 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" @@ -7226,22 +8545,13 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -7268,30 +8578,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -7304,12 +8597,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -7322,12 +8609,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -7340,24 +8621,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -7370,12 +8639,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -7388,12 +8651,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -7406,12 +8663,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -7424,12 +8675,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "0.5.15" @@ -7455,6 +8700,24 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +[[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.66", + "time", +] + [[package]] name = "xattr" version = "1.0.1" @@ -7484,8 +8747,8 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", - "synstructure", + "syn 2.0.104", + "synstructure 0.13.2", ] [[package]] @@ -7505,7 +8768,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -7525,8 +8788,8 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", - "synstructure", + "syn 2.0.104", + "synstructure 0.13.2", ] [[package]] @@ -7546,7 +8809,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] @@ -7579,14 +8842,14 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.104", ] [[package]] name = "zstd" -version = "0.13.3" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" dependencies = [ "zstd-safe", ] diff --git a/bench/BINARY_SIZE.md b/bench/BINARY_SIZE.md index a61c119f9c..53e257b699 100644 --- a/bench/BINARY_SIZE.md +++ b/bench/BINARY_SIZE.md @@ -16,9 +16,9 @@ The programs and their tests are located in [/tests/bench](https://github.com/co Solana version: 2.3.0 -| Program | Binary Size | - | -| ------- | ----------- | ------------------------ | -| bench | 966,096 | 🟢 **-160,744 (14.27%)** | +| Program | Binary Size | - | +| ------- | ----------- | ----------------------- | +| bench | 1,025,312 | 🟢 **-101,528 (9.01%)** | ### Notable changes diff --git a/bench/COMPUTE_UNITS.md b/bench/COMPUTE_UNITS.md index 56c3a6f8ae..cba059a14f 100644 --- a/bench/COMPUTE_UNITS.md +++ b/bench/COMPUTE_UNITS.md @@ -22,77 +22,77 @@ Solana version: 2.3.0 | accountInfo2 | 1,053 | - | | accountInfo4 | 1,750 | - | | accountInfo8 | 3,135 | - | -| accountEmptyInit1 | 4,915 | - | +| accountEmptyInit1 | 4,878 | 🟢 **-37 (0.75%)** | | accountEmpty1 | 774 | - | -| accountEmptyInit2 | 8,793 | - | +| accountEmptyInit2 | 8,755 | 🟢 **-38 (0.43%)** | | accountEmpty2 | 1,174 | - | -| accountEmptyInit4 | 16,495 | 🟢 **-253 (1.51%)** | +| accountEmptyInit4 | 16,503 | 🟢 **-245 (1.46%)** | | accountEmpty4 | 1,962 | - | -| accountEmptyInit8 | 31,997 | 🟢 **-360 (1.11%)** | +| accountEmptyInit8 | 32,005 | 🟢 **-352 (1.09%)** | | accountEmpty8 | 3,548 | - | -| accountSizedInit1 | 5,019 | - | -| accountSized1 | 813 | 🟢 **-9 (1.09%)** | -| accountSizedInit2 | 8,981 | - | -| accountSized2 | 1,222 | 🟢 **-18 (1.45%)** | -| accountSizedInit4 | 16,850 | 🟢 **-304 (1.77%)** | -| accountSized4 | 2,046 | 🟢 **-36 (1.73%)** | -| accountSizedInit8 | 32,653 | 🟢 **-360 (1.09%)** | -| accountSized8 | 3,690 | 🟢 **-72 (1.91%)** | -| accountUnsizedInit1 | 5,127 | - | -| accountUnsized1 | 853 | 🟢 **-21 (2.40%)** | -| accountUnsizedInit2 | 9,131 | 🟢 **-120 (1.30%)** | -| accountUnsized2 | 1,284 | 🟢 **-42 (3.17%)** | -| accountUnsizedInit4 | 17,198 | 🟢 **-180 (1.04%)** | -| accountUnsized4 | 2,147 | 🟢 **-84 (3.77%)** | -| accountUnsizedInit8 | 33,324 | 🟢 **-637 (1.88%)** | -| accountUnsized8 | 3,867 | 🟢 **-168 (4.16%)** | -| boxedAccountEmptyInit1 | 5,007 | - | +| accountSizedInit1 | 4,982 | 🟢 **-37 (0.74%)** | +| accountSized1 | 822 | - | +| accountSizedInit2 | 8,940 | 🟢 **-41 (0.46%)** | +| accountSized2 | 1,240 | - | +| accountSizedInit4 | 16,858 | 🟢 **-296 (1.73%)** | +| accountSized4 | 2,082 | - | +| accountSizedInit8 | 32,661 | 🟢 **-352 (1.07%)** | +| accountSized8 | 3,762 | - | +| accountUnsizedInit1 | 5,090 | 🟢 **-37 (0.72%)** | +| accountUnsized1 | 874 | - | +| accountUnsizedInit2 | 9,139 | 🟢 **-112 (1.21%)** | +| accountUnsized2 | 1,326 | - | +| accountUnsizedInit4 | 17,206 | 🟢 **-172 (0.99%)** | +| accountUnsized4 | 2,231 | - | +| accountUnsizedInit8 | 33,332 | 🟢 **-629 (1.85%)** | +| accountUnsized8 | 4,035 | - | +| boxedAccountEmptyInit1 | 4,970 | 🟢 **-37 (0.74%)** | | boxedAccountEmpty1 | 864 | - | -| boxedAccountEmptyInit2 | 8,906 | - | +| boxedAccountEmptyInit2 | 8,868 | 🟢 **-38 (0.43%)** | | boxedAccountEmpty2 | 1,286 | - | -| boxedAccountEmptyInit4 | 16,648 | 🟢 **-254 (1.50%)** | +| boxedAccountEmptyInit4 | 16,656 | 🟢 **-246 (1.46%)** | | boxedAccountEmpty4 | 2,115 | - | -| boxedAccountEmptyInit8 | 32,231 | 🟢 **-360 (1.10%)** | +| boxedAccountEmptyInit8 | 32,239 | 🟢 **-352 (1.08%)** | | boxedAccountEmpty8 | 3,801 | - | -| boxedAccountSizedInit1 | 5,103 | - | +| boxedAccountSizedInit1 | 5,066 | 🟢 **-37 (0.73%)** | | boxedAccountSized1 | 912 | - | -| boxedAccountSizedInit2 | 9,075 | - | -| boxedAccountSized2 | 1,337 | 🟢 **-18 (1.33%)** | -| boxedAccountSizedInit4 | 16,960 | 🟢 **-254 (1.48%)** | -| boxedAccountSized4 | 2,195 | 🟢 **-36 (1.61%)** | -| boxedAccountSizedInit8 | 32,831 | 🟢 **-690 (2.06%)** | -| boxedAccountSized8 | 3,935 | 🟢 **-72 (1.80%)** | -| boxedAccountUnsizedInit1 | 5,202 | - | -| boxedAccountUnsized1 | 943 | 🟢 **-21 (2.18%)** | -| boxedAccountUnsizedInit2 | 9,251 | - | -| boxedAccountUnsized2 | 1,392 | 🟢 **-42 (2.93%)** | -| boxedAccountUnsizedInit4 | 17,284 | 🟢 **-258 (1.47%)** | -| boxedAccountUnsized4 | 2,283 | 🟢 **-84 (3.55%)** | -| boxedAccountUnsizedInit8 | 33,455 | 🟢 **-698 (2.04%)** | -| boxedAccountUnsized8 | 4,089 | 🟢 **-168 (3.95%)** | -| boxedInterfaceAccountMint1 | 1,110 | - | -| boxedInterfaceAccountMint2 | 1,534 | - | -| boxedInterfaceAccountMint4 | 2,370 | - | -| boxedInterfaceAccountMint8 | 4,064 | - | -| boxedInterfaceAccountToken1 | 1,246 | - | -| boxedInterfaceAccountToken2 | 1,794 | - | -| boxedInterfaceAccountToken4 | 2,878 | - | -| boxedInterfaceAccountToken8 | 5,068 | - | -| interfaceAccountMint1 | 1,126 | - | -| interfaceAccountMint2 | 1,562 | - | -| interfaceAccountMint4 | 2,432 | - | -| interfaceAccountMint8 | 4,163 | - | -| interfaceAccountToken1 | 1,268 | - | -| interfaceAccountToken2 | 1,849 | - | -| interfaceAccountToken4 | 2,997 | - | +| boxedAccountSizedInit2 | 9,036 | 🟢 **-39 (0.43%)** | +| boxedAccountSized2 | 1,355 | - | +| boxedAccountSizedInit4 | 16,968 | 🟢 **-246 (1.43%)** | +| boxedAccountSized4 | 2,231 | - | +| boxedAccountSizedInit8 | 32,839 | 🟢 **-682 (2.03%)** | +| boxedAccountSized8 | 4,007 | - | +| boxedAccountUnsizedInit1 | 5,165 | 🟢 **-37 (0.71%)** | +| boxedAccountUnsized1 | 964 | - | +| boxedAccountUnsizedInit2 | 9,210 | 🟢 **-41 (0.44%)** | +| boxedAccountUnsized2 | 1,434 | - | +| boxedAccountUnsizedInit4 | 17,292 | 🟢 **-250 (1.43%)** | +| boxedAccountUnsized4 | 2,367 | - | +| boxedAccountUnsizedInit8 | 33,463 | 🟢 **-690 (2.02%)** | +| boxedAccountUnsized8 | 4,257 | - | +| boxedInterfaceAccountMint1 | 1,112 | 🔴 **+2 (0.18%)** | +| boxedInterfaceAccountMint2 | 1,538 | 🔴 **+4 (0.26%)** | +| boxedInterfaceAccountMint4 | 2,378 | 🔴 **+8 (0.34%)** | +| boxedInterfaceAccountMint8 | 4,080 | 🔴 **+16 (0.39%)** | +| boxedInterfaceAccountToken1 | 1,248 | 🔴 **+2 (0.16%)** | +| boxedInterfaceAccountToken2 | 1,798 | 🔴 **+4 (0.22%)** | +| boxedInterfaceAccountToken4 | 2,886 | 🔴 **+8 (0.28%)** | +| boxedInterfaceAccountToken8 | 5,084 | 🔴 **+16 (0.32%)** | +| interfaceAccountMint1 | 1,128 | 🔴 **+2 (0.18%)** | +| interfaceAccountMint2 | 1,566 | 🔴 **+4 (0.26%)** | +| interfaceAccountMint4 | 2,440 | 🔴 **+8 (0.33%)** | +| interfaceAccountMint8 | 4,179 | 🔴 **+16 (0.38%)** | +| interfaceAccountToken1 | 1,270 | 🔴 **+2 (0.16%)** | +| interfaceAccountToken2 | 1,853 | 🔴 **+4 (0.22%)** | +| interfaceAccountToken4 | 3,005 | 🔴 **+8 (0.27%)** | | interface1 | 878 | - | | interface2 | 1,023 | - | | interface4 | 1,301 | - | | interface8 | 1,867 | - | -| program1 | 890 | - | -| program2 | 1,035 | - | -| program4 | 1,313 | - | -| program8 | 1,879 | - | +| program1 | 898 | 🔴 **+8 (0.90%)** | +| program2 | 1,051 | 🔴 **+16 (1.55%)** | +| program4 | 1,345 | 🔴 **+32 (2.44%)** | +| program8 | 1,943 | 🔴 **+64 (3.41%)** | | signer1 | 874 | - | | signer2 | 1,173 | - | | signer4 | 1,759 | - | @@ -114,95 +114,95 @@ Solana version: 2.3.0 Solana version: 2.3.0 -| Instruction | Compute Units | - | -|------------------------------|---------------|------------------------| -| accountInfo1 | 685 | 🔴 **+114 (19.96%)** | -| accountInfo2 | 1,053 | 🔴 **+158 (17.65%)** | -| accountInfo4 | 1,750 | 🔴 **+197 (12.69%)** | -| accountInfo8 | 3,135 | 🔴 **+212 (7.25%)** | -| accountEmptyInit1 | 4,915 | 🔴 **+131 (2.74%)** | -| accountEmpty1 | 774 | 🔴 **+129 (20.00%)** | -| accountEmptyInit2 | 8,793 | 🔴 **+124 (1.43%)** | -| accountEmpty2 | 1,174 | 🔴 **+167 (16.58%)** | -| accountEmptyInit4 | 16,748 | 🔴 **+248 (1.50%)** | -| accountEmpty4 | 1,962 | 🔴 **+238 (13.81%)** | -| accountEmptyInit8 | 32,357 | 🔴 **+26 (0.08%)** | -| accountEmpty8 | 3,548 | 🔴 **+385 (12.17%)** | -| accountSizedInit1 | 5,019 | 🔴 **+126 (2.58%)** | -| accountSized1 | 822 | 🔴 **+129 (18.61%)** | -| accountSizedInit2 | 8,981 | 🔴 **+124 (1.40%)** | -| accountSized2 | 1,240 | 🔴 **+165 (15.35%)** | -| accountSizedInit4 | 17,154 | 🔴 **+248 (1.47%)** | -| accountSized4 | 2,082 | 🔴 **+234 (12.66%)** | -| accountSizedInit8 | 33,013 | 🟢 **-28 (0.08%)** | -| accountSized8 | 3,762 | 🔴 **+375 (11.07%)** | -| accountUnsizedInit1 | 5,127 | 🔴 **+121 (2.42%)** | -| accountUnsized1 | 874 | 🔴 **+128 (17.16%)** | -| accountUnsizedInit2 | 9,251 | 🔴 **+124 (1.36%)** | -| accountUnsized2 | 1,326 | 🔴 **+163 (14.02%)** | -| accountUnsizedInit4 | 17,378 | 🟢 **-29 (0.17%)** | -| accountUnsized4 | 2,231 | 🔴 **+229 (11.44%)** | -| accountUnsizedInit8 | 33,961 | 🔴 **+496 (1.48%)** | -| accountUnsized8 | 4,035 | 🔴 **+362 (9.86%)** | -| boxedAccountEmptyInit1 | 5,007 | 🔴 **+131 (2.69%)** | -| boxedAccountEmpty1 | 864 | 🔴 **+130 (17.71%)** | -| boxedAccountEmptyInit2 | 8,906 | 🔴 **+123 (1.40%)** | -| boxedAccountEmpty2 | 1,286 | 🔴 **+170 (15.23%)** | -| boxedAccountEmptyInit4 | 16,902 | 🔴 **+247 (1.48%)** | -| boxedAccountEmpty4 | 2,115 | 🔴 **+243 (12.98%)** | -| boxedAccountEmptyInit8 | 32,591 | 🔴 **+30 (0.09%)** | -| boxedAccountEmpty8 | 3,801 | 🔴 **+400 (11.76%)** | -| boxedAccountSizedInit1 | 5,103 | 🔴 **+131 (2.63%)** | -| boxedAccountSized1 | 912 | 🔴 **+129 (16.48%)** | -| boxedAccountSizedInit2 | 9,075 | 🔴 **+123 (1.37%)** | -| boxedAccountSized2 | 1,355 | 🔴 **+165 (13.87%)** | -| boxedAccountSizedInit4 | 17,214 | 🔴 **+247 (1.46%)** | -| boxedAccountSized4 | 2,231 | 🔴 **+235 (11.77%)** | -| boxedAccountSizedInit8 | 33,521 | 🔴 **+360 (1.09%)** | -| boxedAccountSized8 | 4,007 | 🔴 **+379 (10.45%)** | -| boxedAccountUnsizedInit1 | 5,202 | 🔴 **+130 (2.56%)** | -| boxedAccountUnsized1 | 964 | 🔴 **+128 (15.31%)** | -| boxedAccountUnsizedInit2 | 9,251 | 🔴 **+123 (1.35%)** | -| boxedAccountUnsized2 | 1,434 | 🔴 **+164 (12.91%)** | -| boxedAccountUnsizedInit4 | 17,542 | 🔴 **+247 (1.43%)** | -| boxedAccountUnsized4 | 2,367 | 🔴 **+235 (11.02%)** | -| boxedAccountUnsizedInit8 | 34,153 | 🔴 **+360 (1.07%)** | -| boxedAccountUnsized8 | 4,257 | 🔴 **+376 (9.69%)** | -| boxedInterfaceAccountMint1 | 1,110 | 🟢 **-241 (17.84%)** | -| boxedInterfaceAccountMint2 | 1,534 | 🟢 **-589 (27.74%)** | -| boxedInterfaceAccountMint4 | 2,370 | 🟢 **-1,286 (35.18%)** | -| boxedInterfaceAccountMint8 | 4,064 | 🟢 **-2,674 (39.69%)** | -| boxedInterfaceAccountToken1 | 1,246 | 🟢 **-765 (38.04%)** | -| boxedInterfaceAccountToken2 | 1,794 | 🟢 **-1,637 (47.71%)** | -| boxedInterfaceAccountToken4 | 2,878 | 🟢 **-3,382 (54.03%)** | -| boxedInterfaceAccountToken8 | 5,068 | 🟢 **-6,866 (57.53%)** | -| interfaceAccountMint1 | 1,126 | 🟢 **-350 (23.71%)** | -| interfaceAccountMint2 | 1,562 | 🟢 **-927 (37.24%)** | -| interfaceAccountMint4 | 2,432 | 🟢 **-2,079 (46.09%)** | -| interfaceAccountMint8 | 4,163 | 🟢 **-4,387 (51.31%)** | -| interfaceAccountToken1 | 1,268 | 🟢 **-843 (39.93%)** | -| interfaceAccountToken2 | 1,849 | 🟢 **-1,880 (50.42%)** | -| interfaceAccountToken4 | 2,997 | 🟢 **-3,958 (56.91%)** | -| interface1 | 878 | 🔴 **+109 (14.17%)** | -| interface2 | 1,023 | 🔴 **+111 (12.17%)** | -| interface4 | 1,301 | 🔴 **+112 (9.42%)** | -| interface8 | 1,867 | 🔴 **+119 (6.81%)** | -| program1 | 890 | 🔴 **+111 (14.25%)** | -| program2 | 1,035 | 🔴 **+115 (12.50%)** | -| program4 | 1,313 | 🔴 **+120 (10.06%)** | -| program8 | 1,879 | 🔴 **+135 (7.74%)** | -| signer1 | 874 | 🔴 **+100 (12.92%)** | -| signer2 | 1,173 | 🔴 **+109 (10.24%)** | -| signer4 | 1,759 | 🔴 **+122 (7.45%)** | -| signer8 | 2,941 | 🔴 **+153 (5.49%)** | -| systemAccount1 | 911 | 🔴 **+115 (14.45%)** | -| systemAccount2 | 1,235 | 🔴 **+139 (12.68%)** | -| systemAccount4 | 1,871 | 🔴 **+182 (10.78%)** | -| systemAccount8 | 3,153 | 🔴 **+273 (9.48%)** | -| uncheckedAccount1 | 882 | 🔴 **+99 (12.64%)** | -| uncheckedAccount2 | 1,162 | 🔴 **+106 (10.04%)** | -| uncheckedAccount4 | 1,716 | 🔴 **+122 (7.65%)** | -| uncheckedAccount8 | 2,833 | 🔴 **+154 (5.75%)** | +| Instruction | Compute Units | - | +| --------------------------- | ------------- | ---------------------- | +| accountInfo1 | 685 | 🔴 **+114 (19.96%)** | +| accountInfo2 | 1,053 | 🔴 **+158 (17.65%)** | +| accountInfo4 | 1,750 | 🔴 **+197 (12.69%)** | +| accountInfo8 | 3,135 | 🔴 **+212 (7.25%)** | +| accountEmptyInit1 | 4,915 | 🔴 **+131 (2.74%)** | +| accountEmpty1 | 774 | 🔴 **+129 (20.00%)** | +| accountEmptyInit2 | 8,793 | 🔴 **+124 (1.43%)** | +| accountEmpty2 | 1,174 | 🔴 **+167 (16.58%)** | +| accountEmptyInit4 | 16,748 | 🔴 **+248 (1.50%)** | +| accountEmpty4 | 1,962 | 🔴 **+238 (13.81%)** | +| accountEmptyInit8 | 32,357 | 🔴 **+26 (0.08%)** | +| accountEmpty8 | 3,548 | 🔴 **+385 (12.17%)** | +| accountSizedInit1 | 5,019 | 🔴 **+126 (2.58%)** | +| accountSized1 | 822 | 🔴 **+129 (18.61%)** | +| accountSizedInit2 | 8,981 | 🔴 **+124 (1.40%)** | +| accountSized2 | 1,240 | 🔴 **+165 (15.35%)** | +| accountSizedInit4 | 17,154 | 🔴 **+248 (1.47%)** | +| accountSized4 | 2,082 | 🔴 **+234 (12.66%)** | +| accountSizedInit8 | 33,013 | 🟢 **-28 (0.08%)** | +| accountSized8 | 3,762 | 🔴 **+375 (11.07%)** | +| accountUnsizedInit1 | 5,127 | 🔴 **+121 (2.42%)** | +| accountUnsized1 | 874 | 🔴 **+128 (17.16%)** | +| accountUnsizedInit2 | 9,251 | 🔴 **+124 (1.36%)** | +| accountUnsized2 | 1,326 | 🔴 **+163 (14.02%)** | +| accountUnsizedInit4 | 17,378 | 🟢 **-29 (0.17%)** | +| accountUnsized4 | 2,231 | 🔴 **+229 (11.44%)** | +| accountUnsizedInit8 | 33,961 | 🔴 **+496 (1.48%)** | +| accountUnsized8 | 4,035 | 🔴 **+362 (9.86%)** | +| boxedAccountEmptyInit1 | 5,007 | 🔴 **+131 (2.69%)** | +| boxedAccountEmpty1 | 864 | 🔴 **+130 (17.71%)** | +| boxedAccountEmptyInit2 | 8,906 | 🔴 **+123 (1.40%)** | +| boxedAccountEmpty2 | 1,286 | 🔴 **+170 (15.23%)** | +| boxedAccountEmptyInit4 | 16,902 | 🔴 **+247 (1.48%)** | +| boxedAccountEmpty4 | 2,115 | 🔴 **+243 (12.98%)** | +| boxedAccountEmptyInit8 | 32,591 | 🔴 **+30 (0.09%)** | +| boxedAccountEmpty8 | 3,801 | 🔴 **+400 (11.76%)** | +| boxedAccountSizedInit1 | 5,103 | 🔴 **+131 (2.63%)** | +| boxedAccountSized1 | 912 | 🔴 **+129 (16.48%)** | +| boxedAccountSizedInit2 | 9,075 | 🔴 **+123 (1.37%)** | +| boxedAccountSized2 | 1,355 | 🔴 **+165 (13.87%)** | +| boxedAccountSizedInit4 | 17,214 | 🔴 **+247 (1.46%)** | +| boxedAccountSized4 | 2,231 | 🔴 **+235 (11.77%)** | +| boxedAccountSizedInit8 | 33,521 | 🔴 **+360 (1.09%)** | +| boxedAccountSized8 | 4,007 | 🔴 **+379 (10.45%)** | +| boxedAccountUnsizedInit1 | 5,202 | 🔴 **+130 (2.56%)** | +| boxedAccountUnsized1 | 964 | 🔴 **+128 (15.31%)** | +| boxedAccountUnsizedInit2 | 9,251 | 🔴 **+123 (1.35%)** | +| boxedAccountUnsized2 | 1,434 | 🔴 **+164 (12.91%)** | +| boxedAccountUnsizedInit4 | 17,542 | 🔴 **+247 (1.43%)** | +| boxedAccountUnsized4 | 2,367 | 🔴 **+235 (11.02%)** | +| boxedAccountUnsizedInit8 | 34,153 | 🔴 **+360 (1.07%)** | +| boxedAccountUnsized8 | 4,257 | 🔴 **+376 (9.69%)** | +| boxedInterfaceAccountMint1 | 1,110 | 🟢 **-241 (17.84%)** | +| boxedInterfaceAccountMint2 | 1,534 | 🟢 **-589 (27.74%)** | +| boxedInterfaceAccountMint4 | 2,370 | 🟢 **-1,286 (35.18%)** | +| boxedInterfaceAccountMint8 | 4,064 | 🟢 **-2,674 (39.69%)** | +| boxedInterfaceAccountToken1 | 1,246 | 🟢 **-765 (38.04%)** | +| boxedInterfaceAccountToken2 | 1,794 | 🟢 **-1,637 (47.71%)** | +| boxedInterfaceAccountToken4 | 2,878 | 🟢 **-3,382 (54.03%)** | +| boxedInterfaceAccountToken8 | 5,068 | 🟢 **-6,866 (57.53%)** | +| interfaceAccountMint1 | 1,126 | 🟢 **-350 (23.71%)** | +| interfaceAccountMint2 | 1,562 | 🟢 **-927 (37.24%)** | +| interfaceAccountMint4 | 2,432 | 🟢 **-2,079 (46.09%)** | +| interfaceAccountMint8 | 4,163 | 🟢 **-4,387 (51.31%)** | +| interfaceAccountToken1 | 1,268 | 🟢 **-843 (39.93%)** | +| interfaceAccountToken2 | 1,849 | 🟢 **-1,880 (50.42%)** | +| interfaceAccountToken4 | 2,997 | 🟢 **-3,958 (56.91%)** | +| interface1 | 878 | 🔴 **+109 (14.17%)** | +| interface2 | 1,023 | 🔴 **+111 (12.17%)** | +| interface4 | 1,301 | 🔴 **+112 (9.42%)** | +| interface8 | 1,867 | 🔴 **+119 (6.81%)** | +| program1 | 890 | 🔴 **+111 (14.25%)** | +| program2 | 1,035 | 🔴 **+101 (10.81%)** | +| program4 | 1,313 | 🔴 **+92 (7.53%)** | +| program8 | 1,879 | 🔴 **+79 (4.39%)** | +| signer1 | 874 | 🔴 **+100 (12.92%)** | +| signer2 | 1,173 | 🔴 **+109 (10.24%)** | +| signer4 | 1,759 | 🔴 **+122 (7.45%)** | +| signer8 | 2,941 | 🔴 **+153 (5.49%)** | +| systemAccount1 | 911 | 🔴 **+115 (14.45%)** | +| systemAccount2 | 1,235 | 🔴 **+139 (12.68%)** | +| systemAccount4 | 1,871 | 🔴 **+182 (10.78%)** | +| systemAccount8 | 3,153 | 🔴 **+273 (9.48%)** | +| uncheckedAccount1 | 882 | 🔴 **+99 (12.64%)** | +| uncheckedAccount2 | 1,162 | 🔴 **+106 (10.04%)** | +| uncheckedAccount4 | 1,716 | 🔴 **+122 (7.65%)** | +| uncheckedAccount8 | 2,833 | 🔴 **+154 (5.75%)** | ### Notable changes diff --git a/cli/Cargo.toml b/cli/Cargo.toml index be28b97b4a..8fae49baa9 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -25,23 +25,36 @@ cargo_toml = "0.19.2" chrono = "0.4.19" clap = { version = "4.5.17", features = ["derive"] } clap_complete = "4.5.26" +console = "0.15" dirs = "4.0" +ed25519-dalek = "2" flate2 = "1.0.19" heck = "0.4.0" pathdiff = "0.2.0" portpicker = "0.1.1" regex = "1.8.3" reqwest = { version = "0.11.4", default-features = false, features = ["multipart", "blocking", "rustls-tls"] } +rustls-webpki = "0.102" semver = "1.0.4" serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0" shellexpand = "2.1.0" solana-cli-config = "2" +solana-client = "2" solana-faucet = "2" +solana-loader-v3-interface = { version = "5", features = ["bincode"] } +solana-packet = "2" +solana-pubsub-client = "2" solana-rpc-client = "2" +solana-rpc-client-api = "2" solana-sdk = "2" +solana-sdk-ids = "2.2" solana-signer.workspace = true syn = { version = "1.0.60", features = ["full", "extra-traits"] } tar = "0.4.35" +tiny-bip39 = "2.0" toml = "0.7.6" walkdir = "2.3.2" + +[dev-dependencies] +tempfile = "3" diff --git a/cli/src/account.rs b/cli/src/account.rs new file mode 100644 index 0000000000..c1423875d2 --- /dev/null +++ b/cli/src/account.rs @@ -0,0 +1,156 @@ +use anyhow::{anyhow, Result}; +use clap::Parser; +use solana_rpc_client::rpc_client::RpcClient; +use solana_sdk::commitment_config::CommitmentConfig; +use solana_sdk::pubkey::Pubkey; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +use crate::{ + config::{Config, ConfigOverride}, + DEFAULT_MAINNET_RPC_URL, +}; + +#[derive(Debug, Parser)] +pub struct ShowAccountCommand { + /// Account address to show + pub account_address: Pubkey, + /// Display balance in lamports instead of SOL + #[clap(long)] + pub lamports: bool, + /// Write the account data to this file + #[clap(short = 'o', long)] + pub output_file: Option, + /// Return information in specified output format + #[clap(long, value_parser = ["json", "json-compact"])] + pub output: Option, +} + +pub fn show_account(cfg_override: &ConfigOverride, cmd: ShowAccountCommand) -> Result<()> { + let config = Config::discover(cfg_override)?; + let url = match config { + Some(ref cfg) => cfg.provider.cluster.url().to_string(), + None => { + // If not in workspace, use cluster override or default to localhost + if let Some(ref cluster) = cfg_override.cluster { + cluster.url().to_string() + } else { + DEFAULT_MAINNET_RPC_URL.to_string() + } + } + }; + + let rpc_client = RpcClient::new_with_commitment(url, CommitmentConfig::confirmed()); + + // Fetch the account + let account = rpc_client + .get_account(&cmd.account_address) + .map_err(|e| anyhow!("Unable to fetch account {}: {}", cmd.account_address, e))?; + + // Handle JSON output + if let Some(format) = cmd.output { + use base64::{engine::general_purpose::STANDARD, Engine}; + + let json_output = serde_json::json!({ + "pubkey": cmd.account_address.to_string(), + "account": { + "lamports": account.lamports, + "owner": account.owner.to_string(), + "executable": account.executable, + "rentEpoch": account.rent_epoch, + "data": STANDARD.encode(&account.data), + } + }); + + let output_str = match format.as_str() { + "json" => serde_json::to_string_pretty(&json_output)?, + "json-compact" => serde_json::to_string(&json_output)?, + _ => unreachable!(), + }; + + if let Some(output_file) = cmd.output_file { + let mut file = File::create(&output_file)?; + file.write_all(output_str.as_bytes())?; + println!("Wrote account to {}", output_file.display()); + } else { + println!("{}", output_str); + } + + return Ok(()); + } + + // Text output + println!("Public Key: {}", cmd.account_address); + + if cmd.lamports { + println!("Balance: {} lamports", account.lamports); + } else { + println!("Balance: {} SOL", account.lamports as f64 / 1_000_000_000.0); + } + + println!("Owner: {}", account.owner); + println!("Executable: {}", account.executable); + println!("Rent Epoch: {}", account.rent_epoch); + + // Display account data + let data_len = account.data.len(); + println!("Length: {} (0x{:x}) bytes", data_len, data_len); + + if !account.data.is_empty() { + // Write to output file if specified + if let Some(output_file) = cmd.output_file { + let mut file = File::create(&output_file)?; + file.write_all(&account.data)?; + println!("Wrote account data to {}", output_file.display()); + } + + // Display hex dump + print_hex_dump(&account.data); + } + + Ok(()) +} + +fn print_hex_dump(data: &[u8]) { + const BYTES_PER_LINE: usize = 16; + + for (i, chunk) in data.chunks(BYTES_PER_LINE).enumerate() { + let offset = i * BYTES_PER_LINE; + + // Print offset + print!("{:04x}: ", offset); + + // Print hex values + for (j, byte) in chunk.iter().enumerate() { + if j > 0 && j % 4 == 0 { + print!(" "); + } + print!("{:02x} ", byte); + } + + // Pad if this is the last line and it's not complete + if chunk.len() < BYTES_PER_LINE { + for j in chunk.len()..BYTES_PER_LINE { + if j > 0 && j % 4 == 0 { + print!(" "); + } + print!(" "); + } + } + + print!(" "); + + // Print ASCII representation + for byte in chunk { + let c = *byte as char; + if c.is_ascii_graphic() || c == ' ' { + print!("{}", c); + } else { + print!("."); + } + } + + println!(); + } +} diff --git a/cli/src/config.rs b/cli/src/config.rs index 05b8929a2b..bf9750d191 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -11,6 +11,7 @@ use serde::ser::SerializeMap; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use solana_cli_config::{Config as SolanaConfig, CONFIG_FILE}; use solana_sdk::clock::Slot; +use solana_sdk::commitment_config::CommitmentLevel; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Keypair; use solana_signer::Signer; @@ -27,6 +28,32 @@ use std::str::FromStr; use std::{fmt, io}; use walkdir::WalkDir; +/// Wrapper around CommitmentLevel to support case-insensitive parsing +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CaseInsensitiveCommitmentLevel(pub CommitmentLevel); + +impl FromStr for CaseInsensitiveCommitmentLevel { + type Err = String; + + fn from_str(s: &str) -> Result { + // Convert to lowercase for case-insensitive matching + let lowercase = s.to_lowercase(); + let commitment = CommitmentLevel::from_str(&lowercase).map_err(|_| { + format!( + "Invalid commitment level '{}'. Valid values are: processed, confirmed, finalized", + s + ) + })?; + Ok(CaseInsensitiveCommitmentLevel(commitment)) + } +} + +impl From for CommitmentLevel { + fn from(val: CaseInsensitiveCommitmentLevel) -> Self { + val.0 + } +} + pub trait Merge: Sized { fn merge(&mut self, _other: Self) {} } @@ -39,6 +66,9 @@ pub struct ConfigOverride { /// Wallet override. #[clap(global = true, long = "provider.wallet")] pub wallet: Option, + /// Commitment override (valid values: processed, confirmed, finalized). + #[clap(global = true, long = "commitment")] + pub commitment: Option, } #[derive(Debug)] @@ -1472,6 +1502,109 @@ macro_rules! home_path { home_path!(WalletPath, ".config/solana/id.json"); +/// Config command enum for managing Anchor.toml settings +#[derive(Debug, Parser)] +pub enum ConfigCommand { + /// Get configuration settings from the local Anchor.toml + Get, + /// Set configuration settings in the local Anchor.toml + Set { + /// Cluster to connect to (custom URL). Use -um, -ud, -ut, -ul for standard clusters + #[clap(short = 'u', long = "url")] + url: Option, + /// Path to wallet keypair + #[clap(short = 'k', long = "keypair")] + keypair: Option, + }, +} + +/// Handle config commands (get/set) +pub fn handle_config_command(cfg_override: &ConfigOverride, cmd: ConfigCommand) -> Result<()> { + match cmd { + ConfigCommand::Get => config_get(cfg_override), + ConfigCommand::Set { url, keypair } => { + // Parse the url field to handle both full URLs and monikers (-um, -ud, -ut, -ul) + let cluster = url.map(|u| { + // Check if it's a moniker (single letter) + match u.as_str() { + "m" => "mainnet".to_string(), + "d" => "devnet".to_string(), + "t" => "testnet".to_string(), + "l" => "localnet".to_string(), + _ => u, // Otherwise use as-is + } + }); + + config_set(cfg_override, cluster, keypair) + } + } +} + +/// Get and display configuration settings +fn config_get(_cfg_override: &ConfigOverride) -> Result<()> { + // Use empty override to avoid applying any overrides when reading config + let cfg = Config::discover(&ConfigOverride::default())? + .ok_or_else(|| anyhow!("Not in an Anchor workspace"))?; + + // Show all settings + println!("Config File: {}", cfg.path().display()); + println!(); + println!("Cluster: {}", cfg.provider.cluster); + println!("Wallet: {}", cfg.provider.wallet); + + Ok(()) +} + +/// Set configuration settings in Anchor.toml +fn config_set( + _cfg_override: &ConfigOverride, + cluster: Option, + wallet: Option, +) -> Result<()> { + if cluster.is_none() && wallet.is_none() { + return Err(anyhow!( + "At least one setting must be provided (--url or --keypair)" + )); + } + + // Update local Anchor.toml + // Use empty override to avoid applying any overrides when reading config + let mut cfg = Config::discover(&ConfigOverride::default())? + .ok_or_else(|| anyhow!("Not in an Anchor workspace"))?; + + let mut updated = false; + + if let Some(c) = cluster { + cfg.provider.cluster = c + .parse::() + .with_context(|| format!("Invalid cluster: {}", c))?; + println!("Cluster set to: {}", cfg.provider.cluster); + updated = true; + } + + if let Some(w) = wallet { + let expanded_path = shellexpand::tilde(&w).to_string(); + let path = PathBuf::from(&expanded_path); + + if !path.exists() { + eprintln!("Warning: Wallet file does not exist: {}", path.display()); + } + + cfg.provider.wallet = expanded_path.parse()?; + println!("Wallet set to: {}", w); + updated = true; + } + + if updated { + // Save the updated config + let config_path = cfg.path(); + fs::write(config_path, cfg.to_string()) + .with_context(|| format!("Failed to write config to {}", config_path.display()))?; + } + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/cli/src/keygen.rs b/cli/src/keygen.rs new file mode 100644 index 0000000000..b4dc63d67a --- /dev/null +++ b/cli/src/keygen.rs @@ -0,0 +1,553 @@ +use std::{ + fs, + io::{self, Write}, + path::Path, +}; + +use anyhow::{anyhow, bail, Result}; +use bip39::{Language, Mnemonic, MnemonicType, Seed}; +use console::{Key, Term}; +use dirs::home_dir; +use ed25519_dalek::SigningKey; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + message::Message, + pubkey::Pubkey, + signature::{Keypair, Signer}, + signer::EncodableKey, +}; + +use crate::{config::ConfigOverride, get_keypair, KeygenCommand, DEFAULT_WALLET_PATH}; + +/// Secure password input with asterisk visual feedback +/// - show_spaces: if true, spaces are visible (for seed phrases); if false, all characters are asterisks (for passphrases) +fn secure_input(prompt: &str, show_spaces: bool) -> Result { + print!("{}", prompt); + io::stdout().flush()?; + + let term = Term::stdout(); + let mut input = String::new(); + + loop { + let key = term.read_key()?; + match key { + Key::Enter => { + println!(); + break; + } + Key::Backspace => { + if !input.is_empty() { + input.pop(); + // Move cursor back, print space, move cursor back again + print!("\x08 \x08"); + io::stdout().flush()?; + } + } + Key::Char(c) => { + input.push(c); + // Display spaces as spaces if show_spaces is true, otherwise all asterisks + if show_spaces && c == ' ' { + print!(" "); + } else { + print!("*"); + } + io::stdout().flush()?; + } + Key::Escape => { + println!(); + bail!("Input cancelled"); + } + _ => {} + } + } + + Ok(input) +} + +/// Print a progress step with checkmark +fn print_step(step: &str) { + println!("✓ {}", step); +} + +/// Get the default wallet path from the home directory +pub fn get_default_wallet_path() -> Result { + let Some(home) = home_dir() else { + bail!("Unable to determine home directory for default wallet path"); + }; + + Ok(home.join(DEFAULT_WALLET_PATH).to_string_lossy().to_string()) +} + +pub fn keygen(_cfg_override: &ConfigOverride, cmd: KeygenCommand) -> Result<()> { + match cmd { + KeygenCommand::New { + outfile, + force, + no_passphrase, + silent, + word_count, + } => keygen_new(outfile, force, no_passphrase, silent, word_count), + KeygenCommand::Pubkey { keypair } => keygen_pubkey(keypair), + KeygenCommand::Recover { + outfile, + force, + skip_seed_phrase_validation, + no_passphrase, + } => keygen_recover(outfile, force, skip_seed_phrase_validation, no_passphrase), + KeygenCommand::Verify { pubkey, keypair } => keygen_verify(pubkey, keypair), + } +} + +fn keygen_new( + outfile: Option, + force: bool, + no_passphrase: bool, + silent: bool, + word_count: usize, +) -> Result<()> { + // Determine output file path + let outfile_path = match outfile { + Some(path) => path, + None => get_default_wallet_path()?, + }; + + // Check for overwrite + if Path::new(&outfile_path).exists() { + if !force { + bail!( + "Refusing to overwrite {} without --force flag", + outfile_path + ); + } + println!( + "⚠️ Warning: Overwriting existing keypair at {}", + outfile_path + ); + } + + println!("\n🔑 Generating a new keypair"); + println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); + + // Convert word count to MnemonicType + let mnemonic_type = match word_count { + 12 => MnemonicType::Words12, + 15 => MnemonicType::Words15, + 18 => MnemonicType::Words18, + 21 => MnemonicType::Words21, + 24 => MnemonicType::Words24, + _ => bail!( + "Invalid word count: {}. Must be 12, 15, 18, 21, or 24", + word_count + ), + }; + + // Generate mnemonic with specified word count + print_step(&format!("Generating {}-word mnemonic", word_count)); + let mnemonic = Mnemonic::new(mnemonic_type, Language::English); + + // Get passphrase + let passphrase = if no_passphrase { + print_step("No passphrase required"); + String::new() + } else { + println!("\n🔐 BIP39 Passphrase (optional)"); + let pass = secure_input("Enter BIP39 passphrase (leave empty for none): ", false)?; + if !pass.is_empty() { + print_step("Passphrase set"); + } + pass + }; + + // Generate seed from mnemonic and passphrase + print_step("Deriving keypair from seed"); + let seed = Seed::new(&mnemonic, &passphrase); + + // Create keypair from seed (use first 32 bytes as secret key) + // Ed25519 keypair derivation: use the first 32 bytes of the seed as the secret key + let secret_key_bytes: [u8; 32] = seed.as_bytes()[0..32].try_into().unwrap(); + let signing_key = SigningKey::from_bytes(&secret_key_bytes); + + // Convert to solana Keypair (64 bytes: 32 secret + 32 public) + let verifying_key_bytes = signing_key.verifying_key().to_bytes(); + let mut keypair_bytes = [0u8; 64]; + keypair_bytes[..32].copy_from_slice(&secret_key_bytes); + keypair_bytes[32..].copy_from_slice(&verifying_key_bytes); + let keypair = Keypair::from_bytes(&keypair_bytes)?; + + // Write keypair to file + if let Some(outdir) = Path::new(&outfile_path).parent() { + fs::create_dir_all(outdir)?; + } + keypair + .write_to_file(&outfile_path) + .map_err(|e| anyhow!("Failed to write keypair to {}: {}", outfile_path, e))?; + + // Set restrictive permissions (owner read/write only) + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let mut perms = fs::metadata(&outfile_path)?.permissions(); + perms.set_mode(0o600); + fs::set_permissions(&outfile_path, perms)?; + } + + print_step(&format!("Keypair saved to {}", outfile_path)); + + let phrase: &str = mnemonic.phrase(); + let divider = "━".repeat(phrase.len().max(60)); + let passphrase_msg = if passphrase.is_empty() { + String::new() + } else { + " and your BIP39 passphrase".to_string() + }; + + // Always show the seed phrase - it's critical for recovery + println!("\n{}", divider); + if !silent { + println!("📋 Public Key: {}", keypair.pubkey()); + println!("{}", divider); + } + println!( + "\n⚠️ IMPORTANT: Save this seed phrase{} to recover your keypair:", + passphrase_msg + ); + println!("\n{}\n", phrase); + println!("{}", divider); + + Ok(()) +} + +fn keygen_pubkey(keypair_path: Option) -> Result<()> { + let path = match keypair_path { + Some(path) => path, + None => get_default_wallet_path()?, + }; + + let keypair = get_keypair(&path)?; + println!("{}", keypair.pubkey()); + Ok(()) +} + +fn keygen_recover( + outfile: Option, + force: bool, + _skip_seed_phrase_validation: bool, + no_passphrase: bool, +) -> Result<()> { + println!("\n🔓 Recover keypair from seed phrase"); + println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); + + // Determine output file path + let outfile_path = match outfile { + Some(path) => path, + None => get_default_wallet_path()?, + }; + + // Check for overwrite + if Path::new(&outfile_path).exists() { + if !force { + bail!( + "Refusing to overwrite {} without --force flag", + outfile_path + ); + } + println!( + "⚠️ Warning: Overwriting existing keypair at {}", + outfile_path + ); + } + + // Prompt for seed phrase (secure input with spaces visible) + println!("\n🌱 Enter Recovery Seed Phrase"); + let seed_phrase = secure_input("Seed phrase: ", true)?; + + // Parse mnemonic from seed phrase + let mnemonic = Mnemonic::from_phrase(&seed_phrase, Language::English) + .map_err(|e| anyhow!("Invalid seed phrase: {:?}", e))?; + print_step("Seed phrase validated"); + + // Get passphrase + let passphrase = if no_passphrase { + print_step("No passphrase required"); + String::new() + } else { + println!("\n🔐 BIP39 Passphrase (optional)"); + let pass = secure_input("Passphrase (leave empty for none): ", false)?; + if !pass.is_empty() { + print_step("Passphrase accepted"); + } + pass + }; + + // Generate seed from mnemonic and passphrase + print_step("Deriving keypair from seed"); + let seed = Seed::new(&mnemonic, &passphrase); + + // Create keypair from seed (use first 32 bytes as secret key) + let secret_key_bytes: [u8; 32] = seed.as_bytes()[0..32].try_into().unwrap(); + let signing_key = SigningKey::from_bytes(&secret_key_bytes); + + // Convert to solana Keypair (64 bytes: 32 secret + 32 public) + let verifying_key_bytes = signing_key.verifying_key().to_bytes(); + let mut keypair_bytes = [0u8; 64]; + keypair_bytes[..32].copy_from_slice(&secret_key_bytes); + keypair_bytes[32..].copy_from_slice(&verifying_key_bytes); + let keypair = Keypair::from_bytes(&keypair_bytes)?; + + // Write keypair to file + if let Some(outdir) = Path::new(&outfile_path).parent() { + fs::create_dir_all(outdir)?; + } + keypair + .write_to_file(&outfile_path) + .map_err(|e| anyhow!("Failed to write keypair to {}: {}", outfile_path, e))?; + + // Set restrictive permissions (owner read/write only) + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let mut perms = fs::metadata(&outfile_path)?.permissions(); + perms.set_mode(0o600); + fs::set_permissions(&outfile_path, perms)?; + } + + print_step(&format!("Keypair recovered to {}", outfile_path)); + + println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); + println!("📋 Public Key: {}", keypair.pubkey()); + println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"); + + Ok(()) +} + +fn keygen_verify(pubkey: Pubkey, keypair_path: Option) -> Result<()> { + println!("\n🔍 Verifying keypair"); + println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); + + let path = match keypair_path { + Some(path) => path, + None => get_default_wallet_path()?, + }; + + print_step(&format!("Loading keypair from {}", path)); + let keypair = get_keypair(&path)?; + + // Create a simple message to sign + print_step("Creating test message"); + let message = Message::new( + &[Instruction::new_with_bincode( + Pubkey::default(), + &0, + vec![AccountMeta::new(keypair.pubkey(), true)], + )], + Some(&keypair.pubkey()), + ); + + // Sign the message + print_step("Signing message with keypair"); + let signature = keypair.sign_message(message.serialize().as_slice()); + + // Verify the signature + print_step("Verifying signature"); + if signature.verify(pubkey.as_ref(), message.serialize().as_slice()) { + println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); + println!("✅ Verification Success"); + println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); + println!("Public key {} matches the keypair\n", pubkey); + Ok(()) + } else { + println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); + println!("❌ Verification Failed"); + println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); + bail!("Public key {} does not match the keypair", pubkey); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::{tempdir, TempDir}; + + fn tmp_outfile_path(out_dir: &TempDir, name: &str) -> String { + let path = out_dir.path().join(name); + path.into_os_string().into_string().unwrap() + } + + fn read_keypair_file(path: &str) -> Result { + get_keypair(path) + } + + #[test] + fn test_keygen_new() { + let outfile_dir = tempdir().unwrap(); + let outfile_path = tmp_outfile_path(&outfile_dir, "test-keypair.json"); + + // Test: successful keypair generation with default word count (12) + keygen_new(Some(outfile_path.clone()), false, true, true, 12).unwrap(); + + // Verify the keypair file was created + assert!(Path::new(&outfile_path).exists()); + + // Verify we can read the keypair back + let keypair = read_keypair_file(&outfile_path).unwrap(); + assert_ne!(keypair.pubkey(), Pubkey::default()); + + // Test: refuse to overwrite without --force + let result = keygen_new(Some(outfile_path.clone()), false, true, true, 12); + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains("Refusing to overwrite")); + + // Test: overwrite with --force flag + keygen_new(Some(outfile_path.clone()), true, true, true, 12).unwrap(); + assert!(Path::new(&outfile_path).exists()); + } + + #[test] + fn test_keygen_pubkey() { + let keypair_dir = tempdir().unwrap(); + let keypair_path = tmp_outfile_path(&keypair_dir, "test-keypair.json"); + + // Create a test keypair + let test_keypair = Keypair::new(); + test_keypair.write_to_file(&keypair_path).unwrap(); + + // Test: reading pubkey from file + let result = keygen_pubkey(Some(keypair_path)); + // Since keygen_pubkey prints to stdout, we just verify it doesn't error + assert!(result.is_ok()); + } + + #[test] + fn test_keygen_verify() { + let keypair_dir = tempdir().unwrap(); + let keypair_path = tmp_outfile_path(&keypair_dir, "test-keypair.json"); + + // Create a test keypair + let test_keypair = Keypair::new(); + test_keypair.write_to_file(&keypair_path).unwrap(); + let correct_pubkey = test_keypair.pubkey(); + + // Test: verify with correct pubkey + let result = keygen_verify(correct_pubkey, Some(keypair_path.clone())); + assert!(result.is_ok()); + + // Test: verify with incorrect pubkey + let incorrect_pubkey = Pubkey::new_unique(); + let result = keygen_verify(incorrect_pubkey, Some(keypair_path)); + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains(&format!( + "Public key {} does not match the keypair", + incorrect_pubkey + ))); + } + + #[test] + fn test_keypair_from_seed_consistency() { + // Test that the same seed phrase produces the same keypair + let test_phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; + + let mnemonic = Mnemonic::from_phrase(test_phrase, Language::English).unwrap(); + let seed1 = Seed::new(&mnemonic, ""); + + let secret_key_bytes1: [u8; 32] = seed1.as_bytes()[0..32].try_into().unwrap(); + let signing_key1 = SigningKey::from_bytes(&secret_key_bytes1); + let verifying_key_bytes1 = signing_key1.verifying_key().to_bytes(); + let mut keypair_bytes1 = [0u8; 64]; + keypair_bytes1[..32].copy_from_slice(&secret_key_bytes1); + keypair_bytes1[32..].copy_from_slice(&verifying_key_bytes1); + let keypair1 = Keypair::from_bytes(&keypair_bytes1).unwrap(); + + // Generate again with same phrase + let mnemonic2 = Mnemonic::from_phrase(test_phrase, Language::English).unwrap(); + let seed2 = Seed::new(&mnemonic2, ""); + + let secret_key_bytes2: [u8; 32] = seed2.as_bytes()[0..32].try_into().unwrap(); + let signing_key2 = SigningKey::from_bytes(&secret_key_bytes2); + let verifying_key_bytes2 = signing_key2.verifying_key().to_bytes(); + let mut keypair_bytes2 = [0u8; 64]; + keypair_bytes2[..32].copy_from_slice(&secret_key_bytes2); + keypair_bytes2[32..].copy_from_slice(&verifying_key_bytes2); + let keypair2 = Keypair::from_bytes(&keypair_bytes2).unwrap(); + + // Should produce the same pubkey + assert_eq!(keypair1.pubkey(), keypair2.pubkey()); + assert_eq!(keypair1.to_bytes(), keypair2.to_bytes()); + } + + #[test] + fn test_keypair_with_passphrase() { + // Test that different passphrases produce different keypairs + let test_phrase = + "park remain person kitchen mule spell knee armed position rail grid ankle"; + + let mnemonic = Mnemonic::from_phrase(test_phrase, Language::English).unwrap(); + + // Without passphrase + let seed_no_pass = Seed::new(&mnemonic, ""); + let secret_key_bytes_no_pass: [u8; 32] = seed_no_pass.as_bytes()[0..32].try_into().unwrap(); + let signing_key_no_pass = SigningKey::from_bytes(&secret_key_bytes_no_pass); + let verifying_key_bytes_no_pass = signing_key_no_pass.verifying_key().to_bytes(); + let mut keypair_bytes_no_pass = [0u8; 64]; + keypair_bytes_no_pass[..32].copy_from_slice(&secret_key_bytes_no_pass); + keypair_bytes_no_pass[32..].copy_from_slice(&verifying_key_bytes_no_pass); + let keypair_no_pass = Keypair::from_bytes(&keypair_bytes_no_pass).unwrap(); + + // With passphrase + let seed_with_pass = Seed::new(&mnemonic, "test_passphrase"); + let secret_key_bytes_with_pass: [u8; 32] = + seed_with_pass.as_bytes()[0..32].try_into().unwrap(); + let signing_key_with_pass = SigningKey::from_bytes(&secret_key_bytes_with_pass); + let verifying_key_bytes_with_pass = signing_key_with_pass.verifying_key().to_bytes(); + let mut keypair_bytes_with_pass = [0u8; 64]; + keypair_bytes_with_pass[..32].copy_from_slice(&secret_key_bytes_with_pass); + keypair_bytes_with_pass[32..].copy_from_slice(&verifying_key_bytes_with_pass); + let keypair_with_pass = Keypair::from_bytes(&keypair_bytes_with_pass).unwrap(); + + // Should produce different pubkeys + assert_ne!(keypair_no_pass.pubkey(), keypair_with_pass.pubkey()); + } + + #[test] + fn test_word_count_variations() { + // Test all supported word counts + let word_counts = [12, 15, 18, 21, 24]; + + for word_count in word_counts { + let outfile_dir = tempdir().unwrap(); + let outfile_path = + tmp_outfile_path(&outfile_dir, &format!("test-keypair-{}.json", word_count)); + + // Test: successful keypair generation with different word counts + let result = keygen_new(Some(outfile_path.clone()), false, true, true, word_count); + assert!( + result.is_ok(), + "Failed to generate keypair with {} words", + word_count + ); + + // Verify the keypair file was created + assert!(Path::new(&outfile_path).exists()); + + // Verify we can read the keypair back + let keypair = read_keypair_file(&outfile_path).unwrap(); + assert_ne!(keypair.pubkey(), Pubkey::default()); + } + } + + #[test] + fn test_invalid_word_count() { + let outfile_dir = tempdir().unwrap(); + let outfile_path = tmp_outfile_path(&outfile_dir, "test-invalid-wordcount.json"); + + // Test: invalid word count should fail + let result = keygen_new(Some(outfile_path), false, true, true, 9); + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains("Invalid word count")); + } +} diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 3208f73f35..51faddc079 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -1,7 +1,8 @@ use crate::config::{ - get_default_ledger_path, BootstrapMode, BuildConfig, Config, ConfigOverride, HookType, - Manifest, PackageManager, ProgramArch, ProgramDeployment, ProgramWorkspace, ScriptsConfig, - TestValidator, WithPath, SHUTDOWN_WAIT, STARTUP_WAIT, + get_default_ledger_path, handle_config_command, BootstrapMode, BuildConfig, Config, + ConfigCommand, ConfigOverride, HookType, Manifest, PackageManager, ProgramArch, + ProgramDeployment, ProgramWorkspace, ScriptsConfig, TestValidator, WithPath, SHUTDOWN_WAIT, + STARTUP_WAIT, }; use anchor_client::Cluster; use anchor_lang::prelude::UpgradeableLoaderState; @@ -18,7 +19,11 @@ use regex::{Regex, RegexBuilder}; use rust_template::{ProgramTemplate, TestTemplate}; use semver::{Version, VersionReq}; use serde_json::{json, Map, Value as JsonValue}; +use solana_cli_config::Config as SolanaCliConfig; +use solana_pubsub_client::pubsub_client::{PubsubClient, PubsubClientSubscription}; use solana_rpc_client::rpc_client::RpcClient; +use solana_rpc_client_api::config::{RpcTransactionLogsConfig, RpcTransactionLogsFilter}; +use solana_rpc_client_api::response::{Response as RpcResponse, RpcLogsResponse}; use solana_sdk::commitment_config::CommitmentConfig; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, Signer}; @@ -35,10 +40,15 @@ use std::str::FromStr; use std::string::ToString; use std::sync::LazyLock; +mod account; mod checks; pub mod config; +mod keygen; +mod program; pub mod rust_template; +use keygen::get_default_wallet_path; + // Version of the docker image. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const DOCKER_BUILDER_VERSION: &str = VERSION; @@ -46,6 +56,15 @@ pub const DOCKER_BUILDER_VERSION: &str = VERSION; /// Default RPC port pub const DEFAULT_RPC_PORT: u16 = 8899; +/// WebSocket port offset for solana-test-validator (RPC port + 1) +pub const WEBSOCKET_PORT_OFFSET: u16 = 1; + +/// Default Solana mainnet RPC URL +pub const DEFAULT_MAINNET_RPC_URL: &str = "https://api.mainnet-beta.solana.com"; + +/// Default Solana wallet path relative to home directory +pub const DEFAULT_WALLET_PATH: &str = ".config/solana/id.json"; + pub static AVM_HOME: LazyLock = LazyLock::new(|| { if let Ok(avm_home) = std::env::var("AVM_HOME") { PathBuf::from(avm_home) @@ -235,7 +254,8 @@ pub enum Command { }, /// Remove all artifacts from the generated directories except program keypairs. Clean, - /// Deploys each program in the workspace. + /// [DEPRECATED] Deploys each program in the workspace. Use `anchor program deploy` instead. + #[clap(hide = true)] Deploy { /// Only deploy this program #[clap(short, long)] @@ -249,15 +269,17 @@ pub enum Command { /// Don't upload IDL during deployment (IDL is uploaded by default) #[clap(long)] no_idl: bool, + /// Make the program immutable after deployment (cannot be upgraded) + #[clap(long = "final")] + make_final: bool, /// Arguments to pass to the underlying `solana program deploy` command. #[clap(required = false, last = true)] solana_args: Vec, }, /// Runs the deploy migration script. Migrate, - /// Deploys, initializes an IDL, and migrates all in one command. - /// Upgrades a single program. The configured wallet must be the upgrade - /// authority. + /// [DEPRECATED] Upgrades a single program. Use `anchor program upgrade` instead. + #[clap(hide = true)] Upgrade { /// The program to upgrade. #[clap(short, long)] @@ -271,17 +293,23 @@ pub enum Command { #[clap(required = false, last = true)] solana_args: Vec, }, - #[cfg(feature = "dev")] - /// Runs an airdrop loop, continuously funding the configured wallet. + /// Request an airdrop of SOL Airdrop { - #[clap(short, long)] - url: Option, + /// Amount of SOL to airdrop + amount: f64, + /// Recipient address (defaults to configured wallet) + pubkey: Option, }, /// Cluster commands. Cluster { #[clap(subcommand)] subcmd: ClusterCommand, }, + /// Configuration commands. + Config { + #[clap(subcommand)] + subcmd: ConfigCommand, + }, /// Starts a node shell with an Anchor client setup according to the local /// config. Shell, @@ -329,7 +357,7 @@ pub enum Command { }, /// Fetch and deserialize an account using the IDL provided. Account { - /// Account struct to deserialize + /// Account struct to deserialize (format: .) account_type: String, /// Address of the account to deserialize address: Pubkey, @@ -342,6 +370,49 @@ pub enum Command { #[clap(value_enum)] shell: clap_complete::Shell, }, + /// Get your public key + Address { + /// Confirm key on device; only relevant if using remote wallet + #[clap(long)] + confirm_key: bool, + }, + /// Get your balance + Balance { + /// Account to check balance for (defaults to configured wallet) + pubkey: Option, + /// Display balance in lamports instead of SOL + #[clap(long)] + lamports: bool, + }, + /// Get current epoch + Epoch, + /// Get information about the current epoch + EpochInfo, + /// Stream transaction logs + Logs { + /// Include vote transactions when monitoring all transactions + #[clap(long)] + include_votes: bool, + /// Addresses to filter logs by + #[clap(long)] + address: Option>, + }, + /// Show the contents of an account + #[clap(name = "show-account")] + ShowAccount { + #[clap(flatten)] + cmd: account::ShowAccountCommand, + }, + /// Keypair generation and management + Keygen { + #[clap(subcommand)] + subcmd: KeygenCommand, + }, + /// Program deployment and management (BPF Loader Upgradeable) + Program { + #[clap(subcommand)] + subcmd: ProgramCommand, + }, } #[derive(Debug, Parser)] @@ -356,6 +427,209 @@ pub enum KeysCommand { }, } +#[derive(Debug, Parser)] +pub enum KeygenCommand { + /// Generate a new keypair + New { + /// Path to generated keypair file + #[clap(short = 'o', long)] + outfile: Option, + /// Overwrite the output file if it exists + #[clap(short, long)] + force: bool, + /// Do not prompt for a passphrase + #[clap(long)] + no_passphrase: bool, + /// Do not display the generated pubkey + #[clap(long)] + silent: bool, + /// Number of words in the mnemonic phrase [possible values: 12, 15, 18, 21, 24] + #[clap(short = 'w', long, default_value = "12")] + word_count: usize, + }, + /// Display the pubkey for a given keypair + Pubkey { + /// Keypair filepath + keypair: Option, + }, + /// Recover a keypair from a seed phrase + Recover { + /// Path to recovered keypair file + #[clap(short = 'o', long)] + outfile: Option, + /// Overwrite the output file if it exists + #[clap(short, long)] + force: bool, + /// Skip seed phrase validation + #[clap(long)] + skip_seed_phrase_validation: bool, + /// Do not prompt for a passphrase + #[clap(long)] + no_passphrase: bool, + }, + /// Verify a keypair can sign and verify a message + Verify { + /// Public key to verify + pubkey: Pubkey, + /// Keypair filepath (defaults to configured wallet) + keypair: Option, + }, +} + +#[derive(Debug, Parser)] +pub enum ProgramCommand { + /// Deploy an upgradeable program + Deploy { + /// Program filepath (e.g., target/deploy/my_program.so). + /// If not provided, discovers programs from workspace + program_filepath: Option, + /// Program name to deploy (from workspace). Used when program_filepath is not provided + #[clap(short, long)] + program_name: Option, + /// Program keypair filepath (defaults to target/deploy/{program_name}-keypair.json) + #[clap(long)] + program_keypair: Option, + /// Upgrade authority keypair (defaults to configured wallet) + #[clap(long)] + upgrade_authority: Option, + /// Program id to deploy to (derived from program-keypair if not specified) + #[clap(long)] + program_id: Option, + /// Buffer account to use for deployment + #[clap(long)] + buffer: Option, + /// Maximum transaction length (BPF loader upgradeable limit) + #[clap(long)] + max_len: Option, + /// Don't upload IDL during deployment (IDL is uploaded by default) + #[clap(long)] + no_idl: bool, + /// Make the program immutable after deployment (cannot be upgraded) + #[clap(long = "final")] + make_final: bool, + /// Additional arguments to configure deployment (e.g., --with-compute-unit-price 1000) + #[clap(required = false, last = true)] + solana_args: Vec, + }, + /// Write a program into a buffer account + WriteBuffer { + /// Program filepath (e.g., target/deploy/my_program.so). + /// If not provided, discovers program from workspace using program_name + program_filepath: Option, + /// Program name to write (from workspace). Used when program_filepath is not provided + #[clap(short, long)] + program_name: Option, + /// Buffer account keypair (defaults to new keypair) + #[clap(long)] + buffer: Option, + /// Buffer authority (defaults to configured wallet) + #[clap(long)] + buffer_authority: Option, + /// Maximum transaction length + #[clap(long)] + max_len: Option, + }, + /// Set a new buffer authority + SetBufferAuthority { + /// Buffer account address + buffer: Pubkey, + /// New buffer authority + new_buffer_authority: Pubkey, + }, + /// Set a new program authority + SetUpgradeAuthority { + /// Program id + program_id: Pubkey, + /// New upgrade authority pubkey + #[clap(long)] + new_upgrade_authority: Option, + /// New upgrade authority signer (keypair file). Required unless --skip-new-upgrade-authority-signer-check is used. + /// When provided, both current and new authority will sign (checked mode, recommended) + #[clap(long)] + new_upgrade_authority_signer: Option, + /// Skip new upgrade authority signer check. Allows setting authority with only current authority signature. + /// WARNING: Less safe - use only if you're confident the pubkey is correct + #[clap(long)] + skip_new_upgrade_authority_signer_check: bool, + /// Make the program immutable (cannot be upgraded) + #[clap(long = "final")] + make_final: bool, + /// Current upgrade authority keypair (defaults to configured wallet) + #[clap(long)] + upgrade_authority: Option, + }, + /// Display information about a buffer or program + Show { + /// Account address (buffer or program) + account: Pubkey, + /// Get account information from the Solana config file + #[clap(long)] + get_programs: bool, + /// Get account information from the Solana config file + #[clap(long)] + get_buffers: bool, + /// Show all accounts + #[clap(long)] + all: bool, + }, + /// Upgrade an upgradeable program + Upgrade { + /// Program id to upgrade + program_id: Pubkey, + /// Program filepath (e.g., target/deploy/my_program.so). If not provided, buffer must be specified + #[clap(long)] + program_filepath: Option, + /// Existing buffer account to upgrade from. If not provided, program_filepath must be specified + #[clap(long)] + buffer: Option, + /// Upgrade authority (defaults to configured wallet) + #[clap(long)] + upgrade_authority: Option, + /// Max times to retry on failure + #[clap(long, default_value = "0")] + max_retries: u32, + /// Additional arguments to configure deployment (e.g., --with-compute-unit-price 1000) + #[clap(required = false, last = true)] + solana_args: Vec, + }, + /// Write the program data to a file + Dump { + /// Program account address + account: Pubkey, + /// Output file path + output_file: String, + }, + /// Close a program or buffer account and withdraw all lamports + Close { + /// Account address to close (buffer or program). + /// If not provided, discovers program from workspace using program_name + account: Option, + /// Program name to close (from workspace). Used when account is not provided + #[clap(short, long)] + program_name: Option, + /// Authority keypair (defaults to configured wallet) + #[clap(long)] + authority: Option, + /// Recipient address for reclaimed lamports (defaults to authority) + #[clap(long)] + recipient: Option, + /// Bypass warning prompts + #[clap(long)] + bypass_warning: bool, + }, + /// Extend the length of an upgradeable program + Extend { + /// Program id to extend. + /// If not provided, discovers program from workspace using program_name + program_id: Option, + /// Program name to extend (from workspace). Used when program_id is not provided + #[clap(short, long)] + program_name: Option, + /// Additional bytes to allocate + additional_bytes: usize, + }, +} + #[derive(Debug, Parser)] pub enum IdlCommand { /// Initializes a program's IDL account. Can only be run once. @@ -800,15 +1074,29 @@ fn process_command(opts: Opts) -> Result<()> { program_keypair, verifiable, no_idl, + make_final, solana_args, - } => deploy( - &opts.cfg_override, - program_name, - program_keypair, - verifiable, - no_idl, - solana_args, - ), + } => { + // Show deprecation warning + eprintln!("⚠️ WARNING: 'anchor deploy' is deprecated."); + eprintln!(" Please use 'anchor program deploy' instead."); + eprintln!(" Example: anchor program deploy [PROGRAM_FILEPATH]\n"); + + program::process_deploy( + &opts.cfg_override, + None, // program_filepath + program_name, + program_keypair, + None, // upgrade_authority + None, // program_id + None, // buffer + None, // max_len + verifiable, + no_idl, + make_final, + solana_args, + ) + } Command::Expand { program_name, cargo_args, @@ -818,13 +1106,27 @@ fn process_command(opts: Opts) -> Result<()> { program_filepath, max_retries, solana_args, - } => upgrade( - &opts.cfg_override, - program_id, - program_filepath, - max_retries, - solana_args, - ), + } => { + // Show deprecation warning + eprintln!("⚠️ WARNING: 'anchor upgrade' is deprecated."); + eprintln!(" Please use 'anchor program upgrade' instead."); + eprintln!( + " Example: anchor program upgrade --program-filepath \n" + ); + + // Redirect to new command + program::program( + &opts.cfg_override, + ProgramCommand::Upgrade { + program_id, + program_filepath: Some(program_filepath), + buffer: None, + upgrade_authority: None, + max_retries, + solana_args, + }, + ) + } Command::Idl { subcmd } => idl(&opts.cfg_override, subcmd), Command::Migrate => migrate(&opts.cfg_override), Command::Test { @@ -855,9 +1157,9 @@ fn process_command(opts: Opts) -> Result<()> { cargo_args, arch, ), - #[cfg(feature = "dev")] - Command::Airdrop { .. } => airdrop(&opts.cfg_override), + Command::Airdrop { amount, pubkey } => airdrop(&opts.cfg_override, amount, pubkey), Command::Cluster { subcmd } => cluster(subcmd), + Command::Config { subcmd } => handle_config_command(&opts.cfg_override, subcmd), Command::Shell => shell(&opts.cfg_override), Command::Run { script, @@ -895,6 +1197,17 @@ fn process_command(opts: Opts) -> Result<()> { ); Ok(()) } + Command::Address { confirm_key } => address(&opts.cfg_override, confirm_key), + Command::Balance { pubkey, lamports } => balance(&opts.cfg_override, pubkey, lamports), + Command::Epoch => epoch(&opts.cfg_override), + Command::EpochInfo => epoch_info(&opts.cfg_override), + Command::Logs { + include_votes, + address, + } => logs_subscribe(&opts.cfg_override, include_votes, address), + Command::ShowAccount { cmd } => account::show_account(&opts.cfg_override, cmd), + Command::Keygen { subcmd } => keygen::keygen(&opts.cfg_override, subcmd), + Command::Program { subcmd } => program::program(&opts.cfg_override, subcmd), } } @@ -1176,7 +1489,8 @@ pub fn expand( cd_member(cfg_override, program_name)?; } - let workspace_cfg = Config::discover(cfg_override)?.expect("Not in workspace."); + let workspace_cfg = Config::discover(cfg_override)? + .ok_or_else(|| anyhow!("The 'anchor expand' command requires an Anchor workspace."))?; let cfg_parent = workspace_cfg.path().parent().expect("Invalid Anchor.toml"); let cargo = Manifest::discover()?; @@ -1284,7 +1598,8 @@ pub fn build( if let Some(program_name) = program_name.as_ref() { cd_member(cfg_override, program_name)?; } - let cfg = Config::discover(cfg_override)?.expect("Not in workspace."); + let cfg = Config::discover(cfg_override)? + .ok_or_else(|| anyhow!("The 'anchor build' command requires an Anchor workspace."))?; let cfg_parent = cfg.path().parent().expect("Invalid Anchor.toml"); // Require overflow checks @@ -1924,10 +2239,10 @@ pub fn verify( } fn cd_member(cfg_override: &ConfigOverride, program_name: &str) -> Result<()> { - // Change directories to the given `program_name`, if given. - let cfg = Config::discover(cfg_override)?.expect("Not in workspace."); + // Change directories to the given `program_name`, using either Anchor or Cargo workspace + let programs = program::get_programs_from_workspace(cfg_override, None)?; - for program in cfg.read_all_programs()? { + for program in programs { let cargo_toml = program.path.join("Cargo.toml"); if !cargo_toml.exists() { return Err(anyhow!( @@ -1938,8 +2253,7 @@ fn cd_member(cfg_override: &ConfigOverride, program_name: &str) -> Result<()> { let manifest = Manifest::from_path(&cargo_toml)?; let pkg_name = manifest.package().name(); - let lib_name = manifest.lib_name()?; - if program_name == pkg_name || program_name == lib_name { + if program_name == pkg_name || program_name == program.lib_name { std::env::set_current_dir(&program.path)?; return Ok(()); } @@ -2042,16 +2356,12 @@ fn idl_init( let program_id_str = program_id.to_string(); let mut args = vec!["write", "idl", &program_id_str, &idl_filepath]; - if non_canonical { - args.push("--non-canonical"); - } + // Silently create the IDL account + let _ = create_idl_account(cfg, &keypair, &program_id, &idl, priority_fee)?; - let priority_fee_str; - if let Some(priority_fee) = priority_fee { - priority_fee_str = priority_fee.to_string(); - args.push("--priority-fees"); - args.push(&priority_fee_str); - } + Ok(()) + }) +} args.push("--rpc"); args.push(&url); @@ -2103,7 +2413,82 @@ fn idl_upgrade( return Err(anyhow!("Failed to upgrade IDL")); } - println!("IDL upgraded."); + Ok(()) +} + +// Write the idl to the account buffer, chopping up the IDL into pieces +// and sending multiple transactions in the event the IDL doesn't fit into +// a single transaction. +fn idl_write( + cfg: &Config, + program_id: &Pubkey, + idl: &Idl, + idl_address: Pubkey, + priority_fee: Option, +) -> Result<()> { + // Misc. + let keypair = get_keypair(&cfg.provider.wallet.to_string())?; + let url = cluster_url(cfg, &cfg.test_validator); + let client = create_client(url); + + // Serialize and compress the idl. + let idl_data = { + let json_bytes = serde_json::to_vec(idl)?; + let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); + e.write_all(&json_bytes)?; + e.finish()? + }; + + const MAX_WRITE_SIZE: usize = 600; + let mut offset = 0; + while offset < idl_data.len() { + // Instruction data. + let data = { + let start = offset; + let end = std::cmp::min(offset + MAX_WRITE_SIZE, idl_data.len()); + serialize_idl_ix(anchor_lang::idl::IdlInstruction::Write { + data: idl_data[start..end].to_vec(), + })? + }; + // Instruction accounts. + let accounts = vec![ + AccountMeta::new(idl_address, false), + AccountMeta::new_readonly(keypair.pubkey(), true), + ]; + // Instruction. + let ix = Instruction { + program_id: *program_id, + accounts, + data, + }; + // Send transaction. + let instructions = prepend_compute_unit_ix(vec![ix], &client, priority_fee)?; + + let mut latest_hash = client.get_latest_blockhash()?; + for retries in 0..20 { + if !client.is_blockhash_valid(&latest_hash, client.commitment())? { + latest_hash = client.get_latest_blockhash()?; + } + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&keypair.pubkey()), + &[&keypair], + latest_hash, + ); + + match client.send_and_confirm_transaction_with_spinner(&tx) { + Ok(_) => break, + Err(e) => { + if retries == 19 { + return Err(anyhow!("Error: {e}. Failed to send transaction.")); + } + println!("Error: {e}. Retrying transaction."); + } + } + } + + offset += MAX_WRITE_SIZE; + } Ok(()) } @@ -2116,7 +2501,8 @@ fn idl_build( skip_lint: bool, cargo_args: Vec, ) -> Result<()> { - let cfg = Config::discover(cfg_override)?.expect("Not in workspace"); + let cfg = Config::discover(cfg_override)? + .ok_or_else(|| anyhow!("The 'anchor idl build' command requires an Anchor workspace."))?; let current_dir = std::env::current_dir()?; let program_path = match program_name { Some(name) => cfg.get_program(&name)?.path, @@ -2451,9 +2837,8 @@ fn account( let idl = idl_filepath.map_or_else( || { - Config::discover(cfg_override) - .expect("Error when detecting workspace.") - .expect("Not in workspace.") + Config::discover(cfg_override)? + .ok_or_else(|| anyhow!("The 'anchor idl convert' command requires an Anchor workspace with Anchor.toml for IDL type generation."))? .read_all_programs() .expect("Workspace must contain atleast one program.") .into_iter() @@ -2753,7 +3138,20 @@ fn test( // In either case, skip the deploy if the user specifies. let is_localnet = cfg.provider.cluster == Cluster::Localnet; if (!is_localnet || skip_local_validator) && !skip_deploy { - deploy(cfg_override, None, None, false, true, vec![])?; + program::process_deploy( + cfg_override, + None, // program_filepath + None, // program_name + None, // program_keypair + None, // upgrade_authority + None, // program_id + None, // buffer + None, // max_len + false, // verifiable + true, // no_idl + false, // make_final + vec![], // solana_args + )?; } cfg.run_hooks(HookType::PreTest)?; @@ -2867,8 +3265,15 @@ fn run_test_suite( get_node_dns_option()?, ); - // Setup log reader. - let log_streams = stream_logs(cfg, &url); + // Setup log reader - kept alive until end of scope + let log_streams = match stream_logs(cfg, &url) { + Ok(streams) => Some(streams), + Err(e) => { + eprintln!("Warning: Failed to setup program log streaming: {:#}", e); + eprintln!("Program logs will still be visible in the test output."); + None + } + }; // Run the tests. let test_result = { @@ -2902,9 +3307,11 @@ fn run_test_suite( println!("Failed to kill subprocess {}: {}", child.id(), err); } } - for mut child in log_streams? { - if let Err(err) = child.kill() { - println!("Failed to kill subprocess {}: {}", child.id(), err); + + // Explicitly shutdown log streams - closes WebSocket subscriptions + if let Some(log_streams) = log_streams { + for handle in log_streams { + handle.shutdown(); } } @@ -3096,14 +3503,93 @@ fn validator_flags( Ok(flags) } -fn stream_logs(config: &WithPath, rpc_url: &str) -> Result> { +/// Handle for a log streaming thread. +/// +/// Manages a WebSocket subscription and its associated receiver thread. +/// Call `shutdown()` to cleanly stop the thread. +struct LogStreamHandle { + subscription: PubsubClientSubscription>, +} + +impl LogStreamHandle { + /// Explicitly shutdown the log stream + fn shutdown(self) { + // Send unsubscribe in a background thread to avoid blocking + // PubsubClientSubscription::send_unsubscribe() can block indefinitely if WebSocket is stuck + // The receiver threads will exit when the subscription closes + std::thread::spawn(move || { + let _ = self.subscription.send_unsubscribe(); + }); + } +} + +/// Spawns a thread to receive logs from a subscription and write them to a file +fn spawn_log_receiver_thread(receiver: R, log_file_path: PathBuf) +where + R: IntoIterator> + Send + 'static, +{ + std::thread::spawn(move || { + if let Ok(mut file) = File::create(&log_file_path) { + for response in receiver { + let _ = writeln!( + file, + "Transaction executed in slot {}:", + response.context.slot + ); + let _ = writeln!(file, " Signature: {}", response.value.signature); + let _ = writeln!( + file, + " Status: {}", + response + .value + .err + .map(|err| err.to_string()) + .unwrap_or_else(|| "Ok".to_string()) + ); + let _ = writeln!(file, " Log Messages:"); + for log in response.value.logs { + let _ = writeln!(file, " {}", log); + } + let _ = writeln!(file); // Empty line between transactions + let _ = file.flush(); + } + } else { + eprintln!("Failed to create log file: {:?}", log_file_path); + } + }); +} + +fn stream_logs(config: &WithPath, rpc_url: &str) -> Result> { let program_logs_dir = Path::new(".anchor").join("program-logs"); if program_logs_dir.exists() { fs::remove_dir_all(&program_logs_dir)?; } fs::create_dir_all(&program_logs_dir)?; + // For solana-test-validator, the WebSocket port is RPC port + WEBSOCKET_PORT_OFFSET + // Extract port from rpc_url and construct WebSocket URL + let ws_url = if rpc_url.contains("127.0.0.1") || rpc_url.contains("localhost") { + // Local validator: increment port by 1 for WebSocket + let rpc_port = rpc_url + .rsplit_once(':') + .and_then(|(_, port)| port.parse::().ok()) + .unwrap_or(DEFAULT_RPC_PORT); + + let ws_port = rpc_port + WEBSOCKET_PORT_OFFSET; + let url = format!("ws://127.0.0.1:{}", ws_port); + url + } else { + // Remote cluster: use same URL but replace http(s) with ws(s) + rpc_url + .replace("https://", "wss://") + .replace("http://", "ws://") + }; + + // Give the WebSocket endpoint a moment to be ready (especially for local validators) + std::thread::sleep(std::time::Duration::from_millis(1500)); + let mut handles = vec![]; + for program in config.read_all_programs()? { let idl_path = Path::new("target") .join("idl") @@ -3112,33 +3598,66 @@ fn stream_logs(config: &WithPath, rpc_url: &str) -> Result result, + Err(e) => { + eprintln!( + "Warning: Failed to subscribe to logs for program {}: {}", + program.lib_name, e + ); + continue; + } + }; + + // Spawn thread to write logs to file + spawn_log_receiver_thread(receiver, log_file_path); + + handles.push(LogStreamHandle { + subscription: client, + }); } + if let Some(test) = config.test_validator.as_ref() { if let Some(genesis) = &test.genesis { for entry in genesis { - let log_file = - File::create(program_logs_dir.join(&entry.address).with_extension("log"))?; - let stdio = std::process::Stdio::from(log_file); - let child = std::process::Command::new("solana") - .arg("logs") - .arg(entry.address.clone()) - .arg("--url") - .arg(rpc_url) - .stdout(stdio) - .spawn()?; - handles.push(child); + let log_file_path = program_logs_dir.join(&entry.address).with_extension("log"); + let address = entry.address.clone(); + + // Subscribe to logs using PubsubClient + let (client, receiver) = match PubsubClient::logs_subscribe( + &ws_url, + RpcTransactionLogsFilter::Mentions(vec![address.clone()]), + RpcTransactionLogsConfig { + commitment: Some(CommitmentConfig::confirmed()), + }, + ) { + Ok(result) => result, + Err(e) => { + eprintln!( + "Warning: Failed to subscribe to logs for genesis program {}: {}", + &entry.address, e + ); + continue; + } + }; + + // Spawn thread to write logs to file + spawn_log_receiver_thread(receiver, log_file_path); + + handles.push(LogStreamHandle { + subscription: client, + }); } } } @@ -3278,10 +3797,19 @@ fn cluster_url(cfg: &Config, test_validator: &Option) -> String { } fn clean(cfg_override: &ConfigOverride) -> Result<()> { - let cfg = Config::discover(cfg_override)?.expect("Not in workspace."); - let cfg_parent = cfg.path().parent().expect("Invalid Anchor.toml"); - let dot_anchor_dir = cfg_parent.join(".anchor"); - let target_dir = cfg_parent.join("target"); + // Get workspace root - either from Anchor.toml or use current directory + let workspace_root = if let Ok(Some(cfg)) = Config::discover(cfg_override) { + cfg.path() + .parent() + .expect("Invalid Anchor.toml") + .to_path_buf() + } else { + // No Anchor.toml - use current directory for Cargo workspace + std::env::current_dir()? + }; + + let dot_anchor_dir = workspace_root.join(".anchor"); + let target_dir = workspace_root.join("target"); let deploy_dir = target_dir.join("deploy"); if dot_anchor_dir.exists() { @@ -3319,187 +3847,190 @@ fn clean(cfg_override: &ConfigOverride) -> Result<()> { Ok(()) } -fn deploy( - cfg_override: &ConfigOverride, - program_name: Option, - program_keypair: Option, - verifiable: bool, - no_idl: bool, - solana_args: Vec, -) -> Result<()> { - // Execute the code within the workspace - with_workspace(cfg_override, |cfg| { - let url = cluster_url(cfg, &cfg.test_validator); - let keypair = cfg.provider.wallet.to_string(); - - // Augment the given solana args with recommended defaults. - let client = create_client(&url); - let solana_args = add_recommended_deployment_solana_args(&client, solana_args)?; - - cfg.run_hooks(HookType::PreDeploy)?; - // Deploy the programs. - println!("Deploying cluster: {url}"); - println!("Upgrade authority: {keypair}"); - - for mut program in cfg.get_programs(program_name)? { - let binary_path = program.binary_path(verifiable).display().to_string(); - - println!("Deploying program {:?}...", program.lib_name); - println!("Program path: {binary_path}..."); - - let (program_keypair_filepath, program_id) = match &program_keypair { - Some(path) => (path.clone(), get_keypair(path)?.pubkey()), - None => ( - program.keypair_file()?.path().display().to_string(), - program.pubkey()?, - ), - }; +fn create_idl_account( + cfg: &Config, + keypair_path: &str, + program_id: &Pubkey, + idl: &Idl, + priority_fee: Option, +) -> Result { + // Misc. + let idl_address = IdlAccount::address(program_id); + let keypair = get_keypair(keypair_path)?; + let url = cluster_url(cfg, &cfg.test_validator); + let client = create_client(url); + let idl_data = serialize_idl(idl)?; + + // Run `Create instruction. + { + let pda_max_growth = 60_000; + let idl_header_size = 44; + let idl_data_len = idl_data.len() as u64; + // We're only going to support up to 6 instructions in one transaction + // because will anyone really have a >60kb IDL? + if idl_data_len > pda_max_growth { + return Err(anyhow!( + "Your IDL is over 60kb and this isn't supported right now" + )); + } + // Double for future growth. + let data_len = (idl_data_len * 2).min(pda_max_growth - idl_header_size); + + let num_additional_instructions = data_len / 10000; + let mut instructions = Vec::new(); + let data = serialize_idl_ix(anchor_lang::idl::IdlInstruction::Create { data_len })?; + let program_signer = Pubkey::find_program_address(&[], program_id).0; + let accounts = vec![ + AccountMeta::new_readonly(keypair.pubkey(), true), + AccountMeta::new(idl_address, false), + AccountMeta::new_readonly(program_signer, false), + AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(*program_id, false), + ]; + instructions.push(Instruction { + program_id: *program_id, + accounts, + data, + }); - // Send deploy transactions using the Solana CLI - let exit = std::process::Command::new("solana") - .arg("program") - .arg("deploy") - .arg("--url") - .arg(&url) - .arg("--keypair") - .arg(&keypair) - .arg("--program-id") - .arg(strip_workspace_prefix(program_keypair_filepath)) - .arg(strip_workspace_prefix(binary_path)) - .args(&solana_args) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output() - .expect("Must deploy"); + for _ in 0..num_additional_instructions { + let data = serialize_idl_ix(anchor_lang::idl::IdlInstruction::Resize { data_len })?; + instructions.push(Instruction { + program_id: *program_id, + accounts: vec![ + AccountMeta::new(idl_address, false), + AccountMeta::new_readonly(keypair.pubkey(), true), + AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + ], + data, + }); + } + instructions = prepend_compute_unit_ix(instructions, &client, priority_fee)?; - // Check if deployment was successful - if !exit.status.success() { - println!("There was a problem deploying: {exit:?}."); - std::process::exit(exit.status.code().unwrap_or(1)); + let mut latest_hash = client.get_latest_blockhash()?; + for retries in 0..20 { + if !client.is_blockhash_valid(&latest_hash, client.commitment())? { + latest_hash = client.get_latest_blockhash()?; } - // Get the IDL filepath - let idl_filepath = Path::new("target") - .join("idl") - .join(&program.lib_name) - .with_extension("json"); - - if let Some(idl) = program.idl.as_mut() { - // Add program address to the IDL. - idl.address = program_id.to_string(); - - // Persist it. - write_idl(idl, OutFile::File(idl_filepath.clone()))?; - - // Upload the IDL to the cluster by default (unless no_idl is set) - if !no_idl { - // Wait for the program to be confirmed before initializing IDL to prevent - // race condition where the program isn't yet available in validator cache - let client = create_client(&url); - let max_retries = 5; - let retry_delay = std::time::Duration::from_millis(500); - let cache_delay = std::time::Duration::from_secs(2); - - println!("Waiting for program {} to be confirmed...", program_id); - - for attempt in 0..max_retries { - if let Ok(account) = client.get_account(&program_id) { - if account.executable { - println!("Program confirmed on-chain"); - std::thread::sleep(cache_delay); - break; - } - } - - if attempt == max_retries - 1 { - return Err(anyhow!( - "Timeout waiting for program {} to be confirmed", - program_id - )); - } - - std::thread::sleep(retry_delay); - } + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&keypair.pubkey()), + &[&keypair], + latest_hash, + ); - // Check if IDL account already exists - let (base, _) = Pubkey::find_program_address(&[], &program_id); - let idl_address = Pubkey::create_with_seed(&base, "anchor:idl", &program_id) - .expect("Seed is always valid"); - let idl_account_exists = client.get_account(&idl_address).is_ok(); - - if idl_account_exists { - // IDL account exists, upgrade it - idl_upgrade( - cfg_override, - program_id, - idl_filepath.display().to_string(), - None, - )?; - } else { - // IDL account doesn't exist, create it - idl_init( - cfg_override, - program_id, - idl_filepath.display().to_string(), - None, - false, - )?; + match client.send_and_confirm_transaction_with_spinner(&tx) { + Ok(_) => break, + Err(err) => { + if retries == 19 { + return Err(anyhow!("Error creating IDL account: {}", err)); } + println!("Error creating IDL account: {err}. Retrying..."); } } } + } - println!("Deploy success"); - cfg.run_hooks(HookType::PostDeploy)?; + // Write directly to the IDL account buffer. + idl_write( + cfg, + program_id, + idl, + IdlAccount::address(program_id), + priority_fee, + )?; - Ok(()) - }) + Ok(idl_address) } -fn upgrade( - cfg_override: &ConfigOverride, - program_id: Pubkey, - program_filepath: String, - max_retries: u32, - solana_args: Vec, -) -> Result<()> { - let path: PathBuf = program_filepath.parse().unwrap(); - let program_filepath = path.canonicalize()?.display().to_string(); +fn create_idl_buffer( + cfg: &Config, + keypair_path: &str, + program_id: &Pubkey, + idl: &Idl, + priority_fee: Option, +) -> Result { + let keypair = get_keypair(keypair_path)?; + let url = cluster_url(cfg, &cfg.test_validator); + let client = create_client(url); + + let buffer = Keypair::new(); + + // Creates the new buffer account with the system program. + let create_account_ix = { + let space = IdlAccount::DISCRIMINATOR.len() + 32 + 4 + serialize_idl(idl)?.len(); + let lamports = client.get_minimum_balance_for_rent_exemption(space)?; + solana_sdk::system_instruction::create_account( + &keypair.pubkey(), + &buffer.pubkey(), + lamports, + space as u64, + program_id, + ) + }; - with_workspace(cfg_override, |cfg| { - let url = cluster_url(cfg, &cfg.test_validator); - let client = create_client(&url); - let solana_args = add_recommended_deployment_solana_args(&client, solana_args)?; - - for retry in 0..(1 + max_retries) { - let exit = std::process::Command::new("solana") - .arg("program") - .arg("deploy") - .arg("--url") - .arg(url.clone()) - .arg("--keypair") - .arg(cfg.provider.wallet.to_string()) - .arg("--program-id") - .arg(program_id.to_string()) - .arg(strip_workspace_prefix(program_filepath.clone())) - .args(&solana_args) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output() - .expect("Must deploy"); - if exit.status.success() { - break; - } + // Program instruction to create the buffer. + let create_buffer_ix = { + let accounts = vec![ + AccountMeta::new(buffer.pubkey(), false), + AccountMeta::new_readonly(keypair.pubkey(), true), + ]; + let mut data = anchor_lang::idl::IDL_IX_TAG.to_le_bytes().to_vec(); + data.append(&mut anchor_lang::prelude::borsh::to_vec( + &IdlInstruction::CreateBuffer, + )?); + Instruction { + program_id: *program_id, + accounts, + data, + } + }; - println!("There was a problem deploying: {exit:?}."); - if retry < max_retries { - println!("Retrying {} more time(s)...", max_retries - retry); - } else { - std::process::exit(exit.status.code().unwrap_or(1)); + let instructions = prepend_compute_unit_ix( + vec![create_account_ix, create_buffer_ix], + &client, + priority_fee, + )?; + + let mut latest_hash = client.get_latest_blockhash()?; + for retries in 0..20 { + if !client.is_blockhash_valid(&latest_hash, client.commitment())? { + latest_hash = client.get_latest_blockhash()?; + } + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&keypair.pubkey()), + &[&keypair, &buffer], + latest_hash, + ); + match client.send_and_confirm_transaction_with_spinner(&tx) { + Ok(_) => break, + Err(err) => { + if retries == 19 { + return Err(anyhow!("Error creating buffer account: {}", err)); + } + println!("Error creating buffer account: {err}. Retrying..."); } } - Ok(()) - }) + } + + Ok(buffer.pubkey()) +} + +// Serialize and compress the idl. +fn serialize_idl(idl: &Idl) -> Result> { + let json_bytes = serde_json::to_vec(idl)?; + let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); + e.write_all(&json_bytes)?; + e.finish().map_err(Into::into) +} + +fn serialize_idl_ix(ix_inner: anchor_lang::idl::IdlInstruction) -> Result> { + let mut data = Vec::with_capacity(256); + data.extend_from_slice(&anchor_lang::idl::IDL_IX_TAG.to_le_bytes()); + ix_inner.serialize(&mut data)?; + Ok(data) } fn migrate(cfg_override: &ConfigOverride) -> Result<()> { @@ -3565,6 +4096,7 @@ fn migrate(cfg_override: &ConfigOverride) -> Result<()> { } fn set_workspace_dir_or_exit() { + // First try to find Anchor workspace let d = match Config::discover(&ConfigOverride::default()) { Err(err) => { println!("Workspace configuration error: {err}"); @@ -3572,19 +4104,45 @@ fn set_workspace_dir_or_exit() { } Ok(d) => d, }; + match d { None => { - println!("Not in anchor workspace."); - std::process::exit(1); + // No Anchor.toml found - check for Cargo workspace with Solana programs + let current_dir = match std::env::current_dir() { + Ok(dir) => dir, + Err(_) => { + println!("Unable to determine current directory"); + std::process::exit(1); + } + }; + + let cargo_toml_path = current_dir.join("Cargo.toml"); + if !cargo_toml_path.exists() { + println!("Not in a Solana workspace. This command requires either Anchor.toml or a Cargo workspace with Solana programs."); + std::process::exit(1); + } + + // Check if this is a workspace and has Solana programs + match program::discover_solana_programs(None) { + Ok(programs) if !programs.is_empty() => { + // Found Solana programs in Cargo workspace - stay in current directory + // (already in the right place) + } + _ => { + println!("Not in a Solana workspace. This command requires either Anchor.toml or a Cargo workspace with Solana programs."); + std::process::exit(1); + } + } } Some(cfg) => { + // Found Anchor.toml - change to workspace root match cfg.path().parent() { None => { println!("Unable to make new program"); } Some(parent) => { if std::env::set_current_dir(parent).is_err() { - println!("Not in anchor workspace."); + println!("Not in a Solana workspace. This command requires either Anchor.toml or a Cargo workspace with Solana programs."); std::process::exit(1); } } @@ -3593,34 +4151,58 @@ fn set_workspace_dir_or_exit() { } } -#[cfg(feature = "dev")] -fn airdrop(cfg_override: &ConfigOverride) -> Result<()> { - let url = cfg_override - .cluster - .as_ref() - .unwrap_or(&Cluster::Devnet) - .url(); - loop { - let exit = std::process::Command::new("solana") - .arg("airdrop") - .arg("10") - .arg("--url") - .arg(url) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .output() - .expect("Must airdrop"); - if !exit.status.success() { - println!("There was a problem airdropping: {:?}.", exit); - std::process::exit(exit.status.code().unwrap_or(1)); - } - std::thread::sleep(std::time::Duration::from_millis(10000)); - } +fn airdrop(cfg_override: &ConfigOverride, amount: f64, pubkey: Option) -> Result<()> { + // Get cluster URL and wallet path + let (cluster_url, wallet_path) = get_cluster_and_wallet(cfg_override)?; + + // Create RPC client + let client = RpcClient::new(cluster_url); + + // Determine recipient + let recipient_pubkey = if let Some(pubkey) = pubkey { + pubkey + } else { + // Load keypair from wallet path and get pubkey + let keypair = Keypair::read_from_file(&wallet_path) + .map_err(|e| anyhow!("Failed to read keypair from {}: {}", wallet_path, e))?; + keypair.pubkey() + }; + + // Convert SOL to lamports + let lamports = (amount * 1_000_000_000.0) as u64; + + // Request airdrop + println!("Requesting airdrop of {} SOL", amount); + + let signature = client + .request_airdrop(&recipient_pubkey, lamports) + .map_err(|e| anyhow!("Airdrop request failed: {}", e))?; + + println!("Signature: {}", signature); + + // Confirm the transaction + let recent_blockhash = client + .get_latest_blockhash() + .map_err(|e| anyhow!("Failed to get recent blockhash: {}", e))?; + + client + .confirm_transaction_with_spinner( + &signature, + &recent_blockhash, + CommitmentConfig::confirmed(), + ) + .map_err(|e| anyhow!("Transaction confirmation failed: {}", e))?; + + // Get and display the new balance + let balance = client.get_balance(&recipient_pubkey)?; + println!("{}", format_sol(balance)); + + Ok(()) } fn cluster(_cmd: ClusterCommand) -> Result<()> { println!("Cluster Endpoints:\n"); - println!("* Mainnet - https://api.mainnet-beta.solana.com"); + println!("* Mainnet - {}", DEFAULT_MAINNET_RPC_URL); println!("* Devnet - https://api.devnet.solana.com"); println!("* Testnet - https://api.testnet.solana.com"); Ok(()) @@ -3866,9 +4448,22 @@ fn localnet( let validator_handle = &mut start_test_validator(cfg, &cfg.test_validator, flags, false)?; - // Setup log reader. + // Setup log reader - kept alive until end of scope let url = test_validator_rpc_url(&cfg.test_validator); - let log_streams = stream_logs(cfg, &url); + let log_streams = match stream_logs(cfg, &url) { + Ok(streams) => { + println!( + "Log streams set up successfully ({} streams)", + streams.len() + ); + Some(streams) + } + Err(e) => { + eprintln!("Warning: Failed to setup program log streaming: {:#}", e); + eprintln!(" Program logs will still be visible in the validator output."); + None + } + }; std::io::stdin().lock().lines().next().unwrap().unwrap(); @@ -3881,9 +4476,10 @@ fn localnet( ); } - for mut child in log_streams? { - if let Err(err) = child.kill() { - println!("Failed to kill subprocess {}: {}", child.id(), err); + // Explicitly shutdown log streams - closes WebSocket subscriptions + if let Some(log_streams) = log_streams { + for handle in log_streams { + handle.shutdown(); } } @@ -3905,7 +4501,7 @@ fn with_workspace( let mut cfg = Config::discover(cfg_override) .expect("Previously set the workspace dir") - .expect("Anchor.toml must always exist"); + .expect("This command requires an Anchor workspace."); let r = f(&mut cfg); @@ -3935,7 +4531,7 @@ fn get_node_version() -> Result { Version::parse(output).map_err(Into::into) } -fn add_recommended_deployment_solana_args( +pub fn add_recommended_deployment_solana_args( client: &RpcClient, args: Vec, ) -> Result> { @@ -3975,7 +4571,7 @@ fn add_recommended_deployment_solana_args( Ok(augmented_args) } -fn get_recommended_micro_lamport_fee(client: &RpcClient) -> Result { +pub fn get_recommended_micro_lamport_fee(client: &RpcClient) -> Result { let mut fees = client.get_recent_prioritization_fees(&[])?; if fees.is_empty() { // Fees may be empty, e.g. on localnet @@ -3995,6 +4591,30 @@ fn get_recommended_micro_lamport_fee(client: &RpcClient) -> Result { Ok(median_priority_fee) } +/// Prepend a compute unit ix, if the priority fee is greater than 0. +/// This helps to improve the chances that the transaction will land. +pub fn prepend_compute_unit_ix( + instructions: Vec, + client: &RpcClient, + priority_fee: Option, +) -> Result> { + let priority_fee = match priority_fee { + Some(fee) => fee, + None => get_recommended_micro_lamport_fee(client)?, + }; + + if priority_fee > 0 { + let mut instructions_appended = instructions.clone(); + instructions_appended.insert( + 0, + ComputeBudgetInstruction::set_compute_unit_price(priority_fee), + ); + Ok(instructions_appended) + } else { + Ok(instructions) + } +} + fn get_node_dns_option() -> Result<&'static str> { let version = get_node_version()?; let req = VersionReq::parse(">=16.4.0").unwrap(); @@ -4005,26 +4625,326 @@ fn get_node_dns_option() -> Result<&'static str> { Ok(option) } -// Remove the current workspace directory if it prefixes a string. -// This is used as a workaround for the Solana CLI using the uriparse crate to -// parse args but not handling percent encoding/decoding when using the path as -// a local filesystem path. Removing the workspace prefix handles most/all cases -// of spaces in keypair/binary paths, but this should be fixed in the Solana CLI -// and removed here. -fn strip_workspace_prefix(absolute_path: String) -> String { - let workspace_prefix = - std::env::current_dir().unwrap().display().to_string() + std::path::MAIN_SEPARATOR_STR; - absolute_path - .strip_prefix(&workspace_prefix) - .unwrap_or(&absolute_path) - .into() -} - /// Create a new [`RpcClient`] with `confirmed` commitment level instead of the default(finalized). fn create_client(url: U) -> RpcClient { RpcClient::new_with_commitment(url, CommitmentConfig::confirmed()) } +/// Format lamports as SOL, removing trailing zeros +fn format_sol(lamports: u64) -> String { + let sol = lamports as f64 / 1_000_000_000.0; + let formatted = format!("{:.8}", sol); + + // Remove trailing zeros and decimal point if not needed + let trimmed = formatted.trim_end_matches('0').trim_end_matches('.'); + format!("{} SOL", trimmed) +} + +/// Get cluster URL and wallet path from Anchor config, CLI overrides, or Solana CLI config +fn get_cluster_and_wallet(cfg_override: &ConfigOverride) -> Result<(String, String)> { + // Try to get from Anchor workspace config first + if let Ok(Some(cfg)) = Config::discover(cfg_override) { + return Ok(( + cfg.provider.cluster.url().to_string(), + cfg.provider.wallet.to_string(), + )); + } + + // Try to load Solana CLI config + let (cluster_url, wallet_path) = + if let Some(config_file) = solana_cli_config::CONFIG_FILE.as_ref() { + match SolanaCliConfig::load(config_file) { + Ok(cli_config) => ( + cli_config.json_rpc_url.clone(), + cli_config.keypair_path.clone(), + ), + Err(_) => { + // Fallback to defaults if Solana CLI config doesn't exist + ( + DEFAULT_MAINNET_RPC_URL.to_string(), + get_default_wallet_path()?, + ) + } + } + } else { + // If CONFIG_FILE is not available, use defaults + ( + DEFAULT_MAINNET_RPC_URL.to_string(), + get_default_wallet_path()?, + ) + }; + + // Apply any CLI overrides + let final_cluster_url = cfg_override + .cluster + .as_ref() + .map(|c| c.url().to_string()) + .unwrap_or(cluster_url); + + Ok((final_cluster_url, wallet_path)) +} + +fn address(cfg_override: &ConfigOverride, _confirm_key: bool) -> Result<()> { + // Get wallet path from config or use default + let wallet_path = match Config::discover(cfg_override) { + Ok(Some(cfg)) => cfg.provider.wallet.to_string(), + _ => { + // Not in workspace - use default Solana CLI path + get_default_wallet_path()? + } + }; + + // Load keypair and get pubkey + let keypair = Keypair::read_from_file(&wallet_path) + .map_err(|e| anyhow!("Failed to read keypair from {}: {}", wallet_path, e))?; + + // Print the public key + println!("{}", keypair.pubkey()); + + Ok(()) +} + +fn balance(cfg_override: &ConfigOverride, pubkey: Option, lamports: bool) -> Result<()> { + // Get cluster URL and wallet path + let (cluster_url, wallet_path) = get_cluster_and_wallet(cfg_override)?; + + // Create RPC client + let client = RpcClient::new(cluster_url); + + // Determine which account to check + let account_pubkey = if let Some(pubkey) = pubkey { + pubkey + } else { + // Load keypair from wallet path and get pubkey + let keypair = Keypair::read_from_file(&wallet_path) + .map_err(|e| anyhow!("Failed to read keypair from {}: {}", wallet_path, e))?; + keypair.pubkey() + }; + + // Get balance + let balance = client.get_balance(&account_pubkey)?; + + // Format and display output + if lamports { + println!("{}", balance); + } else { + println!("{}", format_sol(balance)); + } + + Ok(()) +} + +fn epoch(cfg_override: &ConfigOverride) -> Result<()> { + // Get cluster URL + let (cluster_url, _) = get_cluster_and_wallet(cfg_override)?; + + // Create RPC client + let client = RpcClient::new(cluster_url); + + // Get epoch info + let epoch_info = client.get_epoch_info()?; + + // Print just the epoch number + println!("{}", epoch_info.epoch); + + Ok(()) +} + +fn epoch_info(cfg_override: &ConfigOverride) -> Result<()> { + // Get cluster URL + let (cluster_url, _) = get_cluster_and_wallet(cfg_override)?; + + // Create RPC client + let client = RpcClient::new(cluster_url); + + // Get epoch info + let epoch_info = client.get_epoch_info()?; + + // Calculate epoch slot range + let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index; + let last_slot_in_epoch = first_slot_in_epoch + epoch_info.slots_in_epoch; + + // Calculate completion stats + let epoch_completed_percent = + epoch_info.slot_index as f64 / epoch_info.slots_in_epoch as f64 * 100.0; + let remaining_slots = epoch_info.slots_in_epoch - epoch_info.slot_index; + + // Display epoch information (matching Solana CLI format) + println!("Block height: {}", epoch_info.block_height); + println!("Slot: {}", epoch_info.absolute_slot); + println!("Epoch: {}", epoch_info.epoch); + + if let Some(tx_count) = epoch_info.transaction_count { + println!("Transaction Count: {}", tx_count); + } + + println!( + "Epoch Slot Range: [{}..{})", + first_slot_in_epoch, last_slot_in_epoch + ); + println!("Epoch Completed Percent: {:>3.3}%", epoch_completed_percent); + println!( + "Epoch Completed Slots: {}/{} ({} remaining)", + epoch_info.slot_index, epoch_info.slots_in_epoch, remaining_slots + ); + + // Try to calculate epoch completed time + // Get average slot time from performance samples (aggregate up to 60 samples) + if let Ok(samples) = client.get_recent_performance_samples(Some(60)) { + // Aggregate all samples to calculate average slot time + let (total_slots, total_secs) = + samples.iter().fold((0u64, 0u64), |(slots, secs), sample| { + ( + slots.saturating_add(sample.num_slots), + secs.saturating_add(sample.sample_period_secs as u64), + ) + }); + + if total_slots > 0 { + let avg_slot_time_ms = (total_secs * 1000) / total_slots; + + // Calculate time_remaining using average slot time (always estimated) + let remaining_secs = (remaining_slots * avg_slot_time_ms) / 1000; + + // Calculate time_elapsed - try actual block times first, then estimate + // Get the first actual block in the epoch and adjust for slot differences + let start_block_time = client + .get_blocks_with_limit(first_slot_in_epoch, 1) + .ok() + .and_then(|slots| slots.first().cloned()) + .and_then(|first_actual_block| { + client.get_block_time(first_actual_block).ok().map(|time| { + // Adjust backwards if first actual block is after expected start + let slot_diff = first_actual_block.saturating_sub(first_slot_in_epoch); + let time_adjustment = (slot_diff * avg_slot_time_ms / 1000) as i64; + time.saturating_sub(time_adjustment) + }) + }); + + let current_block_time = client.get_block_time(epoch_info.absolute_slot).ok(); + + let (elapsed_secs, is_estimated) = if let (Some(start_time), Some(current_time)) = + (start_block_time, current_block_time) + { + // Use actual block times for elapsed + ((current_time - start_time) as u64, false) + } else { + // Estimate elapsed using average slot time + ((epoch_info.slot_index * avg_slot_time_ms) / 1000, true) + }; + + // Total time = elapsed + remaining + let total_secs = elapsed_secs + remaining_secs; + + let estimated_marker = if is_estimated { "*" } else { "" }; + println!( + "Epoch Completed Time: {}{}/{} ({} remaining)", + format_duration_secs(elapsed_secs), + estimated_marker, + format_duration_secs(total_secs), + format_duration_secs(remaining_secs) + ); + } + } + + Ok(()) +} + +fn logs_subscribe( + cfg_override: &ConfigOverride, + include_votes: bool, + mentions: Option>, +) -> Result<()> { + // Get cluster URL + let (cluster_url, _) = get_cluster_and_wallet(cfg_override)?; + + // Convert HTTP(S) URL to WebSocket URL + // For localhost, the WebSocket port is typically RPC port + 1 + let ws_url = if cluster_url.contains("localhost") || cluster_url.contains("127.0.0.1") { + // Parse the URL to extract and increment the port + cluster_url + .replace("https://", "wss://") + .replace("http://", "ws://") + .replace(":8899", ":8900") // Default test validator ports + } else { + cluster_url + .replace("https://", "wss://") + .replace("http://", "ws://") + }; + + println!("Connecting to {}", ws_url); + + let filter = match (include_votes, mentions) { + (true, Some(mentions)) => { + RpcTransactionLogsFilter::Mentions(mentions.iter().map(|p| p.to_string()).collect()) + } + (true, None) => RpcTransactionLogsFilter::AllWithVotes, + (false, Some(mentions)) => { + RpcTransactionLogsFilter::Mentions(mentions.iter().map(|p| p.to_string()).collect()) + } + (false, None) => RpcTransactionLogsFilter::All, + }; + + let (_client, receiver) = PubsubClient::logs_subscribe( + &ws_url, + filter, + RpcTransactionLogsConfig { + commitment: cfg_override.commitment.map(|c| CommitmentConfig { + commitment: c.into(), + }), + }, + )?; + + loop { + match receiver.recv() { + Ok(logs) => { + println!("Transaction executed in slot {}:", logs.context.slot); + println!(" Signature: {}", logs.value.signature); + println!( + " Status: {}", + logs.value + .err + .map(|err| err.to_string()) + .unwrap_or_else(|| "Ok".to_string()) + ); + println!(" Log Messages:"); + for log in logs.value.logs { + println!(" {log}"); + } + } + Err(err) => { + return Err(anyhow!("Disconnected: {err}")); + } + } + } +} + +/// Format seconds into human-readable duration (e.g., "1day 5h 49m 8s") +fn format_duration_secs(total_seconds: u64) -> String { + let seconds = total_seconds % 60; + let total_minutes = total_seconds / 60; + let minutes = total_minutes % 60; + let total_hours = total_minutes / 60; + let hours = total_hours % 24; + let days = total_hours / 24; + + let mut parts = Vec::new(); + if days > 0 { + parts.push(format!("{}day", days)); + } + if hours > 0 { + parts.push(format!("{}h", hours)); + } + if minutes > 0 { + parts.push(format!("{}m", minutes)); + } + if seconds > 0 || parts.is_empty() { + parts.push(format!("{}s", seconds)); + } + + parts.join(" ") +} + #[cfg(test)] mod tests { use super::*; @@ -4036,6 +4956,7 @@ mod tests { &ConfigOverride { cluster: None, wallet: None, + commitment: None, }, "await".to_string(), true, @@ -4056,6 +4977,7 @@ mod tests { &ConfigOverride { cluster: None, wallet: None, + commitment: None, }, "fn".to_string(), true, @@ -4076,6 +4998,7 @@ mod tests { &ConfigOverride { cluster: None, wallet: None, + commitment: None, }, "1project".to_string(), true, diff --git a/cli/src/program.rs b/cli/src/program.rs new file mode 100644 index 0000000000..b32a4ef86a --- /dev/null +++ b/cli/src/program.rs @@ -0,0 +1,1958 @@ +use anchor_lang::idl::IdlAccount; +use anchor_lang_idl::types::Idl; +use anyhow::{anyhow, bail, Result}; +use solana_client::send_and_confirm_transactions_in_parallel::{ + send_and_confirm_transactions_in_parallel_blocking_v2, SendAndConfirmConfigV2, +}; +use solana_loader_v3_interface::{ + instruction as loader_v3_instruction, state::UpgradeableLoaderState, +}; +use solana_packet::PACKET_DATA_SIZE; +use solana_rpc_client::rpc_client::RpcClient; +use solana_rpc_client_api::config::RpcSendTransactionConfig; +use solana_sdk::{ + commitment_config::CommitmentConfig, + hash::Hash, + message::Message, + pubkey::Pubkey, + signature::Signer, + signer::{keypair::Keypair, EncodableKey}, + transaction::Transaction, +}; +use solana_sdk_ids::bpf_loader_upgradeable as bpf_loader_upgradeable_id; +use std::{ + fs::{self, File}, + io::Write, + path::Path, + sync::Arc, + thread, + time::Duration, +}; + +use crate::{ + config::{Config, Manifest, Program, WithPath}, + ConfigOverride, ProgramCommand, +}; + +/// Parse priority fee from solana args +fn parse_priority_fee_from_args(args: &[String]) -> Option { + args.windows(2) + .find(|pair| pair[0] == "--with-compute-unit-price") + .and_then(|pair| pair[1].parse().ok()) +} + +/// Discover Solana programs from a non-Anchor Cargo workspace +pub fn discover_solana_programs(program_name: Option) -> Result> { + let current_dir = std::env::current_dir()?; + let mut program_paths = Vec::new(); + + // Check if current directory has Cargo.toml + let cargo_toml_path = current_dir.join("Cargo.toml"); + if cargo_toml_path.exists() { + let cargo_content = fs::read_to_string(&cargo_toml_path)?; + let cargo_toml: toml::Value = toml::from_str(&cargo_content)?; + + // Check if it's a workspace Cargo.toml + if let Some(workspace) = cargo_toml.get("workspace") { + // It's a workspace - iterate over members + if let Some(members) = workspace.get("members").and_then(|m| m.as_array()) { + for member in members { + if let Some(member_path) = member.as_str() { + let full_path = current_dir.join(member_path); + if full_path.is_dir() && full_path.join("Cargo.toml").exists() { + program_paths.push(full_path); + } + } + } + } + } else if is_solana_program(¤t_dir)? { + // It's a single program Cargo.toml with cdylib - use current directory + program_paths.push(current_dir.clone()); + } + } + + // If no programs found yet, fallback to looking in programs/ directory + if program_paths.is_empty() { + let programs_dir = current_dir.join("programs"); + if programs_dir.is_dir() { + for entry in fs::read_dir(programs_dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() && path.join("Cargo.toml").exists() { + program_paths.push(path); + } + } + } + } + + // Filter to only Solana programs and build Program structs + let mut programs = Vec::new(); + for path in program_paths { + if !is_solana_program(&path)? { + continue; + } + + let cargo = Manifest::from_path(path.join("Cargo.toml"))?; + let lib_name = cargo.lib_name()?; + + // Check if this is the program we're looking for (if name specified) + if let Some(ref name) = program_name { + let matches = *name == lib_name || *name == path.file_name().unwrap().to_str().unwrap(); + if !matches { + continue; + } + } + + // Try to read IDL if it exists (will be None for non-Anchor programs) + let idl_filepath = current_dir + .join("target") + .join("idl") + .join(&lib_name) + .with_extension("json"); + let idl = fs::read(idl_filepath) + .ok() + .and_then(|bytes| serde_json::from_reader(&*bytes).ok()); + + programs.push(Program { + lib_name, + path: path.canonicalize()?, + idl, + }); + } + + Ok(programs) +} + +/// Check if a given Cargo project is a Solana program +/// A deployable Solana program must have crate-type = ["cdylib", ...] +fn is_solana_program(path: &Path) -> Result { + let cargo_path = path.join("Cargo.toml"); + if !cargo_path.exists() { + return Ok(false); + } + + let cargo_content = fs::read_to_string(&cargo_path)?; + let cargo_toml: toml::Value = toml::from_str(&cargo_content)?; + + // Check if it has cdylib (required for deployable Solana programs) + // This is the definitive marker - libraries and client tools won't have this + if let Some(lib) = cargo_toml.get("lib") { + if let Some(crate_type) = lib.get("crate-type").and_then(|ct| ct.as_array()) { + if crate_type.iter().any(|ct| ct.as_str() == Some("cdylib")) { + return Ok(true); + } + } + } + + Ok(false) +} + +/// Get programs from workspace (Anchor or non-Anchor) +pub fn get_programs_from_workspace( + cfg_override: &ConfigOverride, + program_name: Option, +) -> Result> { + // First try Anchor workspace + if let Some(cfg) = Config::discover(cfg_override)? { + return cfg.get_programs(program_name); + } + + // Fallback to non-Anchor Solana workspace + let programs = discover_solana_programs(program_name.clone())?; + + if programs.is_empty() { + if let Some(name) = program_name { + return Err(anyhow!( + "Program '{}' not found. Make sure you're in a Solana workspace (Anchor or non-Anchor) with programs in the programs/ directory, or provide a program filepath.", + name + )); + } else { + return Err(anyhow!( + "No Solana programs found. Make sure you're in a Solana workspace (Anchor or non-Anchor) with programs in the programs/ directory, or provide a program filepath." + )); + } + } + + Ok(programs) +} + +/// Public entry point for deploying programs - validates and routes to appropriate handler +#[allow(clippy::too_many_arguments)] +pub fn process_deploy( + cfg_override: &ConfigOverride, + program_filepath: Option, + program_name: Option, + program_keypair: Option, + upgrade_authority: Option, + program_id: Option, + buffer: Option, + max_len: Option, + verifiable: bool, + no_idl: bool, + make_final: bool, + solana_args: Vec, +) -> Result<()> { + // If explicit filepath provided, deploy single program + if program_filepath.is_some() { + return program_deploy( + cfg_override, + program_filepath, + program_name, + program_keypair, + upgrade_authority, + program_id, + buffer, + max_len, + no_idl, + make_final, + solana_args, + ); + } + + // Discover from workspace (Anchor or non-Anchor) + let programs = get_programs_from_workspace(cfg_override, program_name.clone())?; + + // Multiple programs and no specific program requested -> deploy all + if programs.len() > 1 && program_name.is_none() { + // Validate that single-program options aren't used + if program_id.is_some() { + return Err(anyhow!( + "Cannot specify --program-id when deploying multiple programs. Use --program-name to deploy a specific program." + )); + } + if buffer.is_some() { + return Err(anyhow!( + "Cannot specify --buffer when deploying multiple programs. Use --program-name to deploy a specific program." + )); + } + if upgrade_authority.is_some() { + return Err(anyhow!( + "Cannot specify --upgrade-authority when deploying multiple programs. Use --program-name to deploy a specific program." + )); + } + if max_len.is_some() { + return Err(anyhow!( + "Cannot specify --max-len when deploying multiple programs. Use --program-name to deploy a specific program." + )); + } + + // Delegate to deploy_workspace + return deploy_workspace( + cfg_override, + None, // program_name - deploy all + program_keypair, + verifiable, + no_idl, + make_final, + solana_args, + ); + } + + // Single program or specific program requested -> deploy single + program_deploy( + cfg_override, + program_filepath, + program_name, + program_keypair, + upgrade_authority, + program_id, + buffer, + max_len, + no_idl, + make_final, + solana_args, + ) +} + +/// Deploy all programs in workspace using native implementation +fn deploy_workspace( + cfg_override: &ConfigOverride, + program_name: Option, + program_keypair: Option, + verifiable: bool, + no_idl: bool, + make_final: bool, + solana_args: Vec, +) -> Result<()> { + // Get programs from workspace (Anchor or non-Anchor) + let programs = get_programs_from_workspace(cfg_override, program_name.clone())?; + + // For Cargo workspaces, we don't have cluster/wallet in config, so just print basic info + if let Ok(Some(cfg)) = Config::discover(cfg_override) { + // Anchor workspace - we have cluster/wallet config + let url = crate::cluster_url(&cfg, &cfg.test_validator); + let keypair = cfg.provider.wallet.to_string(); + println!("Deploying cluster: {url}"); + println!("Upgrade authority: {keypair}"); + } else { + // Cargo workspace - cluster/wallet will come from Solana CLI config or flags + println!("Deploying programs from Cargo workspace"); + } + + for program in programs { + let binary_path = program.binary_path(verifiable); + + println!("\nDeploying program: {}", program.lib_name); + + let program_keypair_filepath = match &program_keypair { + Some(path) => Some(path.clone()), + None => { + // Try to find program keypair + let keypair_path = program + .keypair_file() + .ok() + .map(|kp| kp.path().display().to_string()); + keypair_path + } + }; + + // Use the native program_deploy implementation + program_deploy( + cfg_override, + Some(binary_path.display().to_string()), + None, // program_name - not needed since we have filepath + program_keypair_filepath, + None, // upgrade_authority - uses wallet + None, // program_id - derived from keypair + None, // buffer + None, // max_len + no_idl, + make_final, + solana_args.clone(), + )?; + } + + println!("\nDeploy success"); + Ok(()) +} + +// Main entry point for all program commands +pub fn program(cfg_override: &ConfigOverride, cmd: ProgramCommand) -> Result<()> { + match cmd { + ProgramCommand::Deploy { + program_filepath, + program_name, + program_keypair, + upgrade_authority, + program_id, + buffer, + max_len, + no_idl, + make_final, + solana_args, + } => process_deploy( + cfg_override, + program_filepath, + program_name, + program_keypair, + upgrade_authority, + program_id, + buffer, + max_len, + false, // verifiable + no_idl, + make_final, + solana_args, + ), + ProgramCommand::WriteBuffer { + program_filepath, + program_name, + buffer, + buffer_authority, + max_len, + } => program_write_buffer( + cfg_override, + program_filepath, + program_name, + buffer, + buffer_authority, + max_len, + ), + ProgramCommand::SetBufferAuthority { + buffer, + new_buffer_authority, + } => program_set_buffer_authority(cfg_override, buffer, new_buffer_authority), + ProgramCommand::SetUpgradeAuthority { + program_id, + new_upgrade_authority, + new_upgrade_authority_signer, + skip_new_upgrade_authority_signer_check, + make_final, + upgrade_authority, + } => program_set_upgrade_authority( + cfg_override, + program_id, + new_upgrade_authority, + new_upgrade_authority_signer, + skip_new_upgrade_authority_signer_check, + make_final, + upgrade_authority, + ), + ProgramCommand::Show { + account, + get_programs, + get_buffers, + all, + } => program_show(cfg_override, account, get_programs, get_buffers, all), + ProgramCommand::Upgrade { + program_id, + program_filepath, + buffer, + upgrade_authority, + max_retries, + solana_args, + } => program_upgrade( + cfg_override, + program_id, + program_filepath, + buffer, + upgrade_authority, + max_retries, + solana_args, + ), + ProgramCommand::Dump { + account, + output_file, + } => program_dump(cfg_override, account, output_file), + ProgramCommand::Close { + account, + program_name, + authority, + recipient, + bypass_warning, + } => program_close( + cfg_override, + account, + program_name, + authority, + recipient, + bypass_warning, + ), + ProgramCommand::Extend { + program_id, + program_name, + additional_bytes, + } => program_extend(cfg_override, program_id, program_name, additional_bytes), + } +} + +fn get_rpc_client_and_config( + cfg_override: &ConfigOverride, +) -> Result<(RpcClient, Option>)> { + // Try to discover Anchor config first + let config = Config::discover(cfg_override)?; + + let (url, _wallet_path) = crate::get_cluster_and_wallet(cfg_override)?; + let rpc_client = RpcClient::new_with_commitment(url, CommitmentConfig::confirmed()); + + Ok((rpc_client, config)) +} + +/// Get payer keypair from either Anchor config or Solana CLI config +fn get_payer_keypair( + cfg_override: &ConfigOverride, + config: &Option>, +) -> Result { + if let Some(cfg) = config { + cfg.wallet_kp() + } else { + // No Anchor config - get wallet from Solana CLI config + let (_url, wallet_path) = crate::get_cluster_and_wallet(cfg_override)?; + Keypair::read_from_file(&wallet_path) + .map_err(|e| anyhow!("Failed to read wallet keypair from {}: {}", wallet_path, e)) + } +} + +/// Deploy a single program (either from explicit filepath or workspace) - private implementation +#[allow(clippy::too_many_arguments)] +fn program_deploy( + cfg_override: &ConfigOverride, + program_filepath: Option, + program_name: Option, + program_keypair: Option, + upgrade_authority: Option, + program_id: Option, + buffer: Option, + max_len: Option, + no_idl: bool, + make_final: bool, + solana_args: Vec, +) -> Result<()> { + let (rpc_client, config) = get_rpc_client_and_config(cfg_override)?; + let payer = get_payer_keypair(cfg_override, &config)?; + + // Determine the program filepath + let program_filepath = if let Some(filepath) = program_filepath { + // Explicit filepath provided + filepath + } else { + // Discover from workspace (Anchor or non-Anchor) + let programs = get_programs_from_workspace(cfg_override, program_name.clone())?; + + let program = &programs[0]; + let binary_path = program.binary_path(false); // false = not verifiable build + + println!("Deploying program: {}", program.lib_name); + + binary_path.display().to_string() + }; + + // Augment solana_args with recommended defaults (priority fees, max sign attempts, buffer) + let solana_args = crate::add_recommended_deployment_solana_args(&rpc_client, solana_args)?; + + // Parse priority fee from solana_args + let priority_fee = parse_priority_fee_from_args(&solana_args); + + // Read program data + let program_data = fs::read(&program_filepath) + .map_err(|e| anyhow!("Failed to read program file {}: {}", program_filepath, e))?; + + // Determine program keypair + let loaded_program_keypair = if let Some(keypair_path) = program_keypair { + // Load from specified keypair file + Keypair::read_from_file(&keypair_path).map_err(|e| { + anyhow!( + "Failed to read program keypair from {}: {}", + keypair_path, + e + ) + })? + } else if let Some(_program_id) = program_id { + return Err(anyhow!( + "When --program-id is specified, --program-keypair must also be provided" + )); + } else { + // Auto-detect from target/deploy/{program_name}-keypair.json + let program_name = Path::new(&program_filepath) + .file_stem() + .and_then(|s| s.to_str()) + .ok_or_else(|| anyhow!("Invalid program filepath"))?; + + let keypair_path = format!("target/deploy/{}-keypair.json", program_name); + Keypair::read_from_file(&keypair_path).map_err(|e| { + anyhow!( + "Failed to read program keypair from {}: {}. \ + Use --program-keypair to specify a custom location.", + keypair_path, + e + ) + })? + }; + + let program_id = loaded_program_keypair.pubkey(); + + // Determine upgrade authority + let upgrade_authority = if let Some(auth_path) = upgrade_authority { + let authority_keypair = Keypair::read_from_file(&auth_path) + .map_err(|e| anyhow!("Failed to read upgrade authority keypair: {}", e))?; + println!( + "Using custom upgrade authority: {}", + authority_keypair.pubkey() + ); + authority_keypair + } else { + payer.insecure_clone() + }; + + // Check if program already exists + let program_account = rpc_client.get_account(&program_id); + + if program_account.is_ok() { + // Program exists - validate it can be upgraded BEFORE writing buffer + println!("Program already exists, upgrading..."); + + // Verify program can be upgraded before doing expensive buffer write + verify_program_can_be_upgraded(&rpc_client, &program_id, &upgrade_authority)?; + + // Write to buffer + let buffer_pubkey = if let Some(buffer) = buffer { + buffer + } else { + let buffer_keypair = Keypair::new(); + write_program_buffer( + &rpc_client, + &payer, + &program_data, + &upgrade_authority.pubkey(), + &buffer_keypair, + max_len, + CommitmentConfig::confirmed(), + RpcSendTransactionConfig { + skip_preflight: false, + preflight_commitment: Some(CommitmentConfig::confirmed().commitment), + encoding: None, + max_retries: None, + min_context_slot: None, + }, + )? + }; + + // Upgrade the program (skip verification - already done above at line 324) + upgrade_program( + &rpc_client, + &payer, + &program_id, + &buffer_pubkey, + &upgrade_authority, + priority_fee, + true, // skip_program_verification + )?; + } else { + // New deployment + + let buffer_pubkey = if let Some(buffer) = buffer { + buffer + } else { + let buffer_keypair = Keypair::new(); + write_program_buffer( + &rpc_client, + &payer, + &program_data, + &upgrade_authority.pubkey(), + &buffer_keypair, + max_len, + CommitmentConfig::confirmed(), + RpcSendTransactionConfig { + skip_preflight: false, + preflight_commitment: Some(CommitmentConfig::confirmed().commitment), + encoding: None, + max_retries: None, + min_context_slot: None, + }, + )? + }; + + // Deploy from buffer + let max_data_len = max_len.unwrap_or(program_data.len()); + deploy_program( + &rpc_client, + &payer, + &buffer_pubkey, + &loaded_program_keypair, + &upgrade_authority, + max_data_len, + priority_fee, + )?; + } + + // Print the program ID + println!("Program ID: {}", program_id); + + // Deploy IDL if not skipped + if !no_idl { + // Extract program name from filepath + let program_name = Path::new(&program_filepath) + .file_stem() + .and_then(|s| s.to_str()) + .ok_or_else(|| anyhow!("Invalid program filepath"))?; + + // Look for IDL file in target/idl/{program_name}.json + let idl_filepath = format!("target/idl/{}.json", program_name); + + if Path::new(&idl_filepath).exists() { + // Read and update the IDL with the program address + let idl_content = fs::read_to_string(&idl_filepath) + .map_err(|e| anyhow!("Failed to read IDL file {}: {}", idl_filepath, e))?; + + let mut idl: Idl = serde_json::from_str(&idl_content) + .map_err(|e| anyhow!("Failed to parse IDL file {}: {}", idl_filepath, e))?; + + // Update the IDL with the program address + idl.address = program_id.to_string(); + + // Write the updated IDL back to the file + let idl_json = serde_json::to_string_pretty(&idl) + .map_err(|e| anyhow!("Failed to serialize IDL: {}", e))?; + fs::write(&idl_filepath, idl_json) + .map_err(|e| anyhow!("Failed to write IDL file {}: {}", idl_filepath, e))?; + + // Wait for the program to be confirmed before initializing IDL to prevent + // race condition where the program isn't yet available in validator cache + let max_retries = 5; + let retry_delay = Duration::from_millis(500); + let cache_delay = Duration::from_secs(2); + + for attempt in 0..max_retries { + if let Ok(account) = rpc_client.get_account(&program_id) { + if account.executable { + thread::sleep(cache_delay); + break; + } + } + + if attempt == max_retries - 1 { + println!("Failed"); + return Err(anyhow!( + "Timeout waiting for program {} to be confirmed", + program_id + )); + } + + thread::sleep(retry_delay); + } + + // Check if IDL account already exists + let idl_address = IdlAccount::address(&program_id); + let idl_account_exists = rpc_client.get_account(&idl_address).is_ok(); + + if idl_account_exists { + // IDL account exists, upgrade it + crate::idl_upgrade(cfg_override, program_id, idl_filepath, None)?; + } else { + // IDL account doesn't exist, create it + crate::idl_init(cfg_override, program_id, idl_filepath, None)?; + } + + println!("✓ Idl account created: {}", idl_address); + } else { + println!( + "Warning: IDL file not found at {}, skipping IDL deployment", + idl_filepath + ); + } + } + + // Make program immutable if --final flag is set + if make_final { + println!("\nMaking program immutable..."); + + let set_authority_ix = loader_v3_instruction::set_upgrade_authority( + &program_id, + &upgrade_authority.pubkey(), + None, // None = remove upgrade authority = immutable + ); + + let recent_blockhash = rpc_client.get_latest_blockhash()?; + let tx = Transaction::new_signed_with_payer( + &[set_authority_ix], + Some(&payer.pubkey()), + &[&payer, &upgrade_authority], + recent_blockhash, + ); + + rpc_client + .send_and_confirm_transaction(&tx) + .map_err(|e| anyhow!("Failed to make program immutable: {}", e))?; + + println!("✓ Program is now immutable (cannot be upgraded)"); + } + + Ok(()) +} + +/// Verify that a buffer account is valid for upgrading +fn verify_buffer_account( + rpc_client: &RpcClient, + buffer_pubkey: &Pubkey, + buffer_authority: &Pubkey, +) -> Result<()> { + let buffer_account = rpc_client + .get_account(buffer_pubkey) + .map_err(|e| anyhow!("Buffer account {} not found: {}", buffer_pubkey, e))?; + + // Check if buffer is owned by BPF Upgradeable Loader + if buffer_account.owner != bpf_loader_upgradeable_id::id() { + return Err(anyhow!( + "Buffer account {} is not owned by the BPF Upgradeable Loader", + buffer_pubkey + )); + } + + // Verify it's actually a Buffer account + match bincode::deserialize::(&buffer_account.data) { + Ok(UpgradeableLoaderState::Buffer { authority_address }) => { + // Check if buffer is immutable + if authority_address.is_none() { + return Err(anyhow!("Buffer {} is immutable", buffer_pubkey)); + } + // Verify the authority matches + if authority_address != Some(*buffer_authority) { + return Err(anyhow!( + "Buffer's authority {:?} does not match authority provided {}", + authority_address, + buffer_authority + )); + } + } + Ok(_) => { + return Err(anyhow!("Account {} is not a Buffer account", buffer_pubkey)); + } + Err(e) => { + return Err(anyhow!( + "Failed to deserialize buffer account {}: {}", + buffer_pubkey, + e + )); + } + } + + Ok(()) +} + +/// Verify that a program exists, is upgradeable, and the authority matches +/// This should be called BEFORE doing expensive operations like buffer writes +fn verify_program_can_be_upgraded( + rpc_client: &RpcClient, + program_id: &Pubkey, + upgrade_authority: &Keypair, +) -> Result<()> { + // Verify the program exists + let program_account = rpc_client + .get_account(program_id) + .map_err(|e| anyhow!("Failed to get program account {}: {}", program_id, e))?; + + if program_account.owner != bpf_loader_upgradeable_id::id() { + return Err(anyhow!( + "Program {} is not an upgradeable program", + program_id + )); + } + + // Check if this is a valid program and get the ProgramData address + let programdata_address = + match bincode::deserialize::(&program_account.data) { + Ok(UpgradeableLoaderState::Program { + programdata_address, + }) => programdata_address, + _ => { + return Err(anyhow!( + "{} is not an upgradeable program account", + program_id + )); + } + }; + + // Verify the ProgramData account exists and is valid + let programdata_account = rpc_client.get_account(&programdata_address).map_err(|e| { + anyhow!( + "Failed to get ProgramData account: {}. The program may have been closed.", + e + ) + })?; + + // Verify it's a valid ProgramData account + match bincode::deserialize::(&programdata_account.data) { + Ok(UpgradeableLoaderState::ProgramData { + upgrade_authority_address, + .. + }) => { + // Check if the program is immutable + if upgrade_authority_address.is_none() { + return Err(anyhow!( + "Program {} is immutable and cannot be upgraded", + program_id + )); + } + // Verify the authority matches + if upgrade_authority_address != Some(upgrade_authority.pubkey()) { + return Err(anyhow!( + "Upgrade authority mismatch. Expected {:?}, but ProgramData has {:?}", + Some(upgrade_authority.pubkey()), + upgrade_authority_address + )); + } + } + _ => { + return Err(anyhow!( + "Program {} has been closed or is in an invalid state", + program_id + )); + } + } + + Ok(()) +} + +#[allow(deprecated)] +fn deploy_program( + rpc_client: &RpcClient, + payer: &Keypair, + buffer: &Pubkey, + program_keypair: &Keypair, + upgrade_authority: &Keypair, + max_data_len: usize, + priority_fee: Option, +) -> Result<()> { + let program_id = program_keypair.pubkey(); + let mut deploy_ixs = loader_v3_instruction::deploy_with_max_program_len( + &payer.pubkey(), + &program_id, + buffer, + &upgrade_authority.pubkey(), + rpc_client + .get_minimum_balance_for_rent_exemption(UpgradeableLoaderState::size_of_program())?, + max_data_len, + ) + .map_err(|e| anyhow!("Failed to create deploy instruction: {}", e))?; + + // Add priority fee if specified + deploy_ixs = crate::prepend_compute_unit_ix(deploy_ixs, rpc_client, priority_fee)?; + + let recent_blockhash = rpc_client.get_latest_blockhash()?; + let deploy_tx = Transaction::new_signed_with_payer( + &deploy_ixs, + Some(&payer.pubkey()), + &[payer, program_keypair, upgrade_authority], + recent_blockhash, + ); + + rpc_client + .send_and_confirm_transaction(&deploy_tx) + .map_err(|e| anyhow!("Failed to deploy program: {}", e))?; + + Ok(()) +} + +fn upgrade_program( + rpc_client: &RpcClient, + payer: &Keypair, + program_id: &Pubkey, + buffer: &Pubkey, + upgrade_authority: &Keypair, + priority_fee: Option, + skip_program_verification: bool, +) -> Result<()> { + // Verify program can be upgraded (unless caller already verified) + if !skip_program_verification { + verify_program_can_be_upgraded(rpc_client, program_id, upgrade_authority)?; + } + + // Verify the buffer account is valid + verify_buffer_account(rpc_client, buffer, &upgrade_authority.pubkey())?; + + println!("Sending upgrade transaction..."); + + let upgrade_ix = loader_v3_instruction::upgrade( + program_id, + buffer, + &upgrade_authority.pubkey(), + &payer.pubkey(), + ); + + // Add priority fee if specified + let upgrade_ixs = crate::prepend_compute_unit_ix(vec![upgrade_ix], rpc_client, priority_fee)?; + + let recent_blockhash = rpc_client.get_latest_blockhash()?; + let upgrade_tx = Transaction::new_signed_with_payer( + &upgrade_ixs, + Some(&payer.pubkey()), + &[payer, upgrade_authority], + recent_blockhash, + ); + + let signature = rpc_client + .send_and_confirm_transaction(&upgrade_tx) + .map_err(|e| anyhow!("Failed to upgrade program: {}", e))?; + println!("Signature: {}", signature); + Ok(()) +} + +fn program_write_buffer( + cfg_override: &ConfigOverride, + program_filepath: Option, + program_name: Option, + _buffer: Option, + buffer_authority: Option, + max_len: Option, +) -> Result<()> { + let (rpc_client, config) = get_rpc_client_and_config(cfg_override)?; + let payer = get_payer_keypair(cfg_override, &config)?; + + // Determine the program filepath + let program_filepath = if let Some(filepath) = program_filepath { + filepath + } else { + // Discover from workspace (Anchor or non-Anchor) + let programs = get_programs_from_workspace(cfg_override, program_name.clone())?; + + if programs.len() > 1 && program_name.is_none() { + let program_names: Vec<_> = programs.iter().map(|p| p.lib_name.as_str()).collect(); + return Err(anyhow!( + "Multiple programs found: {}. Use --program-name to specify which one to write", + program_names.join(", ") + )); + } + + let program = &programs[0]; + let binary_path = program.binary_path(false); + + println!("Writing buffer for program: {}", program.lib_name); + + binary_path.display().to_string() + }; + + // Read program data + let program_data = fs::read(&program_filepath) + .map_err(|e| anyhow!("Failed to read program file {}: {}", program_filepath, e))?; + + // Determine buffer authority + let buffer_authority_keypair = if let Some(auth_path) = buffer_authority { + Keypair::read_from_file(&auth_path) + .map_err(|e| anyhow!("Failed to read buffer authority keypair: {}", e))? + } else { + payer.insecure_clone() + }; + + let buffer_keypair = Keypair::new(); + let buffer_pubkey = write_program_buffer( + &rpc_client, + &payer, + &program_data, + &buffer_authority_keypair.pubkey(), + &buffer_keypair, + max_len, + CommitmentConfig::confirmed(), + RpcSendTransactionConfig { + skip_preflight: false, + preflight_commitment: Some(CommitmentConfig::confirmed().commitment), + encoding: None, + max_retries: None, + min_context_slot: None, + }, + )?; + + println!("Buffer: {}", buffer_pubkey); + Ok(()) +} + +fn program_set_buffer_authority( + cfg_override: &ConfigOverride, + buffer: Pubkey, + new_buffer_authority: Pubkey, +) -> Result<()> { + let (rpc_client, config) = get_rpc_client_and_config(cfg_override)?; + let payer = get_payer_keypair(cfg_override, &config)?; + + println!("Setting buffer authority..."); + println!("Buffer: {}", buffer); + println!("New authority: {}", new_buffer_authority); + + let set_authority_ixs = loader_v3_instruction::set_buffer_authority( + &buffer, + &payer.pubkey(), + &new_buffer_authority, + ); + + let recent_blockhash = rpc_client.get_latest_blockhash()?; + let tx = Transaction::new_signed_with_payer( + &[set_authority_ixs], + Some(&payer.pubkey()), + &[&payer], + recent_blockhash, + ); + + rpc_client + .send_and_confirm_transaction(&tx) + .map_err(|e| anyhow!("Failed to set buffer authority: {}", e))?; + + println!("Buffer authority updated successfully!"); + Ok(()) +} + +fn program_set_upgrade_authority( + cfg_override: &ConfigOverride, + program_id: Pubkey, + new_upgrade_authority: Option, + new_upgrade_authority_signer: Option, + skip_new_upgrade_authority_signer_check: bool, + make_final: bool, + current_upgrade_authority: Option, +) -> Result<()> { + let (rpc_client, config) = get_rpc_client_and_config(cfg_override)?; + let payer = get_payer_keypair(cfg_override, &config)?; + + // Validate that this is a Program account, not ProgramData + let program_account = rpc_client + .get_account(&program_id) + .map_err(|e| anyhow!("Failed to get account {}: {}", program_id, e))?; + + if program_account.owner != bpf_loader_upgradeable_id::id() { + return Err(anyhow!( + "Account {} is not owned by the BPF Upgradeable Loader", + program_id + )); + } + + // Ensure this is a Program account, not ProgramData or Buffer + match bincode::deserialize::(&program_account.data) { + Ok(UpgradeableLoaderState::Program { .. }) => { + // Valid program account + } + Ok(UpgradeableLoaderState::ProgramData { .. }) => { + return Err(anyhow!( + "Error: {} is a ProgramData account, not a Program account.\n\n\ + To set the upgrade authority, you must provide the Program ID, not the ProgramData address.\n\ + Use 'anchor program show {}' to find the associated Program ID.", + program_id, + program_id + )); + } + Ok(UpgradeableLoaderState::Buffer { .. }) => { + return Err(anyhow!( + "{} is a Buffer account, not a Program account. Use set-buffer-authority for buffers.", + program_id + )); + } + _ => { + return Err(anyhow!("{} is not a valid upgradeable program", program_id)); + } + } + + println!("Setting upgrade authority..."); + println!("Program ID: {}", program_id); + + if make_final { + println!("Making program immutable (cannot be upgraded)"); + } else if let Some(new_auth) = new_upgrade_authority { + println!("New upgrade authority: {}", new_auth); + } else { + bail!("Must provide either --new-upgrade-authority or --final"); + } + + // Determine current authority keypair (must be a signer) + let current_authority_keypair = if let Some(auth_path) = current_upgrade_authority { + let keypair = Keypair::read_from_file(&auth_path) + .map_err(|e| anyhow!("Failed to read current upgrade authority keypair: {}", e))?; + println!("Using custom current authority: {}", keypair.pubkey()); + keypair + } else { + payer.insecure_clone() + }; + + // Validate signer requirements and load keypair + let new_auth_keypair_opt = if let Some(signer_path) = new_upgrade_authority_signer { + // Signer provided - use checked mode + let keypair = Keypair::read_from_file(&signer_path) + .map_err(|e| anyhow!("Failed to read new upgrade authority signer keypair: {}", e))?; + + // Verify the pubkey matches if both are provided + if let Some(pubkey) = new_upgrade_authority { + if pubkey != keypair.pubkey() { + return Err(anyhow!( + "New upgrade authority pubkey mismatch: --new-upgrade-authority ({}) \ + doesn't match --new-upgrade-authority-signer keypair ({})", + pubkey, + keypair.pubkey() + )); + } + } + + println!("Using CHECKED mode - both current and new authority will sign (recommended)"); + Some(keypair) + } else if new_upgrade_authority.is_some() && !make_final { + // No signer provided but new authority specified + if skip_new_upgrade_authority_signer_check { + // User explicitly allowed unchecked mode + println!("WARNING: Using UNCHECKED mode - only current authority will sign"); + println!(" This is less safe! The new authority won't verify ownership."); + None + } else { + // By default, require the signer for safety + return Err(anyhow!( + "New upgrade authority signer is required for safety.\n\ + Please provide --new-upgrade-authority-signer (recommended),\n\ + or use --skip-new-upgrade-authority-signer-check if you're confident the pubkey is correct." + )); + } + } else { + // Making program final or no new authority - no signer needed + None + }; + + // Build instruction based on mode + let set_authority_ixs = if let Some(ref new_auth_keypair) = new_auth_keypair_opt { + // Checked mode: both current and new authority sign (safer) + loader_v3_instruction::set_upgrade_authority_checked( + &program_id, + ¤t_authority_keypair.pubkey(), + &new_auth_keypair.pubkey(), + ) + } else { + // Unchecked mode or final mode: only current authority signs + loader_v3_instruction::set_upgrade_authority( + &program_id, + ¤t_authority_keypair.pubkey(), + new_upgrade_authority.as_ref(), + ) + }; + + let recent_blockhash = rpc_client.get_latest_blockhash()?; + + let signature = if let Some(ref new_auth_keypair) = new_auth_keypair_opt { + // Checked mode with 3 signers + let tx = Transaction::new_signed_with_payer( + &[set_authority_ixs], + Some(&payer.pubkey()), + &[&payer, ¤t_authority_keypair, new_auth_keypair], + recent_blockhash, + ); + rpc_client + .send_and_confirm_transaction(&tx) + .map_err(|e| anyhow!("Failed to set upgrade authority: {}", e))? + } else { + // Unchecked mode or final mode with 2 signers + let tx = Transaction::new_signed_with_payer( + &[set_authority_ixs], + Some(&payer.pubkey()), + &[&payer, ¤t_authority_keypair], + recent_blockhash, + ); + rpc_client + .send_and_confirm_transaction(&tx) + .map_err(|e| anyhow!("Failed to set upgrade authority: {}", e))? + }; + + println!(); + println!("Upgrade authority updated successfully!"); + println!("Signature: {}", signature); + Ok(()) +} + +fn program_show( + cfg_override: &ConfigOverride, + account: Pubkey, + _get_programs: bool, + _get_buffers: bool, + _all: bool, +) -> Result<()> { + let (rpc_client, _config) = get_rpc_client_and_config(cfg_override)?; + + let account_data = rpc_client + .get_account(&account) + .map_err(|e| anyhow!("Failed to get account {}: {}", account, e))?; + + println!("Account: {}", account); + println!("Owner: {}", account_data.owner); + println!("Balance: {} lamports", account_data.lamports); + println!("Data length: {} bytes", account_data.data.len()); + println!("Executable: {}", account_data.executable); + + // Try to parse as upgradeable loader state + if account_data.owner == bpf_loader_upgradeable_id::id() { + match bincode::deserialize::(&account_data.data) { + Ok(state) => match state { + UpgradeableLoaderState::Uninitialized => { + println!("Type: Uninitialized"); + } + UpgradeableLoaderState::Buffer { authority_address } => { + println!("Type: Buffer"); + if let Some(authority) = authority_address { + println!("Authority: {}", authority); + } else { + println!("Authority: None (immutable)"); + } + } + UpgradeableLoaderState::Program { + programdata_address, + } => { + println!("Type: Program"); + println!("Program Data Address: {}", programdata_address); + + // Fetch program data account + if let Ok(programdata_account) = rpc_client.get_account(&programdata_address) { + if let Ok(UpgradeableLoaderState::ProgramData { + slot, + upgrade_authority_address, + }) = bincode::deserialize::( + &programdata_account.data, + ) { + println!("Slot: {}", slot); + if let Some(authority) = upgrade_authority_address { + println!("Upgrade Authority: {}", authority); + } else { + println!("Upgrade Authority: None (immutable)"); + } + } + } + } + UpgradeableLoaderState::ProgramData { + slot, + upgrade_authority_address, + } => { + println!("Type: Program Data"); + println!("Slot: {}", slot); + if let Some(authority) = upgrade_authority_address { + println!("Upgrade Authority: {}", authority); + } else { + println!("Upgrade Authority: None (immutable)"); + } + } + }, + Err(e) => { + println!("Failed to parse as upgradeable loader state: {}", e); + } + } + } + + Ok(()) +} + +fn program_upgrade( + cfg_override: &ConfigOverride, + program_id: Pubkey, + program_filepath: Option, + buffer: Option, + upgrade_authority: Option, + max_retries: u32, + solana_args: Vec, +) -> Result<()> { + let (rpc_client, config) = get_rpc_client_and_config(cfg_override)?; + let payer = get_payer_keypair(cfg_override, &config)?; + + // Augment solana_args with recommended defaults if provided + let solana_args = if !solana_args.is_empty() { + crate::add_recommended_deployment_solana_args(&rpc_client, solana_args)? + } else { + solana_args + }; + + // Parse priority fee from solana_args + let priority_fee = parse_priority_fee_from_args(&solana_args); + + // Determine upgrade authority + let upgrade_authority_keypair = if let Some(auth_path) = upgrade_authority { + let keypair = Keypair::read_from_file(&auth_path) + .map_err(|e| anyhow!("Failed to read upgrade authority keypair: {}", e))?; + println!("Using custom upgrade authority: {}", keypair.pubkey()); + keypair + } else { + payer.insecure_clone() + }; + + // Verify the program can be upgraded BEFORE doing expensive operations + // This prevents wasting time/money on buffer writes if the program is closed or immutable + verify_program_can_be_upgraded(&rpc_client, &program_id, &upgrade_authority_keypair)?; + + // Case 1: Using existing buffer (no retries needed) + if let Some(buffer_pubkey) = buffer { + return upgrade_program( + &rpc_client, + &payer, + &program_id, + &buffer_pubkey, + &upgrade_authority_keypair, + priority_fee, + true, // skip_program_verification - already done above + ); + } + + // Case 2: Creating buffer from program file (with retries) + let program_filepath = program_filepath + .ok_or_else(|| anyhow!("Either --program-filepath or --buffer must be provided"))?; + + let program_data = fs::read(&program_filepath) + .map_err(|e| anyhow!("Failed to read program file {}: {}", program_filepath, e))?; + + // Retry loop for buffer creation and upgrade + for retry in 0..(1 + max_retries) { + if max_retries > 0 { + println!("\nAttempt {}/{}", retry + 1, max_retries + 1); + } + + // Create a new buffer for each attempt + let buffer_keypair = Keypair::new(); + + // Write to buffer + let result = write_program_buffer( + &rpc_client, + &payer, + &program_data, + &upgrade_authority_keypair.pubkey(), + &buffer_keypair, + None, // max_len + CommitmentConfig::confirmed(), + RpcSendTransactionConfig { + skip_preflight: false, + preflight_commitment: Some(CommitmentConfig::confirmed().commitment), + encoding: None, + max_retries: None, + min_context_slot: None, + }, + ); + + let buffer_pubkey = match result { + Ok(pubkey) => pubkey, + Err(e) => { + println!("Buffer write failed: {}", e); + if retry < max_retries { + println!("Retrying {} more time(s)...", max_retries - retry); + continue; + } else { + return Err(e); + } + } + }; + + // Upgrade the program (skip verification - already done before retry loop) + let result = upgrade_program( + &rpc_client, + &payer, + &program_id, + &buffer_pubkey, + &upgrade_authority_keypair, + priority_fee, + true, // skip_program_verification + ); + + match result { + Ok(_) => { + if max_retries > 0 { + println!("\nUpgrade success"); + } + return Ok(()); + } + Err(e) => { + println!("Upgrade failed: {}", e); + if retry < max_retries { + println!("Retrying {} more time(s)...", max_retries - retry); + } else { + return Err(e); + } + } + } + } + + Ok(()) +} + +fn program_dump(cfg_override: &ConfigOverride, account: Pubkey, output_file: String) -> Result<()> { + let (rpc_client, _config) = get_rpc_client_and_config(cfg_override)?; + + println!("Fetching program data..."); + + let account_data = rpc_client + .get_account(&account) + .map_err(|e| anyhow!("Failed to get account {}: {}", account, e))?; + + // Check if this is a program account + let program_data = if account_data.owner == bpf_loader_upgradeable_id::id() { + match bincode::deserialize::(&account_data.data) { + Ok(UpgradeableLoaderState::Program { + programdata_address, + }) => { + // Fetch the program data account + let programdata_account = rpc_client + .get_account(&programdata_address) + .map_err(|e| anyhow!("Failed to get program data account: {}", e))?; + + // Skip the UpgradeableLoaderState header + let data_offset = UpgradeableLoaderState::size_of_programdata_metadata(); + programdata_account.data[data_offset..].to_vec() + } + Ok(UpgradeableLoaderState::Buffer { .. }) => { + // Buffer account - skip the header + let data_offset = UpgradeableLoaderState::size_of_buffer_metadata(); + account_data.data[data_offset..].to_vec() + } + Ok(UpgradeableLoaderState::ProgramData { .. }) => { + // Program data account - skip the header + let data_offset = UpgradeableLoaderState::size_of_programdata_metadata(); + account_data.data[data_offset..].to_vec() + } + _ => account_data.data, + } + } else { + // Regular program or other account + account_data.data + }; + + println!("Writing {} bytes to {}...", program_data.len(), output_file); + + let mut file = + File::create(&output_file).map_err(|e| anyhow!("Failed to create output file: {}", e))?; + + file.write_all(&program_data) + .map_err(|e| anyhow!("Failed to write program data: {}", e))?; + + println!("Program dumped to {}", output_file); + Ok(()) +} + +fn program_close( + cfg_override: &ConfigOverride, + account: Option, + program_name: Option, + authority: Option, + recipient: Option, + bypass_warning: bool, +) -> Result<()> { + let (rpc_client, config) = get_rpc_client_and_config(cfg_override)?; + let payer = get_payer_keypair(cfg_override, &config)?; + + // Determine the account to close + let account = if let Some(acc) = account { + acc + } else if let Some(name) = program_name { + // Discover from workspace (Anchor or non-Anchor) + let programs = get_programs_from_workspace(cfg_override, Some(name.clone()))?; + + let program = &programs[0]; + + // Get the program keypair to derive program ID + let keypair_path = program.keypair_file()?.path().display().to_string(); + let program_keypair = Keypair::read_from_file(&keypair_path).map_err(|e| { + anyhow!( + "Failed to read program keypair from {}: {}", + keypair_path, + e + ) + })?; + + let program_id = program_keypair.pubkey(); + println!("Closing program: {} ({})", program.lib_name, program_id); + program_id + } else { + return Err(anyhow!( + "Must provide either account address or --program-name" + )); + }; + + // Fetch the account to determine its type + let account_data = rpc_client + .get_account(&account) + .map_err(|e| anyhow!("Failed to get account {}: {}", account, e))?; + + // Check if this is a BPF Loader Upgradeable account + if account_data.owner != bpf_loader_upgradeable_id::id() { + return Err(anyhow!( + "Account {} is not owned by the BPF Loader Upgradeable program", + account + )); + } + + // Determine which account to actually close + let (account_to_close, account_type, program_account) = + match bincode::deserialize::(&account_data.data) { + Ok(UpgradeableLoaderState::Program { + programdata_address, + }) => (programdata_address, "ProgramData", Some(account)), + Ok(UpgradeableLoaderState::Buffer { .. }) => (account, "Buffer", None), + Ok(UpgradeableLoaderState::ProgramData { .. }) => (account, "ProgramData", None), + _ => { + return Err(anyhow!( + "Account {} is not a Buffer, Program, or ProgramData account", + account + )); + } + }; + + // Determine authority + let authority_keypair = if let Some(auth_path) = authority { + Keypair::read_from_file(&auth_path) + .map_err(|e| anyhow!("Failed to read authority keypair: {}", e))? + } else { + payer.insecure_clone() + }; + + // Determine recipient + let recipient_pubkey = recipient.unwrap_or_else(|| authority_keypair.pubkey()); + + if !bypass_warning { + println!(); + println!( + "WARNING: This will close the {} account and reclaim all lamports.", + account_type + ); + + if account_type == "ProgramData" { + println!(); + println!( + "IMPORTANT: Closing the ProgramData account will make the program non-upgradeable" + ); + println!("and the program will become immutable. This action cannot be undone!"); + } + + println!(); + print!("Continue? (y/n): "); + std::io::Write::flush(&mut std::io::stdout())?; + + let mut input = String::new(); + std::io::stdin().read_line(&mut input)?; + + if !input.trim().eq_ignore_ascii_case("y") { + println!("Cancelled"); + return Ok(()); + } + } + + println!("Closing {} account...", account_type); + + let close_ixs = loader_v3_instruction::close_any( + &account_to_close, + &recipient_pubkey, + Some(&authority_keypair.pubkey()), + program_account.as_ref(), + ); + + let recent_blockhash = rpc_client.get_latest_blockhash()?; + let tx = Transaction::new_signed_with_payer( + &[close_ixs], + Some(&payer.pubkey()), + &[&payer, &authority_keypair], + recent_blockhash, + ); + + rpc_client + .send_and_confirm_transaction(&tx) + .map_err(|e| anyhow!("Failed to close account: {}", e))?; + + println!("{} account closed", account_type); + println!("Reclaimed lamports sent to: {}", recipient_pubkey); + Ok(()) +} + +fn program_extend( + cfg_override: &ConfigOverride, + program_id: Option, + program_name: Option, + additional_bytes: usize, +) -> Result<()> { + let (rpc_client, config) = get_rpc_client_and_config(cfg_override)?; + let payer = get_payer_keypair(cfg_override, &config)?; + + if additional_bytes == 0 { + return Err(anyhow!("Additional bytes must be greater than zero")); + } + + // Determine the program ID + let program_id = if let Some(id) = program_id { + id + } else if let Some(name) = program_name { + // Discover from workspace (Anchor or non-Anchor) + let programs = get_programs_from_workspace(cfg_override, Some(name.clone()))?; + + let program = &programs[0]; + + // Get the program keypair to derive program ID + let keypair_path = program.keypair_file()?.path().display().to_string(); + let program_keypair = Keypair::read_from_file(&keypair_path).map_err(|e| { + anyhow!( + "Failed to read program keypair from {}: {}", + keypair_path, + e + ) + })?; + + let program_id = program_keypair.pubkey(); + println!("Extending program: {} ({})", program.lib_name, program_id); + program_id + } else { + return Err(anyhow!("Must provide either program ID or --program-name")); + }; + + println!("Extending program data..."); + println!("Program ID: {}", program_id); + println!("Additional bytes: {}", additional_bytes); + + // Get the program account to find the ProgramData address + let program_account = rpc_client + .get_account(&program_id) + .map_err(|e| anyhow!("Failed to get program account {}: {}", program_id, e))?; + + if program_account.owner != bpf_loader_upgradeable_id::id() { + return Err(anyhow!( + "Account {} is not an upgradeable program", + program_id + )); + } + + // Get the ProgramData address + let programdata_address = + match bincode::deserialize::(&program_account.data) { + Ok(UpgradeableLoaderState::Program { + programdata_address, + }) => programdata_address, + _ => { + return Err(anyhow!( + "{} is not an upgradeable program account", + program_id + )); + } + }; + + // Get the ProgramData account to verify upgrade authority + let programdata_account = rpc_client + .get_account(&programdata_address) + .map_err(|e| anyhow!("Program {} is closed: {}", program_id, e))?; + + // Get the upgrade authority address + let upgrade_authority_address = + match bincode::deserialize::(&programdata_account.data) { + Ok(UpgradeableLoaderState::ProgramData { + upgrade_authority_address, + .. + }) => upgrade_authority_address, + _ => { + return Err(anyhow!("Program {} is closed", program_id)); + } + }; + + let upgrade_authority_address = upgrade_authority_address + .ok_or_else(|| anyhow!("Program {} is not upgradeable", program_id))?; + + // Verify the payer is the upgrade authority + if upgrade_authority_address != payer.pubkey() { + return Err(anyhow!( + "Upgrade authority mismatch. Expected {}, but ProgramData has {}", + payer.pubkey(), + upgrade_authority_address + )); + } + + // Use the checked version which requires upgrade authority signature + let extend_ix = loader_v3_instruction::extend_program_checked( + &program_id, + &upgrade_authority_address, + Some(&payer.pubkey()), + additional_bytes as u32, + ); + + let recent_blockhash = rpc_client.get_latest_blockhash()?; + let tx = Transaction::new_signed_with_payer( + &[extend_ix], + Some(&payer.pubkey()), + &[&payer], // payer is also the upgrade authority + recent_blockhash, + ); + + rpc_client + .send_and_confirm_transaction(&tx) + .map_err(|e| anyhow!("Failed to extend program: {}", e))?; + + println!("Program extended succesfully!"); + Ok(()) +} + +// ========== Agave's core parallel deployment functions ========== + +pub fn calculate_max_chunk_size(baseline_msg: Message) -> usize { + let tx_size = bincode::serialized_size(&Transaction { + signatures: vec![ + solana_sdk::signature::Signature::default(); + baseline_msg.header.num_required_signatures as usize + ], + message: baseline_msg, + }) + .unwrap() as usize; + // add 1 byte buffer to account for shortvec encoding + PACKET_DATA_SIZE.saturating_sub(tx_size).saturating_sub(1) +} + +#[allow(clippy::too_many_arguments)] +pub fn send_deploy_messages( + rpc_client: &RpcClient, + initial_message: Option, + write_messages: Vec, + final_message: Option, + fee_payer_signer: &dyn Signer, + initial_signer: Option<&dyn Signer>, + write_signer: Option<&dyn Signer>, + final_signers: Option<&[&dyn Signer]>, + max_sign_attempts: usize, + commitment: CommitmentConfig, + send_transaction_config: RpcSendTransactionConfig, +) -> Result> { + // Handle initial message (e.g., buffer creation) + if let Some(message) = initial_message { + if let Some(initial_signer) = initial_signer { + let mut initial_transaction = Transaction::new_unsigned(message.clone()); + let blockhash = rpc_client.get_latest_blockhash()?; + + // Sign based on number of required signatures + if message.header.num_required_signatures == 3 { + initial_transaction.try_sign( + &[fee_payer_signer, initial_signer, write_signer.unwrap()], + blockhash, + )?; + } else if message.header.num_required_signatures == 2 { + initial_transaction.try_sign(&[fee_payer_signer, initial_signer], blockhash)?; + } else { + initial_transaction.try_sign(&[fee_payer_signer], blockhash)?; + } + + rpc_client + .send_and_confirm_transaction_with_spinner_and_config( + &initial_transaction, + commitment, + send_transaction_config, + ) + .map_err(|err| anyhow!("Account allocation failed: {}", err))?; + } else { + return Err(anyhow!( + "Buffer account not created yet, must provide a key pair" + )); + } + } + + if !write_messages.is_empty() { + if let Some(write_signer) = write_signer { + send_messages_in_batches( + rpc_client, + &write_messages, + &[fee_payer_signer, write_signer], + max_sign_attempts, + commitment, + send_transaction_config, + )?; + } + } + + if let Some(message) = final_message { + if let Some(final_signers) = final_signers { + let mut final_tx = Transaction::new_unsigned(message); + let blockhash = rpc_client.get_latest_blockhash()?; + let mut signers = final_signers.to_vec(); + signers.push(fee_payer_signer); + final_tx.try_sign(&signers, blockhash)?; + + return Ok(Some( + rpc_client + .send_and_confirm_transaction_with_spinner_and_config( + &final_tx, + commitment, + send_transaction_config, + ) + .map_err(|e| anyhow!("Deploying program failed: {}", e))?, + )); + } + } + + Ok(None) +} + +/// Complete buffer writing implementation +#[allow(clippy::too_many_arguments)] +pub fn write_program_buffer( + rpc_client: &RpcClient, + payer: &dyn Signer, + program_data: &[u8], + buffer_authority: &Pubkey, + buffer_keypair: &dyn Signer, + max_len: Option, + commitment: CommitmentConfig, + send_transaction_config: RpcSendTransactionConfig, +) -> Result { + let buffer_pubkey = buffer_keypair.pubkey(); + + let program_len = program_data.len(); + let buffer_len = max_len.unwrap_or(program_len); + + // Calculate required lamports for buffer + let buffer_data_len = UpgradeableLoaderState::size_of_buffer(buffer_len); + let min_balance = rpc_client + .get_minimum_balance_for_rent_exemption(buffer_data_len) + .map_err(|e| anyhow!("Failed to get rent exemption: {}", e))?; + + // Get blockhash for all messages + let blockhash = rpc_client.get_latest_blockhash()?; + + // Create buffer initialization message + let initial_instructions = loader_v3_instruction::create_buffer( + &payer.pubkey(), + &buffer_pubkey, + buffer_authority, + min_balance, + buffer_len, + ) + .map_err(|e| anyhow!("Failed to create buffer instruction: {}", e))?; + + let initial_message = Some(Message::new_with_blockhash( + &initial_instructions, + Some(&payer.pubkey()), + &blockhash, + )); + + // Prepare all write messages upfront + let write_messages = prepare_write_messages( + program_data, + &buffer_pubkey, + buffer_authority, + &payer.pubkey(), + &blockhash, + ); + + const MAX_SIGN_ATTEMPTS: usize = 5; + send_deploy_messages( + rpc_client, + initial_message, + write_messages, + None, + payer, + Some(buffer_keypair), + Some(payer), + None, + MAX_SIGN_ATTEMPTS, + commitment, + send_transaction_config, + )?; + Ok(buffer_pubkey) +} + +/// Prepare write messages +fn prepare_write_messages( + program_data: &[u8], + buffer_pubkey: &Pubkey, + buffer_authority: &Pubkey, + fee_payer: &Pubkey, + blockhash: &Hash, +) -> Vec { + let create_msg = |offset: u32, bytes: Vec| { + let instruction = + loader_v3_instruction::write(buffer_pubkey, buffer_authority, offset, bytes); + Message::new_with_blockhash(&[instruction], Some(fee_payer), blockhash) + }; + + let mut write_messages = Vec::new(); + let chunk_size = calculate_max_chunk_size(create_msg(0, Vec::new())); + + for (chunk, i) in program_data.chunks(chunk_size).zip(0usize..) { + let offset = i.saturating_mul(chunk_size); + write_messages.push(create_msg(offset as u32, chunk.to_vec())); + } + + write_messages +} + +/// Send messages in parallel +fn send_messages_in_batches( + rpc_client: &RpcClient, + messages: &[Message], + signers: &[&dyn Signer], + max_sign_attempts: usize, + commitment: CommitmentConfig, + send_config: RpcSendTransactionConfig, +) -> Result<()> { + // Use parallel send and confirm function + // Create a new RpcClient with the same URL and wrap in Arc for parallel processing + let url = rpc_client.url(); + let new_rpc_client = RpcClient::new_with_commitment(url, commitment); + let rpc_client_arc = Arc::new(new_rpc_client); + + let transaction_errors = send_and_confirm_transactions_in_parallel_blocking_v2( + rpc_client_arc, + None, + messages, + signers, + SendAndConfirmConfigV2 { + resign_txs_count: Some(max_sign_attempts), + with_spinner: true, + rpc_send_transaction_config: send_config, + }, + ) + .map_err(|err| anyhow!("Data writes to account failed: {}", err))? + .into_iter() + .flatten() + .collect::>(); + + if !transaction_errors.is_empty() { + for transaction_error in &transaction_errors { + eprintln!("{:?}", transaction_error); + } + return Err(anyhow!( + "{} write transactions failed", + transaction_errors.len() + )); + } + + Ok(()) +} diff --git a/tests/bench/bench.json b/tests/bench/bench.json index c1cdd9ae23..1643aaa214 100644 --- a/tests/bench/bench.json +++ b/tests/bench/bench.json @@ -1677,81 +1677,81 @@ "solanaVersion": "2.3.0", "result": { "binarySize": { - "bench": 966096 + "bench": 1025312 }, "computeUnits": { "accountInfo1": 685, "accountInfo2": 1053, "accountInfo4": 1750, "accountInfo8": 3135, - "accountEmptyInit1": 4915, + "accountEmptyInit1": 4878, "accountEmpty1": 774, - "accountEmptyInit2": 8793, + "accountEmptyInit2": 8755, "accountEmpty2": 1174, - "accountEmptyInit4": 16495, + "accountEmptyInit4": 16503, "accountEmpty4": 1962, - "accountEmptyInit8": 31997, + "accountEmptyInit8": 32005, "accountEmpty8": 3548, - "accountSizedInit1": 5019, - "accountSized1": 813, - "accountSizedInit2": 8981, - "accountSized2": 1222, - "accountSizedInit4": 16850, - "accountSized4": 2046, - "accountSizedInit8": 32653, - "accountSized8": 3690, - "accountUnsizedInit1": 5127, - "accountUnsized1": 853, - "accountUnsizedInit2": 9131, - "accountUnsized2": 1284, - "accountUnsizedInit4": 17198, - "accountUnsized4": 2147, - "accountUnsizedInit8": 33324, - "accountUnsized8": 3867, - "boxedAccountEmptyInit1": 5007, + "accountSizedInit1": 4982, + "accountSized1": 822, + "accountSizedInit2": 8940, + "accountSized2": 1240, + "accountSizedInit4": 16858, + "accountSized4": 2082, + "accountSizedInit8": 32661, + "accountSized8": 3762, + "accountUnsizedInit1": 5090, + "accountUnsized1": 874, + "accountUnsizedInit2": 9139, + "accountUnsized2": 1326, + "accountUnsizedInit4": 17206, + "accountUnsized4": 2231, + "accountUnsizedInit8": 33332, + "accountUnsized8": 4035, + "boxedAccountEmptyInit1": 4970, "boxedAccountEmpty1": 864, - "boxedAccountEmptyInit2": 8906, + "boxedAccountEmptyInit2": 8868, "boxedAccountEmpty2": 1286, - "boxedAccountEmptyInit4": 16648, + "boxedAccountEmptyInit4": 16656, "boxedAccountEmpty4": 2115, - "boxedAccountEmptyInit8": 32231, + "boxedAccountEmptyInit8": 32239, "boxedAccountEmpty8": 3801, - "boxedAccountSizedInit1": 5103, + "boxedAccountSizedInit1": 5066, "boxedAccountSized1": 912, - "boxedAccountSizedInit2": 9075, - "boxedAccountSized2": 1337, - "boxedAccountSizedInit4": 16960, - "boxedAccountSized4": 2195, - "boxedAccountSizedInit8": 32831, - "boxedAccountSized8": 3935, - "boxedAccountUnsizedInit1": 5202, - "boxedAccountUnsized1": 943, - "boxedAccountUnsizedInit2": 9251, - "boxedAccountUnsized2": 1392, - "boxedAccountUnsizedInit4": 17284, - "boxedAccountUnsized4": 2283, - "boxedAccountUnsizedInit8": 33455, - "boxedAccountUnsized8": 4089, - "boxedInterfaceAccountMint1": 1110, - "boxedInterfaceAccountMint2": 1534, - "boxedInterfaceAccountMint4": 2370, - "boxedInterfaceAccountMint8": 4064, - "boxedInterfaceAccountToken1": 1246, - "boxedInterfaceAccountToken2": 1794, - "boxedInterfaceAccountToken4": 2878, - "boxedInterfaceAccountToken8": 5068, - "interfaceAccountMint1": 1126, - "interfaceAccountMint2": 1562, - "interfaceAccountMint4": 2432, - "interfaceAccountMint8": 4163, - "interfaceAccountToken1": 1268, - "interfaceAccountToken2": 1849, - "interfaceAccountToken4": 2997, + "boxedAccountSizedInit2": 9036, + "boxedAccountSized2": 1355, + "boxedAccountSizedInit4": 16968, + "boxedAccountSized4": 2231, + "boxedAccountSizedInit8": 32839, + "boxedAccountSized8": 4007, + "boxedAccountUnsizedInit1": 5165, + "boxedAccountUnsized1": 964, + "boxedAccountUnsizedInit2": 9210, + "boxedAccountUnsized2": 1434, + "boxedAccountUnsizedInit4": 17292, + "boxedAccountUnsized4": 2367, + "boxedAccountUnsizedInit8": 33463, + "boxedAccountUnsized8": 4257, + "boxedInterfaceAccountMint1": 1112, + "boxedInterfaceAccountMint2": 1538, + "boxedInterfaceAccountMint4": 2378, + "boxedInterfaceAccountMint8": 4080, + "boxedInterfaceAccountToken1": 1248, + "boxedInterfaceAccountToken2": 1798, + "boxedInterfaceAccountToken4": 2886, + "boxedInterfaceAccountToken8": 5084, + "interfaceAccountMint1": 1128, + "interfaceAccountMint2": 1566, + "interfaceAccountMint4": 2440, + "interfaceAccountMint8": 4179, + "interfaceAccountToken1": 1270, + "interfaceAccountToken2": 1853, + "interfaceAccountToken4": 3005, "interface1": 878, "interface2": 1023, "interface4": 1301, "interface8": 1867, - "program1": 890, + "program1": 898, "program2": 1051, "program4": 1345, "program8": 1943, diff --git a/tests/cpi-returns/tests/cpi-return.ts b/tests/cpi-returns/tests/cpi-return.ts index 75c608e599..cf6044043b 100644 --- a/tests/cpi-returns/tests/cpi-return.ts +++ b/tests/cpi-returns/tests/cpi-return.ts @@ -44,7 +44,7 @@ describe("CPI return", () => { systemProgram: SystemProgram.programId, }) .signers([cpiReturn]) - .rpc(); + .rpc(confirmOptions); }); it("can return u64 from a cpi", async () => { @@ -57,6 +57,7 @@ describe("CPI return", () => { .rpc(confirmOptions); let t = await provider.connection.getTransaction(tx, { commitment: "confirmed", + maxSupportedTransactionVersion: 0, }); const [key, data, buffer] = getReturnLog(t); @@ -98,6 +99,7 @@ describe("CPI return", () => { .rpc(confirmOptions); let t = await provider.connection.getTransaction(tx, { commitment: "confirmed", + maxSupportedTransactionVersion: 0, }); const [key, data, buffer] = getReturnLog(t); @@ -136,6 +138,7 @@ describe("CPI return", () => { .rpc(confirmOptions); let t = await provider.connection.getTransaction(tx, { commitment: "confirmed", + maxSupportedTransactionVersion: 0, }); const [key, data, buffer] = getReturnLog(t); diff --git a/tests/events/tests/events.ts b/tests/events/tests/events.ts index b90689052f..4205a30636 100644 --- a/tests/events/tests/events.ts +++ b/tests/events/tests/events.ts @@ -2,6 +2,7 @@ import * as anchor from "@coral-xyz/anchor"; import { assert } from "chai"; import { Events } from "../target/types/events"; +import { ConfirmOptions } from "@solana/web3.js"; describe("Events", () => { // Configure the client to use the local cluster. @@ -15,17 +16,27 @@ describe("Events", () => { }; type Event = anchor.IdlEvents; + + const config: ConfirmOptions = { + commitment: "confirmed", + preflightCommitment: "confirmed", + skipPreflight: true, + maxRetries: 3, + } as const; + const getEvent = async ( eventName: E, methodName: keyof typeof program["methods"] ) => { let listenerId: number; - const event = await new Promise((res) => { + const eventPromise = new Promise((res) => { listenerId = program.addEventListener(eventName, (event) => { res(event); }); - program.methods[methodName]().rpc(); }); + + await program.methods[methodName]().rpc(config); + const event = await eventPromise; await program.removeEventListener(listenerId); return event;