From 2f27bea780322bc7225afe55a0ee9777d56f8856 Mon Sep 17 00:00:00 2001 From: Edgar Date: Thu, 26 Jan 2023 00:10:09 -0500 Subject: [PATCH 1/6] wip wip orca remove IDL nits --- Cargo.lock | 756 +++++++++++++++++++++++++++++++-- Cargo.toml | 1 + cli/src/main.rs | 2 +- orca_client/Cargo.toml | 34 ++ orca_client/src/main.rs | 118 +++++ orca_client/src/orca.rs | 131 ++++++ orca_client/src/orca_utils.rs | 120 ++++++ orca_client/src/send_bundle.rs | 127 ++++++ searcher_client/src/lib.rs | 2 +- 9 files changed, 1251 insertions(+), 40 deletions(-) create mode 100644 orca_client/Cargo.toml create mode 100644 orca_client/src/main.rs create mode 100644 orca_client/src/orca.rs create mode 100644 orca_client/src/orca_utils.rs create mode 100644 orca_client/src/send_bundle.rs diff --git a/Cargo.lock b/Cargo.lock index 07317f1..e4e51cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,182 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "anchor-attribute-access-control" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.50", + "quote 1.0.23", + "regex", + "syn 1.0.107", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-syn", + "anyhow", + "bs58 0.4.0", + "proc-macro2 1.0.50", + "quote 1.0.23", + "rustversion", + "syn 1.0.107", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-syn", + "proc-macro2 1.0.50", + "syn 1.0.107", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-syn", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "anchor-attribute-interface" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-syn", + "anyhow", + "heck 0.3.3", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "anchor-attribute-state" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "anchor-client" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-lang", + "anyhow", + "regex", + "serde", + "solana-client", + "solana-sdk", + "thiserror", + "url", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "anchor-lang" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-attribute-access-control", + "anchor-attribute-account", + "anchor-attribute-constant", + "anchor-attribute-error", + "anchor-attribute-event", + "anchor-attribute-interface", + "anchor-attribute-program", + "anchor-attribute-state", + "anchor-derive-accounts", + "arrayref", + "base64 0.13.1", + "bincode", + "borsh 0.9.3", + "bytemuck", + "solana-program", + "thiserror", +] + +[[package]] +name = "anchor-spl" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anchor-lang", + "solana-program", + "spl-associated-token-account", + "spl-token", +] + +[[package]] +name = "anchor-syn" +version = "0.20.1" +source = "git+https://github.com/project-serum/anchor?tag=v0.20.1#a81ff88d76956533a4ca5ae74d5dec37d7d76b51" +dependencies = [ + "anyhow", + "bs58 0.3.1", + "heck 0.3.3", + "proc-macro2 1.0.50", + "proc-macro2-diagnostics", + "quote 1.0.23", + "serde", + "serde_json", + "sha2 0.9.9", + "syn 1.0.107", + "thiserror", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -216,9 +392,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.62" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "689894c2db1ea643a50834b999abf1c110887402542955ff5451dab8f861f9ed" +checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1" dependencies = [ "proc-macro2 1.0.50", "quote 1.0.23", @@ -329,29 +505,85 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +[[package]] +name = "borsh" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b13fa9bf62be34702e5ee4526aff22530ae22fe34a0c4290d30d5e4e782e6" +dependencies = [ + "borsh-derive 0.7.2", +] + [[package]] name = "borsh" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" dependencies = [ - "borsh-derive", + "borsh-derive 0.9.3", "hashbrown 0.11.2", ] +[[package]] +name = "borsh-derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6aaa45f8eec26e4bf71e7e5492cf53a91591af8f871f422d550e7cc43f6b927" +dependencies = [ + "borsh-derive-internal 0.7.2", + "borsh-schema-derive-internal 0.7.2", + "proc-macro2 1.0.50", + "syn 1.0.107", +] + +[[package]] +name = "borsh-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307f3740906bac2c118a8122fe22681232b244f1369273e45f1156b45c43d2dd" +dependencies = [ + "borsh-derive-internal 0.8.2", + "borsh-schema-derive-internal 0.8.2", + "proc-macro-crate 0.1.5", + "proc-macro2 1.0.50", + "syn 1.0.107", +] + [[package]] name = "borsh-derive" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" dependencies = [ - "borsh-derive-internal", - "borsh-schema-derive-internal", + "borsh-derive-internal 0.9.3", + "borsh-schema-derive-internal 0.9.3", "proc-macro-crate 0.1.5", "proc-macro2 1.0.50", "syn 1.0.107", ] +[[package]] +name = "borsh-derive-internal" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61621b9d3cca65cc54e2583db84ef912d59ae60d2f04ba61bc0d7fc57556bda2" +dependencies = [ + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2104c73179359431cc98e016998f2f23bc7a05bc53e79741bcba705f30047bc" +dependencies = [ + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + [[package]] name = "borsh-derive-internal" version = "0.9.3" @@ -363,6 +595,28 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "borsh-schema-derive-internal" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b38abfda570837b0949c2c7ebd31417e15607861c23eacb2f668c69f6f3bf7" +dependencies = [ + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae29eb8418fcd46f723f8691a2ac06857d31179d33d2f2d91eb13967de97c728" +dependencies = [ + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + [[package]] name = "borsh-schema-derive-internal" version = "0.9.3" @@ -395,6 +649,12 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" + [[package]] name = "bs58" version = "0.4.0" @@ -417,6 +677,27 @@ dependencies = [ "serde", ] +[[package]] +name = "bytecheck" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +dependencies = [ + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + [[package]] name = "bytemuck" version = "1.13.0" @@ -543,9 +824,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.1" +version = "4.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2" +checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" dependencies = [ "bitflags", "clap_derive 4.1.0", @@ -990,9 +1271,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "encode_unicode" @@ -1054,6 +1335,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "errno" version = "0.2.8" @@ -1342,6 +1636,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "histogram" version = "0.6.9" @@ -1608,7 +1908,7 @@ dependencies = [ "chrono", "clap 3.2.23", "crossbeam-channel", - "env_logger", + "env_logger 0.9.3", "futures", "futures-util", "histogram", @@ -1647,9 +1947,9 @@ version = "0.1.0" dependencies = [ "bincode", "chrono", - "clap 4.1.1", + "clap 4.1.4", "crossbeam-channel", - "env_logger", + "env_logger 0.9.3", "futures", "futures-util", "histogram", @@ -1924,6 +2224,66 @@ dependencies = [ "winapi", ] +[[package]] +name = "mpl-token-auth-rules" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a14e1ac5350734fd07f17d7eab733d27b7b45ca963642c565ec34ab015d9ceda" +dependencies = [ + "borsh 0.9.3", + "mpl-token-metadata-context-derive", + "num-derive", + "num-traits", + "rmp-serde", + "serde", + "shank", + "solana-program", + "solana-zk-token-sdk", + "thiserror", +] + +[[package]] +name = "mpl-token-metadata" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5accdfde5c18465c65bd664169845a185f8a00d973d5c15396fef858dd2e52d9" +dependencies = [ + "arrayref", + "borsh 0.9.3", + "mpl-token-auth-rules", + "mpl-token-metadata-context-derive", + "mpl-utils", + "num-derive", + "num-traits", + "shank", + "solana-program", + "spl-associated-token-account", + "spl-token", + "thiserror", +] + +[[package]] +name = "mpl-token-metadata-context-derive" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12989bc45715b0ee91944855130131479f9c772e198a910c3eb0ea327d5bffc3" +dependencies = [ + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "mpl-utils" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6195ce98b92f1d0ea06d0cc9b2392d81673e02b8fb063589926fa73ee6b071a" +dependencies = [ + "arrayref", + "borsh 0.9.3", + "solana-program", + "spl-token", +] + [[package]] name = "multimap" version = "0.8.3" @@ -1952,6 +2312,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "ntapi" version = "0.3.7" @@ -2072,20 +2441,20 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" dependencies = [ - "proc-macro-crate 1.2.1", + "proc-macro-crate 1.3.0", "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", @@ -2124,6 +2493,36 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "orca_client" +version = "0.1.0" +dependencies = [ + "anchor-client", + "anchor-lang", + "anchor-spl", + "anyhow", + "clap 4.1.4", + "env_logger 0.10.0", + "futures", + "jito-protos", + "jito-searcher-client", + "log", + "rust_decimal", + "rust_decimal_macros", + "solana-client", + "solana-client-helpers", + "solana-metrics", + "solana-program", + "solana-sdk", + "solana-transaction-status", + "spl-memo", + "spl-token", + "thiserror", + "tokio", + "tonic", + "whirlpool", +] + [[package]] name = "os_str_bytes" version = "6.4.1" @@ -2178,6 +2577,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + [[package]] name = "pbkdf2" version = "0.4.0" @@ -2308,13 +2713,12 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -2359,6 +2763,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" +dependencies = [ + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", + "version_check", + "yansi", +] + [[package]] name = "prost" version = "0.8.0" @@ -2410,6 +2827,26 @@ dependencies = [ "prost", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + [[package]] name = "qstring" version = "0.7.2" @@ -2582,9 +3019,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -2650,6 +3087,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "rend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +dependencies = [ + "bytecheck", +] + [[package]] name = "reqwest" version = "0.11.14" @@ -2706,6 +3152,53 @@ dependencies = [ "winapi", ] +[[package]] +name = "rkyv" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +dependencies = [ + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +dependencies = [ + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "rmp" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b13be192e0220b8afb7222aa5813cb62cc269ebb5cac346ca6487681d2913e" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rpassword" version = "6.0.1" @@ -2718,12 +3211,46 @@ dependencies = [ "winapi", ] +[[package]] +name = "rust_decimal" +version = "1.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe32e8c89834541077a5c5bbe5691aa69324361e27e6aeb3552a737db4a70c8" +dependencies = [ + "arrayvec", + "borsh 0.9.3", + "bytecheck", + "byteorder", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rust_decimal_macros" +version = "1.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71a78314ee3b7e684f34f1574fe0935cac8eb453217974473be82f032d9b4ee" +dependencies = [ + "quote 1.0.23", + "rust_decimal", +] + [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + [[package]] name = "rustc_version" version = "0.4.0" @@ -2876,11 +3403,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "security-framework" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645926f31b250a2dca3c232496c2d898d91036e45ca0e97e0e2390c54e11be36" +checksum = "7c4437699b6d34972de58652c68b98cb5b53a4199ab126db8e20ec8ded29a721" dependencies = [ "bitflags", "core-foundation", @@ -3026,6 +3559,40 @@ dependencies = [ "keccak", ] +[[package]] +name = "shank" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63e565b5e95ad88ab38f312e89444c749360641c509ef2de0093b49f55974a5" +dependencies = [ + "shank_macro", +] + +[[package]] +name = "shank_macro" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63927d22a1e8b74bda98cc6e151fcdf178b7abb0dc6c4f81e0bbf5ffe2fc4ec8" +dependencies = [ + "proc-macro2 1.0.50", + "quote 1.0.23", + "shank_macro_impl", + "syn 1.0.107", +] + +[[package]] +name = "shank_macro_impl" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce03403df682f80f4dc1efafa87a4d0cb89b03726d0565e6364bdca5b9a441" +dependencies = [ + "anyhow", + "proc-macro2 1.0.50", + "quote 1.0.23", + "serde", + "syn 1.0.107", +] + [[package]] name = "shell-words" version = "1.1.0" @@ -3091,7 +3658,7 @@ dependencies = [ "Inflector", "base64 0.13.1", "bincode", - "bs58", + "bs58 0.4.0", "bv", "lazy_static", "serde", @@ -3172,7 +3739,7 @@ dependencies = [ "async-trait", "base64 0.13.1", "bincode", - "bs58", + "bs58 0.4.0", "bytes", "clap 2.34.0", "crossbeam-channel", @@ -3216,6 +3783,20 @@ dependencies = [ "url", ] +[[package]] +name = "solana-client-helpers" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b109be3d34e0d16fd424c0dbd23a18dad4b522f8c98c14e56423ce9b28b67c" +dependencies = [ + "solana-client", + "solana-sdk", + "spl-associated-token-account", + "spl-token", + "spl-token-swap", + "thiserror", +] + [[package]] name = "solana-config-program" version = "1.14.13" @@ -3263,7 +3844,7 @@ dependencies = [ "ahash", "blake3", "block-buffer 0.9.0", - "bs58", + "bs58 0.4.0", "bv", "byteorder", "cc", @@ -3306,7 +3887,7 @@ version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447d16a70a1b5383736ef44801050c0e1affd022303b22ed899352f958c2de4b" dependencies = [ - "env_logger", + "env_logger 0.9.3", "lazy_static", "log", ] @@ -3394,9 +3975,9 @@ dependencies = [ "bincode", "bitflags", "blake3", - "borsh", - "borsh-derive", - "bs58", + "borsh 0.9.3", + "borsh-derive 0.9.3", + "bs58 0.4.0", "bv", "bytemuck", "cc", @@ -3499,8 +4080,8 @@ dependencies = [ "base64 0.13.1", "bincode", "bitflags", - "borsh", - "bs58", + "borsh 0.9.3", + "bs58 0.4.0", "bytemuck", "byteorder", "chrono", @@ -3546,7 +4127,7 @@ version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d0acbad862093ea123f3a27364336dcb0c8373522cd6810496a34e932c56c1" dependencies = [ - "bs58", + "bs58 0.4.0", "proc-macro2 1.0.50", "quote 1.0.23", "rustversion", @@ -3591,8 +4172,8 @@ dependencies = [ "Inflector", "base64 0.13.1", "bincode", - "borsh", - "bs58", + "borsh 0.9.3", + "bs58 0.4.0", "lazy_static", "log", "serde", @@ -3702,7 +4283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbc000f0fdf1f12f99d77d398137c1751345b18c88258ce0f99b7872cf6c9bd6" dependencies = [ "assert_matches", - "borsh", + "borsh 0.9.3", "num-derive", "num-traits", "solana-program", @@ -3711,6 +4292,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "spl-math" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ecdd22720b9e5ab578a862928f5010ca197419502bdace600ccd5d23dae9352" +dependencies = [ + "borsh 0.7.2", + "borsh-derive 0.8.2", + "num-derive", + "num-traits", + "solana-program", + "thiserror", + "uint 0.8.5", +] + [[package]] name = "spl-memo" version = "3.0.1" @@ -3753,6 +4349,28 @@ dependencies = [ "thiserror", ] +[[package]] +name = "spl-token-swap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c63b79be6174568e8724912b15e62d0c6b0424ac98397e9a5a867ac2881553af" +dependencies = [ + "arrayref", + "enum_dispatch", + "num-derive", + "num-traits", + "solana-program", + "spl-math", + "spl-token", + "thiserror", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.8.0" @@ -4062,6 +4680,23 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + [[package]] name = "tonic" version = "0.5.2" @@ -4217,6 +4852,30 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "uint" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9db035e67dfaf7edd9aebfe8676afcd63eed53c8a4044fed514c8cccf1835177" +dependencies = [ + "byteorder", + "crunchy", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unicode-bidi" version = "0.3.10" @@ -4470,6 +5129,21 @@ dependencies = [ "once_cell", ] +[[package]] +name = "whirlpool" +version = "0.1.0" +source = "git+https://github.com/orca-so/whirlpools?tag=0.2.0#34040c0645094dbf70ed31480a7d5c0804371620" +dependencies = [ + "anchor-lang", + "anchor-spl", + "borsh 0.9.3", + "mpl-token-metadata", + "solana-program", + "spl-token", + "thiserror", + "uint 0.9.5", +] + [[package]] name = "winapi" version = "0.3.9" @@ -4594,6 +5268,12 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "yasna" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 59e4881..cfe4356 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,6 @@ members = [ "backrun", "cli", "jito_protos", + "orca_client", "searcher_client", ] diff --git a/cli/src/main.rs b/cli/src/main.rs index f509775..535e406 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -114,7 +114,7 @@ async fn main() { let mut client = get_searcher_client(&args.block_engine_url, &keypair) .await - .expect("connects to searcher client"); + .expect("connects to searcher whirlpools"); match args.command { Commands::NextScheduledLeader => { diff --git a/orca_client/Cargo.toml b/orca_client/Cargo.toml new file mode 100644 index 0000000..37a36ad --- /dev/null +++ b/orca_client/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "orca_client" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +futures = "0.3.1" +solana-client = "=1.14.13" +solana-metrics = "=1.14.13" +solana-sdk = "=1.14.13" +solana-transaction-status = "=1.14.13" +solana-program="1.14.13" +spl-token = "3.5.0" +spl-memo = "3.0.1" +thiserror = "1.0.34" +tokio = { version = "~1.14.1", features = ["rt-multi-thread"] } +tonic = { version = "0.5.2", features = ["tls", "tls-roots", "tls-webpki-roots"] } +log = "0.4.17" +jito-protos = { path = "../jito_protos" } +jito-searcher-client = { path = "../searcher_client" } +solana-client-helpers = "1.1.0" +clap = {version = "4.1.4", features = ["derive", "env"]} + +anchor-lang = { git = "https://github.com/project-serum/anchor", tag = "v0.20.1", version = "0.20.1", package = "anchor-lang" } +anchor-spl = { git = "https://github.com/project-serum/anchor", tag = "v0.20.1", version = "0.20.1", package = "anchor-spl" } +anchor-client = { git = "https://github.com/project-serum/anchor", tag = "v0.20.1", version = "0.20.1", package = "anchor-client" } +anyhow = "1.0.68" +whirlpool = { git = "https://github.com/orca-so/whirlpools", tag = "0.2.0", package = "whirlpool", features = ["cpi"] } +rust_decimal = { version = "1.23", features = ["maths"] } +rust_decimal_macros = "1.23" +env_logger = "0.10.0" + diff --git a/orca_client/src/main.rs b/orca_client/src/main.rs new file mode 100644 index 0000000..943cf75 --- /dev/null +++ b/orca_client/src/main.rs @@ -0,0 +1,118 @@ +mod orca; +mod orca_utils; +mod send_bundle; + +use std::{env, rc::Rc, sync::Arc}; + +use anchor_client::{Client as AnchorClient, Cluster}; +use clap::Parser; +use env_logger::TimestampPrecision; +use jito_searcher_client::get_searcher_client; +use orca::swap; +use send_bundle::build_and_send_bundle; +use solana_client::rpc_client::RpcClient; +use solana_client_helpers::Client as SolanaClient; +use solana_sdk::{ + commitment_config::CommitmentConfig, + signature::{read_keypair_file, Signer}, + transaction::{Transaction, VersionedTransaction}, +}; +use spl_memo::build_memo; + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about=None)] +struct Args { + #[clap(long, env)] + rpc_url: String, + + #[clap(long, env)] + rpc_ws_url: String, + + #[clap(long, env)] + keypair_path: String, + + #[clap(long, env)] + block_engine_url: String, +} + +/// Example of how to build orca swap from SAMO-> USDC and submit as a bundle +fn main() { + let args: Args = Args::parse(); + if env::var("RUST_LOG").is_err() { + env::set_var("RUST_LOG", "info") + } + env_logger::builder() + .format_timestamp(Some(TimestampPrecision::Micros)) + .init(); + + let connection = + RpcClient::new_with_commitment(args.rpc_url.to_string(), CommitmentConfig::confirmed()); + + let payer = + read_keypair_file(args.keypair_path.clone()).expect("example requires a keypair file"); + + let payer_pubkey = payer.pubkey(); + + let solana_client = SolanaClient { + client: connection, + payer, + }; + let auth_keypair = + Arc::new(read_keypair_file(args.keypair_path.clone()).expect("reads keypair at path")); + + // build Anchor client + let cluster = Cluster::Custom(args.rpc_url.clone(), args.rpc_ws_url); + let payer = + read_keypair_file(args.keypair_path.clone()).expect("example requires a keypair file"); + let anchor_client = AnchorClient::new_with_options( + cluster.clone(), + Rc::new(payer), + CommitmentConfig::confirmed(), + ); + + let program = anchor_client.program(whirlpool::id()); + let instructions = swap(100000, &solana_client, program, payer_pubkey).unwrap(); + + let blockhash = solana_client + .client + .get_latest_blockhash() + .expect("get blockhash"); + + let payer = + read_keypair_file(args.keypair_path.clone()).expect("example requires a keypair file"); + // TODO: make these mutable so that we can pass in a more recent blockhash once jito is leader + let tx_0 = VersionedTransaction::from(Transaction::new_signed_with_payer( + instructions.as_ref(), + Some(&payer_pubkey), + &[&payer], + blockhash.clone(), + )); + let tx_1 = VersionedTransaction::from(Transaction::new_signed_with_payer( + &[build_memo( + format!("i b swimmin in da mempool 🏊🏊🏊🏊").as_bytes(), + &[], + )], + Some(&payer_pubkey), + &[&payer], + blockhash.clone(), + )); + + let rt = tokio::runtime::Runtime::new().unwrap(); + + rt.block_on(async { + let mut searcher_client = + get_searcher_client(args.block_engine_url.as_str(), &auth_keypair) + .await + .unwrap(); + println!("sending bundle now!"); + build_and_send_bundle( + args.rpc_url.clone(), + vec![tx_1, tx_0.clone()], + args.keypair_path.clone().to_string(), + 100_000, + "96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5".to_string(), + &mut searcher_client, + ) + .await; + }); +} diff --git a/orca_client/src/orca.rs b/orca_client/src/orca.rs new file mode 100644 index 0000000..86fcc03 --- /dev/null +++ b/orca_client/src/orca.rs @@ -0,0 +1,131 @@ +use ::whirlpool::{ + math::MIN_SQRT_PRICE_X64, + state::{TickArray, Whirlpool}, +}; +use anchor_client::Program; +use anchor_lang::{ + prelude::*, + solana_program::{instruction::Instruction, pubkey}, + AccountDeserialize, +}; +use anyhow::Result; +use solana_client_helpers::{ + spl_associated_token_account::get_associated_token_address, Client as SolanaClient, +}; +use spl_token::ID as TOKEN_PROGRAM_ID; + +use crate::orca_utils::*; + +const ORCA_WHIRLPOOL_PROGRAM_ID: Pubkey = pubkey!("whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"); + +// SAMO/USDC(64) whirlpool need to be cloned on local-validator +const SAMO_USDC_WHIRLPOOL_ADDRESS: Pubkey = pubkey!("9vqYJjDUFecLL2xPUC4Rc7hyCtZ6iJ4mDiVZX7aFXoAe"); + +pub fn swap( + amount_in: u64, + solana_client: &SolanaClient, + program: Program, + payer_pubkey: Pubkey, +) -> Result> { + // swap input + let a_to_b = true; + + // get whirlpool + let mut whirlpool_data: &[u8] = &solana_client + .get_account_data(&SAMO_USDC_WHIRLPOOL_ADDRESS) + .unwrap(); + let whirlpool = Whirlpool::try_deserialize(&mut whirlpool_data).unwrap(); + + println!( + "whirlpool token_mint_a {}", + whirlpool.token_mint_a.to_string() + ); + println!( + "whirlpool token_mint_b {}", + whirlpool.token_mint_b.to_string() + ); + println!( + "whirlpool token_vault_a {}", + whirlpool.token_vault_a.to_string() + ); + println!( + "whirlpool token_vault_b {}", + whirlpool.token_vault_b.to_string() + ); + println!("whirlpool tick_spacing {}", whirlpool.tick_spacing); + println!( + "whirlpool tick_current_index {}", + whirlpool.tick_current_index + ); + println!("whirlpool sqrt_price {}", whirlpool.sqrt_price); + + // get tickarray for swap + let tick_arrays = poolutil_get_tick_array_pubkeys_for_swap( + whirlpool.tick_current_index, + whirlpool.tick_spacing, + a_to_b, + &ORCA_WHIRLPOOL_PROGRAM_ID, + &SAMO_USDC_WHIRLPOOL_ADDRESS, + ); + let mut ta0_data: &[u8] = &solana_client.get_account_data(&tick_arrays[0]).unwrap(); + let mut ta1_data: &[u8] = &solana_client.get_account_data(&tick_arrays[1]).unwrap(); + let mut ta2_data: &[u8] = &solana_client.get_account_data(&tick_arrays[2]).unwrap(); + let ta0 = TickArray::try_deserialize(&mut ta0_data).unwrap(); + let ta1 = TickArray::try_deserialize(&mut ta1_data).unwrap(); + let ta2 = TickArray::try_deserialize(&mut ta2_data).unwrap(); + + println!("tick_arrays[0] {}", tick_arrays[0].to_string()); + println!("tick_arrays[1] {}", tick_arrays[1].to_string()); + println!("tick_arrays[2] {}", tick_arrays[2].to_string()); + + // get quote + let [quote_amount_in, quote_amount_out] = get_swap_quote( + &whirlpool, + [ta0, ta1, ta2], + amount_in, + true, // amount is input amount + a_to_b, + ); + let amount_out = calc_slippage(quote_amount_out, 1, 100); // 1% + println!("quote amount_in {}", quote_amount_in); + println!("quote amount_out {}", quote_amount_out); + println!("amount_out (slippage included) {}", amount_out); + + // get oracle + let oracle = pdautil_get_oracle(&ORCA_WHIRLPOOL_PROGRAM_ID, &SAMO_USDC_WHIRLPOOL_ADDRESS); + + // get ATA + // - Assume that the ATA has already been created + // - If one token of pair is SOL, the WSOL account must be processed (avoid SOL in this example) + let ata_a = get_associated_token_address(&payer_pubkey, &whirlpool.token_mint_a); + let ata_b = get_associated_token_address(&payer_pubkey, &whirlpool.token_mint_b); + println!("ata_a {}", ata_a.to_string()); + println!("ata_b {}", ata_b.to_string()); + + // execute proxy_swap + let instructions = program + .request() + .accounts(whirlpool::accounts::Swap { + whirlpool: SAMO_USDC_WHIRLPOOL_ADDRESS, + token_program: TOKEN_PROGRAM_ID, + token_authority: payer_pubkey, + token_owner_account_a: ata_a, + token_owner_account_b: ata_b, + token_vault_a: whirlpool.token_vault_a, + token_vault_b: whirlpool.token_vault_b, + tick_array_0: tick_arrays[0], + tick_array_1: tick_arrays[1], + tick_array_2: tick_arrays[2], + oracle, + }) + .args(whirlpool::instruction::Swap { + a_to_b: a_to_b, + amount_specified_is_input: true, + other_amount_threshold: amount_out, + sqrt_price_limit: MIN_SQRT_PRICE_X64, // a to b + amount: amount_in, + }) + .instructions()?; + + Ok(instructions) +} diff --git a/orca_client/src/orca_utils.rs b/orca_client/src/orca_utils.rs new file mode 100644 index 0000000..f170a13 --- /dev/null +++ b/orca_client/src/orca_utils.rs @@ -0,0 +1,120 @@ +/// Helper functions courtesy of @yugure-orca +use std::cell::RefCell; + +use anchor_client::solana_sdk::pubkey::Pubkey; +use whirlpool::{ + manager::swap_manager::swap, + math::{MAX_SQRT_PRICE_X64, MIN_SQRT_PRICE_X64}, + state::{TickArray, Whirlpool, MAX_TICK_INDEX, MIN_TICK_INDEX, TICK_ARRAY_SIZE}, + util::SwapTickSequence, +}; + +pub fn div_floor(a: i32, b: i32) -> i32 { + if a < 0 && a % b != 0 { + a / b - 1 + } else { + a / b + } +} + +pub fn tickutil_get_start_tick_index( + tick_current_index: i32, + tick_spacing: u16, + offset: i32, +) -> i32 { + let ticks_in_array = TICK_ARRAY_SIZE * tick_spacing as i32; + let real_index = div_floor(tick_current_index, ticks_in_array); + let start_tick_index = (real_index + offset) * ticks_in_array; + + assert!(MIN_TICK_INDEX <= start_tick_index); + assert!(start_tick_index + ticks_in_array <= MAX_TICK_INDEX); + start_tick_index +} + +pub fn pdautil_get_tick_array( + program_id: &Pubkey, + whirlpool_pubkey: &Pubkey, + start_tick_index: i32, +) -> Pubkey { + let start_tick_index_str = start_tick_index.to_string(); + let seeds = [ + b"tick_array", + whirlpool_pubkey.as_ref(), + start_tick_index_str.as_bytes(), + ]; + let (pubkey, _bump) = Pubkey::find_program_address(&seeds, program_id); + pubkey +} + +pub fn poolutil_get_tick_array_pubkeys_for_swap( + tick_current_index: i32, + tick_spacing: u16, + a_to_b: bool, + program_id: &Pubkey, + whirlpool_pubkey: &Pubkey, +) -> [Pubkey; 3] { + let mut offset = 0; + let mut pubkeys: [Pubkey; 3] = Default::default(); + let shifted = if a_to_b { 0i32 } else { tick_spacing as i32 }; + + for i in 0..pubkeys.len() { + let start_tick_index = + tickutil_get_start_tick_index(tick_current_index + shifted, tick_spacing, offset); + let tick_array_pubkey = + pdautil_get_tick_array(program_id, whirlpool_pubkey, start_tick_index); + pubkeys[i] = tick_array_pubkey; + offset = if a_to_b { offset - 1 } else { offset + 1 }; + } + + pubkeys +} + +pub fn pdautil_get_oracle(program_id: &Pubkey, whirlpool_pubkey: &Pubkey) -> Pubkey { + let seeds = [b"oracle", whirlpool_pubkey.as_ref()]; + let (pubkey, _bump) = Pubkey::find_program_address(&seeds, program_id); + pubkey +} + +pub fn get_swap_quote( + whirlpool: &Whirlpool, + tick_arrays: [TickArray; 3], + amount: u64, + amount_specified_is_input: bool, + a_to_b: bool, +) -> [u64; 2] { + let ta0_refcell = RefCell::new(tick_arrays[0]); + let ta1_refcell = RefCell::new(tick_arrays[1]); + let ta2_refcell = RefCell::new(tick_arrays[2]); + let mut swap_tick_sequence = SwapTickSequence::new( + ta0_refcell.borrow_mut(), + Some(ta1_refcell.borrow_mut()), + Some(ta2_refcell.borrow_mut()), + ); + + // dummy + let timestamp = whirlpool.reward_last_updated_timestamp; + let sqrt_price_limit = if a_to_b { + MIN_SQRT_PRICE_X64 + } else { + MAX_SQRT_PRICE_X64 + }; + + let swap_update = swap( + whirlpool, + &mut swap_tick_sequence, + amount, + sqrt_price_limit, + amount_specified_is_input, + a_to_b, + timestamp, + ) + .unwrap(); + + [swap_update.amount_a, swap_update.amount_b] +} + +pub fn calc_slippage(amount: u64, slippage_num: u64, slippage_denom: u64) -> u64 { + let num = (slippage_denom - slippage_num) as u128; + let denom = slippage_denom as u128; + u64::try_from((amount as u128) * num / denom).unwrap() +} diff --git a/orca_client/src/send_bundle.rs b/orca_client/src/send_bundle.rs new file mode 100644 index 0000000..35da7ab --- /dev/null +++ b/orca_client/src/send_bundle.rs @@ -0,0 +1,127 @@ +use std::{str::FromStr, time::Duration}; + +use futures::StreamExt; +use jito_protos::{ + bundle::Bundle, + convert::proto_packet_from_versioned_tx, + searcher::{ + searcher_service_client::SearcherServiceClient, NextScheduledLeaderRequest, + SendBundleRequest, SubscribeBundleResultsRequest, + }, +}; +use jito_searcher_client::token_authenticator::ClientInterceptor; + +use solana_client::nonblocking::rpc_client::RpcClient; +use solana_sdk::{ + commitment_config::CommitmentConfig, + pubkey::Pubkey, + signature::{read_keypair_file, Signature, Signer}, + system_instruction::transfer, + transaction::{Transaction, VersionedTransaction}, +}; +use tokio::time::{sleep, timeout}; +use tonic::{codegen::InterceptedService, transport::Channel}; + +type Client = SearcherServiceClient>; + +/// Builds and send bundle, attaching the tip and waiting until the next leader +pub async fn build_and_send_bundle( + rpc_url: String, + bundle: Vec, + keypair_path: String, + tip_lamports: u64, + tip_account: String, + client: &mut Client, +) { + let payer_keypair = read_keypair_file(keypair_path).expect("reads keypair at path"); + let tip_account = Pubkey::from_str(&tip_account).expect("valid pubkey for tip account"); + let rpc_client = RpcClient::new(rpc_url); + let balance = rpc_client + .get_balance(&payer_keypair.pubkey()) + .await + .expect("reads balance"); + + println!( + "payer public key: {:?} lamports: {:?}", + payer_keypair.pubkey(), + balance + ); + + let mut bundle_results_subscription = client + .subscribe_bundle_results(SubscribeBundleResultsRequest {}) + .await + .expect("subscribe to bundle results") + .into_inner(); + + // wait for jito-solana leader slot + let mut is_leader_slot = false; + while !is_leader_slot { + let next_leader = client + .get_next_scheduled_leader(NextScheduledLeaderRequest {}) + .await + .expect("gets next scheduled leader") + .into_inner(); + let num_slots = next_leader.next_leader_slot - next_leader.current_slot; + is_leader_slot = num_slots <= 2; + println!("next jito leader slot in {} slots", num_slots); + sleep(Duration::from_millis(500)).await; + } + + // build + sign the transactions + let blockhash = rpc_client + .get_latest_blockhash() + .await + .expect("get blockhash"); + + let tip_tx = VersionedTransaction::from(Transaction::new_signed_with_payer( + &[transfer( + &payer_keypair.pubkey(), + &tip_account, + tip_lamports, + )], + Some(&payer_keypair.pubkey()), + &[&payer_keypair], + blockhash.clone(), + )); + let txs = [bundle, vec![tip_tx]].concat(); + + let signatures: Vec = txs.iter().map(|tx| tx.signatures[0]).collect(); + + // convert them to packets + send over + let packets: Vec<_> = txs.iter().map(proto_packet_from_versioned_tx).collect(); + let result = client + .send_bundle(SendBundleRequest { + bundle: Some(Bundle { + header: None, + packets, + }), + }) + .await + .expect("sends bundle"); + + // grab uuid from block engine + wait for results + let uuid = result.into_inner().uuid; + println!("bundle sent uuid: {:?}", uuid); + println!("waiting for 5 seconds to hear results..."); + while let Ok(Some(Ok(results))) = + timeout(Duration::from_secs(5), bundle_results_subscription.next()).await + { + println!("bundle results: {:?}", results); + } + + let futs: Vec<_> = signatures + .iter() + .map(|sig| { + rpc_client.get_signature_status_with_commitment(sig, CommitmentConfig::processed()) + }) + .collect(); + let results = futures::future::join_all(futs).await; + if !results.iter().all(|r| matches!(r, Ok(Some(Ok(()))))) { + println!("transactions in bundle did not land :("); + } else { + println!("bundle landed successfully!!"); + for sig in signatures { + println!("https://solscan.io/tx/{}", sig); + } + } +} diff --git a/searcher_client/src/lib.rs b/searcher_client/src/lib.rs index d8c1ed0..9a26396 100644 --- a/searcher_client/src/lib.rs +++ b/searcher_client/src/lib.rs @@ -21,7 +21,7 @@ pub mod token_authenticator; pub enum BlockEngineConnectionError { #[error("transport error {0}")] TransportError(#[from] transport::Error), - #[error("client error {0}")] + #[error("whirlpools error {0}")] ClientError(#[from] Status), } From d87fc5d4a4e852ee1fcf4096e152eab0b9683cd1 Mon Sep 17 00:00:00 2001 From: Edgar Date: Wed, 15 Feb 2023 14:40:26 -0500 Subject: [PATCH 2/6] move send_bundle to generic function in searcher_client --- Cargo.lock | 63 ++++++++-------- cli/src/main.rs | 108 +++++++--------------------- jito_protos/protos | 2 +- orca_client/Cargo.toml | 4 +- orca_client/src/main.rs | 12 ++-- orca_client/src/orca.rs | 8 +-- orca_client/src/orca_utils.rs | 2 +- orca_client/src/send_bundle.rs | 127 --------------------------------- searcher_client/Cargo.toml | 3 + searcher_client/src/lib.rs | 123 ++++++++++++++++++++++++++++++- 10 files changed, 193 insertions(+), 259 deletions(-) delete mode 100644 orca_client/src/send_bundle.rs diff --git a/Cargo.lock b/Cargo.lock index e4e51cb..7e828d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1927,6 +1927,36 @@ dependencies = [ "tonic", ] +[[package]] +name = "jito-orca-client" +version = "0.1.0" +dependencies = [ + "anchor-client", + "anchor-lang", + "anchor-spl", + "anyhow", + "clap 4.1.4", + "env_logger 0.10.0", + "futures", + "jito-protos", + "jito-searcher-client", + "log", + "rust_decimal", + "rust_decimal_macros", + "solana-client", + "solana-client-helpers", + "solana-metrics", + "solana-program", + "solana-sdk", + "solana-transaction-status", + "spl-memo", + "spl-token", + "thiserror", + "tokio", + "tonic", + "whirlpool", +] + [[package]] name = "jito-protos" version = "0.1.0" @@ -1973,10 +2003,13 @@ name = "jito-searcher-client" version = "0.1.0" dependencies = [ "chrono", + "futures", + "futures-util", "jito-protos", "log", "prost-types", "serde", + "solana-client", "solana-metrics", "solana-sdk", "thiserror", @@ -2493,36 +2526,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "orca_client" -version = "0.1.0" -dependencies = [ - "anchor-client", - "anchor-lang", - "anchor-spl", - "anyhow", - "clap 4.1.4", - "env_logger 0.10.0", - "futures", - "jito-protos", - "jito-searcher-client", - "log", - "rust_decimal", - "rust_decimal_macros", - "solana-client", - "solana-client-helpers", - "solana-metrics", - "solana-program", - "solana-sdk", - "solana-transaction-status", - "spl-memo", - "spl-token", - "thiserror", - "tokio", - "tonic", - "whirlpool", -] - [[package]] name = "os_str_bytes" version = "6.4.1" diff --git a/cli/src/main.rs b/cli/src/main.rs index 535e406..5163de7 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -4,26 +4,25 @@ use clap::{Parser, Subcommand}; use env_logger::TimestampPrecision; use futures_util::StreamExt; use jito_protos::{ - bundle::Bundle, - convert::{proto_packet_from_versioned_tx, versioned_tx_from_packet}, + convert::{versioned_tx_from_packet}, searcher::{ searcher_service_client::SearcherServiceClient, ConnectedLeadersRequest, GetTipAccountsRequest, NextScheduledLeaderRequest, PendingTxSubscriptionRequest, - SendBundleRequest, SubscribeBundleResultsRequest, }, }; -use jito_searcher_client::{get_searcher_client, token_authenticator::ClientInterceptor}; -use log::{info, warn}; +use jito_searcher_client::{ + build_and_send_bundle, get_searcher_client, token_authenticator::ClientInterceptor, +}; +use log::{info}; use solana_client::nonblocking::rpc_client::RpcClient; use solana_sdk::{ - commitment_config::CommitmentConfig, pubkey::Pubkey, - signature::{read_keypair_file, Signature, Signer}, + signature::{read_keypair_file, Signer}, system_instruction::transfer, transaction::{Transaction, VersionedTransaction}, }; use spl_memo::build_memo; -use tokio::time::{sleep, timeout}; +use tokio::time::{timeout}; use tonic::{codegen::InterceptedService, transport::Channel}; #[derive(Parser, Debug)] @@ -114,7 +113,7 @@ async fn main() { let mut client = get_searcher_client(&args.block_engine_url, &keypair) .await - .expect("connects to searcher whirlpools"); + .expect("connects to searcher client"); match args.command { Commands::NextScheduledLeader => { @@ -226,51 +225,23 @@ async fn main() { lamports, tip_account, } => { - let payer_keypair = read_keypair_file(payer).expect("reads keypair at path"); - let tip_account = Pubkey::from_str(&tip_account).expect("valid pubkey for tip account"); - let rpc_client = RpcClient::new(rpc_url); - let balance = rpc_client - .get_balance(&payer_keypair.pubkey()) - .await - .expect("reads balance"); - - info!( - "payer public key: {:?} lamports: {:?}", - payer_keypair.pubkey(), - balance - ); - - let mut bundle_results_subscription = client - .subscribe_bundle_results(SubscribeBundleResultsRequest {}) - .await - .expect("subscribe to bundle results") - .into_inner(); - - // wait for jito-solana leader slot - let mut is_leader_slot = false; - while !is_leader_slot { - let next_leader = client - .get_next_scheduled_leader(NextScheduledLeaderRequest {}) - .await - .expect("gets next scheduled leader") - .into_inner(); - let num_slots = next_leader.next_leader_slot - next_leader.current_slot; - is_leader_slot = num_slots <= 2; - info!("next jito leader slot in {} slots", num_slots); - sleep(Duration::from_millis(500)).await; - } - // build + sign the transactions + let rpc_client = RpcClient::new(rpc_url.clone()); let blockhash = rpc_client .get_latest_blockhash() .await .expect("get blockhash"); + let payer_keypair = read_keypair_file(payer.clone()).expect("reads keypair at path"); let txs: Vec<_> = (0..num_txs) .map(|i| { VersionedTransaction::from(Transaction::new_signed_with_payer( &[ build_memo(format!("jito bundle {}: {}", i, message).as_bytes(), &[]), - transfer(&payer_keypair.pubkey(), &tip_account, lamports), + transfer( + &payer_keypair.pubkey(), + &Pubkey::from_str(tip_account.as_str()).unwrap(), + lamports, + ), ], Some(&payer_keypair.pubkey()), &[&payer_keypair], @@ -279,46 +250,15 @@ async fn main() { }) .collect(); - let signatures: Vec = txs.iter().map(|tx| tx.signatures[0]).collect(); - - // convert them to packets + send over - let packets: Vec<_> = txs.iter().map(proto_packet_from_versioned_tx).collect(); - let result = client - .send_bundle(SendBundleRequest { - bundle: Some(Bundle { - header: None, - packets, - }), - }) - .await - .expect("sends bundle"); - - // grab uuid from block engine + wait for results - let uuid = result.into_inner().uuid; - info!("bundle sent uuid: {:?}", uuid); - info!("waiting for 5 seconds to hear results..."); - while let Ok(Some(Ok(results))) = - timeout(Duration::from_secs(5), bundle_results_subscription.next()).await - { - info!("bundle results: {:?}", results); - } - - let futs: Vec<_> = signatures - .iter() - .map(|sig| { - rpc_client - .get_signature_status_with_commitment(sig, CommitmentConfig::processed()) - }) - .collect(); - let results = futures::future::join_all(futs).await; - if !results.iter().all(|r| matches!(r, Ok(Some(Ok(()))))) { - warn!("transactions in bundle did not land :("); - } else { - info!("bundle landed successfully!!"); - for sig in signatures { - info!("https://solscan.io/tx/{}", sig); - } - } + build_and_send_bundle( + rpc_url.clone(), + txs, + payer.clone(), + lamports, + tip_account, + &mut client, + ) + .await; } } } diff --git a/jito_protos/protos b/jito_protos/protos index 30b8de6..0b1c749 160000 --- a/jito_protos/protos +++ b/jito_protos/protos @@ -1 +1 @@ -Subproject commit 30b8de6280157d85ab6ceeb7b3d00b67d754b727 +Subproject commit 0b1c7494ee9798f7c1df154eca8ae43a38e8c6b6 diff --git a/orca_client/Cargo.toml b/orca_client/Cargo.toml index 37a36ad..d1df79a 100644 --- a/orca_client/Cargo.toml +++ b/orca_client/Cargo.toml @@ -1,10 +1,8 @@ [package] -name = "orca_client" +name = "jito-orca-client" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] futures = "0.3.1" solana-client = "=1.14.13" diff --git a/orca_client/src/main.rs b/orca_client/src/main.rs index 943cf75..8d02ae6 100644 --- a/orca_client/src/main.rs +++ b/orca_client/src/main.rs @@ -1,15 +1,13 @@ mod orca; mod orca_utils; -mod send_bundle; use std::{env, rc::Rc, sync::Arc}; use anchor_client::{Client as AnchorClient, Cluster}; use clap::Parser; use env_logger::TimestampPrecision; -use jito_searcher_client::get_searcher_client; +use jito_searcher_client::{build_and_send_bundle, get_searcher_client}; use orca::swap; -use send_bundle::build_and_send_bundle; use solana_client::rpc_client::RpcClient; use solana_client_helpers::Client as SolanaClient; use solana_sdk::{ @@ -33,9 +31,12 @@ struct Args { #[clap(long, env)] block_engine_url: String, + + #[clap(long, env)] + tip_account: String, } -/// Example of how to build orca swap from SAMO-> USDC and submit as a bundle +/// Example of how to build orca swap from SAMO-> USDC with a memo and submit as a bundle fn main() { let args: Args = Args::parse(); if env::var("RUST_LOG").is_err() { @@ -104,13 +105,12 @@ fn main() { get_searcher_client(args.block_engine_url.as_str(), &auth_keypair) .await .unwrap(); - println!("sending bundle now!"); build_and_send_bundle( args.rpc_url.clone(), vec![tx_1, tx_0.clone()], args.keypair_path.clone().to_string(), 100_000, - "96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5".to_string(), + args.tip_account, &mut searcher_client, ) .await; diff --git a/orca_client/src/orca.rs b/orca_client/src/orca.rs index 86fcc03..458b928 100644 --- a/orca_client/src/orca.rs +++ b/orca_client/src/orca.rs @@ -1,7 +1,3 @@ -use ::whirlpool::{ - math::MIN_SQRT_PRICE_X64, - state::{TickArray, Whirlpool}, -}; use anchor_client::Program; use anchor_lang::{ prelude::*, @@ -13,6 +9,10 @@ use solana_client_helpers::{ spl_associated_token_account::get_associated_token_address, Client as SolanaClient, }; use spl_token::ID as TOKEN_PROGRAM_ID; +use whirlpool::{ + math::MIN_SQRT_PRICE_X64, + state::{TickArray, Whirlpool}, +}; use crate::orca_utils::*; diff --git a/orca_client/src/orca_utils.rs b/orca_client/src/orca_utils.rs index f170a13..813eca1 100644 --- a/orca_client/src/orca_utils.rs +++ b/orca_client/src/orca_utils.rs @@ -1,4 +1,4 @@ -/// Helper functions courtesy of @yugure-orca +/// Helper functions courtesy of @yugure-orca ( https://github.com/yugure-orca/whirlpools ) use std::cell::RefCell; use anchor_client::solana_sdk::pubkey::Pubkey; diff --git a/orca_client/src/send_bundle.rs b/orca_client/src/send_bundle.rs deleted file mode 100644 index 35da7ab..0000000 --- a/orca_client/src/send_bundle.rs +++ /dev/null @@ -1,127 +0,0 @@ -use std::{str::FromStr, time::Duration}; - -use futures::StreamExt; -use jito_protos::{ - bundle::Bundle, - convert::proto_packet_from_versioned_tx, - searcher::{ - searcher_service_client::SearcherServiceClient, NextScheduledLeaderRequest, - SendBundleRequest, SubscribeBundleResultsRequest, - }, -}; -use jito_searcher_client::token_authenticator::ClientInterceptor; - -use solana_client::nonblocking::rpc_client::RpcClient; -use solana_sdk::{ - commitment_config::CommitmentConfig, - pubkey::Pubkey, - signature::{read_keypair_file, Signature, Signer}, - system_instruction::transfer, - transaction::{Transaction, VersionedTransaction}, -}; -use tokio::time::{sleep, timeout}; -use tonic::{codegen::InterceptedService, transport::Channel}; - -type Client = SearcherServiceClient>; - -/// Builds and send bundle, attaching the tip and waiting until the next leader -pub async fn build_and_send_bundle( - rpc_url: String, - bundle: Vec, - keypair_path: String, - tip_lamports: u64, - tip_account: String, - client: &mut Client, -) { - let payer_keypair = read_keypair_file(keypair_path).expect("reads keypair at path"); - let tip_account = Pubkey::from_str(&tip_account).expect("valid pubkey for tip account"); - let rpc_client = RpcClient::new(rpc_url); - let balance = rpc_client - .get_balance(&payer_keypair.pubkey()) - .await - .expect("reads balance"); - - println!( - "payer public key: {:?} lamports: {:?}", - payer_keypair.pubkey(), - balance - ); - - let mut bundle_results_subscription = client - .subscribe_bundle_results(SubscribeBundleResultsRequest {}) - .await - .expect("subscribe to bundle results") - .into_inner(); - - // wait for jito-solana leader slot - let mut is_leader_slot = false; - while !is_leader_slot { - let next_leader = client - .get_next_scheduled_leader(NextScheduledLeaderRequest {}) - .await - .expect("gets next scheduled leader") - .into_inner(); - let num_slots = next_leader.next_leader_slot - next_leader.current_slot; - is_leader_slot = num_slots <= 2; - println!("next jito leader slot in {} slots", num_slots); - sleep(Duration::from_millis(500)).await; - } - - // build + sign the transactions - let blockhash = rpc_client - .get_latest_blockhash() - .await - .expect("get blockhash"); - - let tip_tx = VersionedTransaction::from(Transaction::new_signed_with_payer( - &[transfer( - &payer_keypair.pubkey(), - &tip_account, - tip_lamports, - )], - Some(&payer_keypair.pubkey()), - &[&payer_keypair], - blockhash.clone(), - )); - let txs = [bundle, vec![tip_tx]].concat(); - - let signatures: Vec = txs.iter().map(|tx| tx.signatures[0]).collect(); - - // convert them to packets + send over - let packets: Vec<_> = txs.iter().map(proto_packet_from_versioned_tx).collect(); - let result = client - .send_bundle(SendBundleRequest { - bundle: Some(Bundle { - header: None, - packets, - }), - }) - .await - .expect("sends bundle"); - - // grab uuid from block engine + wait for results - let uuid = result.into_inner().uuid; - println!("bundle sent uuid: {:?}", uuid); - println!("waiting for 5 seconds to hear results..."); - while let Ok(Some(Ok(results))) = - timeout(Duration::from_secs(5), bundle_results_subscription.next()).await - { - println!("bundle results: {:?}", results); - } - - let futs: Vec<_> = signatures - .iter() - .map(|sig| { - rpc_client.get_signature_status_with_commitment(sig, CommitmentConfig::processed()) - }) - .collect(); - let results = futures::future::join_all(futs).await; - if !results.iter().all(|r| matches!(r, Ok(Some(Ok(()))))) { - println!("transactions in bundle did not land :("); - } else { - println!("bundle landed successfully!!"); - for sig in signatures { - println!("https://solscan.io/tx/{}", sig); - } - } -} diff --git a/searcher_client/Cargo.toml b/searcher_client/Cargo.toml index 6f5d333..729643b 100644 --- a/searcher_client/Cargo.toml +++ b/searcher_client/Cargo.toml @@ -15,3 +15,6 @@ solana-sdk = "=1.14.13" thiserror = "1.0.34" tokio = "~1.14.1" tonic = "0.5.2" +solana-client="1.14.13" +futures-util = "0.3.21" +futures = "0.3.21" diff --git a/searcher_client/src/lib.rs b/searcher_client/src/lib.rs index 9a26396..468c103 100644 --- a/searcher_client/src/lib.rs +++ b/searcher_client/src/lib.rs @@ -1,11 +1,26 @@ -use std::sync::Arc; +use std::{str::FromStr, sync::Arc, time::Duration}; +use futures_util::StreamExt; use jito_protos::{ auth::{auth_service_client::AuthServiceClient, Role}, - searcher::searcher_service_client::SearcherServiceClient, + bundle::Bundle, + convert::proto_packet_from_versioned_tx, + searcher::{ + searcher_service_client::SearcherServiceClient, NextScheduledLeaderRequest, + SendBundleRequest, SubscribeBundleResultsRequest, + }, +}; +use log::info; +use solana_client::nonblocking::rpc_client::RpcClient; +use solana_sdk::{ + commitment_config::CommitmentConfig, + pubkey::Pubkey, + signature::{read_keypair_file, Keypair, Signature, Signer}, + system_instruction::transfer, + transaction::{Transaction, VersionedTransaction}, }; -use solana_sdk::signature::Keypair; use thiserror::Error; +use tokio::time::{sleep, timeout}; use tonic::{ codegen::InterceptedService, transport, @@ -54,3 +69,105 @@ pub async fn create_grpc_channel(url: &str) -> BlockEngineConnectionResult, + keypair_path: String, + tip_lamports: u64, + tip_account: String, + client: &mut SearcherServiceClient>, +) { + let payer_keypair = read_keypair_file(keypair_path).expect("reads keypair at path"); + let tip_account = Pubkey::from_str(&tip_account).expect("valid pubkey for tip account"); + let rpc_client = RpcClient::new(rpc_url); + let balance = rpc_client + .get_balance(&payer_keypair.pubkey()) + .await + .expect("reads balance"); + + info!( + "payer public key: {:?} lamports: {:?}", + payer_keypair.pubkey(), + balance + ); + + let mut bundle_results_subscription = client + .subscribe_bundle_results(SubscribeBundleResultsRequest {}) + .await + .expect("subscribe to bundle results") + .into_inner(); + + // wait for jito-solana leader slot + let mut is_leader_slot = false; + while !is_leader_slot { + let next_leader = client + .get_next_scheduled_leader(NextScheduledLeaderRequest {}) + .await + .expect("gets next scheduled leader") + .into_inner(); + let num_slots = next_leader.next_leader_slot - next_leader.current_slot; + is_leader_slot = num_slots <= 2; + info!("next jito leader slot in {} slots", num_slots); + sleep(Duration::from_millis(500)).await; + } + + // build + sign the transactions + let blockhash = rpc_client + .get_latest_blockhash() + .await + .expect("get blockhash"); + + let tip_tx = VersionedTransaction::from(Transaction::new_signed_with_payer( + &[transfer( + &payer_keypair.pubkey(), + &tip_account, + tip_lamports, + )], + Some(&payer_keypair.pubkey()), + &[&payer_keypair], + blockhash.clone(), + )); + let txs = [bundle, vec![tip_tx]].concat(); + + let signatures: Vec = txs.iter().map(|tx| tx.signatures[0]).collect(); + + // convert them to packets + send over + let packets: Vec<_> = txs.iter().map(proto_packet_from_versioned_tx).collect(); + let result = client + .send_bundle(SendBundleRequest { + bundle: Some(Bundle { + header: None, + packets, + }), + }) + .await + .expect("sends bundle"); + + // grab uuid from block engine + wait for results + let uuid = result.into_inner().uuid; + info!("bundle sent uuid: {:?}", uuid); + info!("waiting for 5 seconds to hear results..."); + while let Ok(Some(Ok(results))) = + timeout(Duration::from_secs(5), bundle_results_subscription.next()).await + { + info!("bundle results: {:?}", results); + } + + let futs: Vec<_> = signatures + .iter() + .map(|sig| { + rpc_client.get_signature_status_with_commitment(sig, CommitmentConfig::processed()) + }) + .collect(); + let results = futures::future::join_all(futs).await; + if !results.iter().all(|r| matches!(r, Ok(Some(Ok(()))))) { + info!("transactions in bundle did not land :("); + } else { + info!("bundle landed successfully!!"); + for sig in signatures { + info!("https://solscan.io/tx/{}", sig); + } + } +} From 27f3e471a7df8a0a23b0cb2e075efe56dbf29f9f Mon Sep 17 00:00:00 2001 From: Edgar Date: Wed, 15 Feb 2023 14:47:15 -0500 Subject: [PATCH 3/6] relpace with rpc client --- cli/src/main.rs | 8 ++++---- orca_client/src/main.rs | 4 ++-- searcher_client/src/lib.rs | 3 +-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 5163de7..470072b 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -4,7 +4,7 @@ use clap::{Parser, Subcommand}; use env_logger::TimestampPrecision; use futures_util::StreamExt; use jito_protos::{ - convert::{versioned_tx_from_packet}, + convert::versioned_tx_from_packet, searcher::{ searcher_service_client::SearcherServiceClient, ConnectedLeadersRequest, GetTipAccountsRequest, NextScheduledLeaderRequest, PendingTxSubscriptionRequest, @@ -13,7 +13,7 @@ use jito_protos::{ use jito_searcher_client::{ build_and_send_bundle, get_searcher_client, token_authenticator::ClientInterceptor, }; -use log::{info}; +use log::info; use solana_client::nonblocking::rpc_client::RpcClient; use solana_sdk::{ pubkey::Pubkey, @@ -22,7 +22,7 @@ use solana_sdk::{ transaction::{Transaction, VersionedTransaction}, }; use spl_memo::build_memo; -use tokio::time::{timeout}; +use tokio::time::timeout; use tonic::{codegen::InterceptedService, transport::Channel}; #[derive(Parser, Debug)] @@ -251,7 +251,7 @@ async fn main() { .collect(); build_and_send_bundle( - rpc_url.clone(), + &rpc_client, txs, payer.clone(), lamports, diff --git a/orca_client/src/main.rs b/orca_client/src/main.rs index 8d02ae6..9dd6f5b 100644 --- a/orca_client/src/main.rs +++ b/orca_client/src/main.rs @@ -8,7 +8,7 @@ use clap::Parser; use env_logger::TimestampPrecision; use jito_searcher_client::{build_and_send_bundle, get_searcher_client}; use orca::swap; -use solana_client::rpc_client::RpcClient; +use solana_client::{nonblocking::rpc_client::RpcClient as AsyncRpcClient, rpc_client::RpcClient}; use solana_client_helpers::Client as SolanaClient; use solana_sdk::{ commitment_config::CommitmentConfig, @@ -106,7 +106,7 @@ fn main() { .await .unwrap(); build_and_send_bundle( - args.rpc_url.clone(), + &AsyncRpcClient::new(args.rpc_url.clone()), vec![tx_1, tx_0.clone()], args.keypair_path.clone().to_string(), 100_000, diff --git a/searcher_client/src/lib.rs b/searcher_client/src/lib.rs index 468c103..1bc5321 100644 --- a/searcher_client/src/lib.rs +++ b/searcher_client/src/lib.rs @@ -72,7 +72,7 @@ pub async fn create_grpc_channel(url: &str) -> BlockEngineConnectionResult, keypair_path: String, tip_lamports: u64, @@ -81,7 +81,6 @@ pub async fn build_and_send_bundle( ) { let payer_keypair = read_keypair_file(keypair_path).expect("reads keypair at path"); let tip_account = Pubkey::from_str(&tip_account).expect("valid pubkey for tip account"); - let rpc_client = RpcClient::new(rpc_url); let balance = rpc_client .get_balance(&payer_keypair.pubkey()) .await From e7b9d10e4be82793251ba9760b25a469c53407d4 Mon Sep 17 00:00:00 2001 From: Edgar Date: Wed, 15 Feb 2023 22:45:39 -0500 Subject: [PATCH 4/6] wip --- backrun/src/main.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/backrun/src/main.rs b/backrun/src/main.rs index 54bcd5c..d156b4f 100644 --- a/backrun/src/main.rs +++ b/backrun/src/main.rs @@ -11,6 +11,7 @@ use std::{ use clap::Parser; use env_logger::TimestampPrecision; +use futures_util::StreamExt; use histogram::Histogram; use jito_protos::{ bundle::Bundle, @@ -18,6 +19,7 @@ use jito_protos::{ searcher::{ searcher_service_client::SearcherServiceClient, ConnectedLeadersRequest, NextScheduledLeaderRequest, PendingTxNotification, SendBundleRequest, SendBundleResponse, + SubscribeBundleResultsRequest, }, }; use jito_searcher_client::{ @@ -46,7 +48,7 @@ use thiserror::Error; use tokio::{ runtime::Builder, sync::mpsc::{channel, Receiver}, - time::interval, + time::{interval, timeout}, }; use tonic::{codegen::InterceptedService, transport::Channel, Response, Status}; @@ -144,7 +146,7 @@ fn build_bundles( .as_bytes(), &[], ), - transfer(&keypair.pubkey(), &tip_account, 1), + transfer(&keypair.pubkey(), &tip_account, 10_000), ], Some(&keypair.pubkey()), &[keypair], @@ -513,6 +515,11 @@ async fn run_searcher_loop( let mut highest_slot = 0; let mut is_leader_slot = false; + let mut bundle_results_subscription = searcher_client + .subscribe_bundle_results(SubscribeBundleResultsRequest {}) + .await + .expect("subscribe to bundle results") + .into_inner(); let mut tick = interval(Duration::from_secs(5)); loop { @@ -532,6 +539,13 @@ async fn run_searcher_loop( let send_elapsed = now.elapsed().as_micros() as u64; let send_rt_pp_us = send_elapsed / bundles.len() as u64; + while let Ok(Some(Ok(results))) = + timeout(Duration::from_secs(5), bundle_results_subscription.next()).await + { + info!("bundle results: {:?}", results); + } + + match block_stats.entry(highest_slot) { Entry::Occupied(mut entry) => { let mut stats = entry.get_mut(); From 3b9039db0a2f0ba36f6b071e4fcaaea0b813c872 Mon Sep 17 00:00:00 2001 From: Edgar Date: Wed, 15 Feb 2023 23:10:33 -0500 Subject: [PATCH 5/6] merge masterl --- Cargo.lock | 2 +- orca_client/src/main.rs | 15 +++++++++------ searcher_client/Cargo.toml | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2075016..3b6a027 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2068,7 +2068,7 @@ dependencies = [ "solana-transaction-status", "thiserror", "tokio", - "tonic 0.5.2", + "tonic 0.8.3", ] [[package]] diff --git a/orca_client/src/main.rs b/orca_client/src/main.rs index 75a5e6a..61ecab5 100644 --- a/orca_client/src/main.rs +++ b/orca_client/src/main.rs @@ -102,21 +102,24 @@ fn main() { let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { - let mut bundle_results_subscription = client - .subscribe_bundle_results(SubscribeBundleResultsRequest {}) - .await - .expect("subscribe to bundle results") - .into_inner(); let mut searcher_client = get_searcher_client(args.block_engine_url.as_str(), &auth_keypair) .await .unwrap(); + let mut bundle_results_subscription = searcher_client + .subscribe_bundle_results(SubscribeBundleResultsRequest {}) + .await + .expect("subscribe to bundle results") + .into_inner(); + send_bundle_with_confirmation( &[tx_0, tx_1], &AsyncRpcClient::new(args.rpc_url.clone()), &mut searcher_client, &mut bundle_results_subscription, - ); + ) + .await + .unwrap(); }); } diff --git a/searcher_client/Cargo.toml b/searcher_client/Cargo.toml index 82f25b1..814029a 100644 --- a/searcher_client/Cargo.toml +++ b/searcher_client/Cargo.toml @@ -19,4 +19,4 @@ solana-sdk = "=1.14.13" solana-transaction-status = "=1.14.13" thiserror = "1.0.34" tokio = "~1.14.1" -tonic = "0.5.2" +tonic = "0.8.3" From 252c14a835263186111337b24c0199483e70fb5e Mon Sep 17 00:00:00 2001 From: Edgar Date: Wed, 15 Feb 2023 23:39:49 -0500 Subject: [PATCH 6/6] println -> info --- orca_client/src/main.rs | 39 +++++++++++++++++++++++++++++++-------- orca_client/src/orca.rs | 31 ++++++++++++++++--------------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/orca_client/src/main.rs b/orca_client/src/main.rs index 61ecab5..42c862f 100644 --- a/orca_client/src/main.rs +++ b/orca_client/src/main.rs @@ -1,22 +1,26 @@ mod orca; mod orca_utils; -use std::{env, rc::Rc, sync::Arc}; +use std::{env, rc::Rc, str::FromStr, sync::Arc, time::Duration}; use anchor_client::{Client as AnchorClient, Cluster}; use clap::Parser; use env_logger::TimestampPrecision; -use jito_protos::searcher::SubscribeBundleResultsRequest; +use jito_protos::searcher::{NextScheduledLeaderRequest, SubscribeBundleResultsRequest}; use jito_searcher_client::{get_searcher_client, send_bundle_with_confirmation}; +use log::info; use orca::swap; use solana_client::{nonblocking::rpc_client::RpcClient as AsyncRpcClient, rpc_client::RpcClient}; use solana_client_helpers::Client as SolanaClient; +use solana_program::pubkey::Pubkey; use solana_sdk::{ commitment_config::CommitmentConfig, signature::{read_keypair_file, Signer}, + system_instruction::transfer, transaction::{Transaction, VersionedTransaction}, }; use spl_memo::build_memo; +use tokio::time::sleep; #[derive(Parser, Debug)] #[clap(author, version, about, long_about=None)] @@ -83,11 +87,17 @@ fn main() { let payer = read_keypair_file(args.keypair_path.clone()).expect("example requires a keypair file"); // TODO: make these mutable so that we can pass in a more recent blockhash once jito is leader + + let tip_account = Pubkey::from_str(&args.tip_account).expect("valid pubkey for tip account"); + let tx_0 = VersionedTransaction::from(Transaction::new_signed_with_payer( - &[build_memo( - format!("i b swimmin in da mempool 🏊🏊🏊🏊").as_bytes(), - &[], - )], + &[ + build_memo( + format!("i b swimmin in da mempool 🏊🏊🏊🏊").as_bytes(), + &[], + ), + transfer(&payer.pubkey(), &tip_account, 10000), + ], Some(&payer_pubkey), &[&payer], blockhash.clone(), @@ -106,13 +116,25 @@ fn main() { get_searcher_client(args.block_engine_url.as_str(), &auth_keypair) .await .unwrap(); - + // wait for jito-solana leader slot + let mut is_leader_slot = false; + while !is_leader_slot { + let next_leader = searcher_client + .get_next_scheduled_leader(NextScheduledLeaderRequest {}) + .await + .expect("gets next scheduled leader") + .into_inner(); + let num_slots = next_leader.next_leader_slot - next_leader.current_slot; + is_leader_slot = num_slots <= 2; + info!("next jito leader slot in {num_slots} slots"); + sleep(Duration::from_millis(500)).await; + } let mut bundle_results_subscription = searcher_client .subscribe_bundle_results(SubscribeBundleResultsRequest {}) .await .expect("subscribe to bundle results") .into_inner(); - + info!("bundle resu"); send_bundle_with_confirmation( &[tx_0, tx_1], &AsyncRpcClient::new(args.rpc_url.clone()), @@ -121,5 +143,6 @@ fn main() { ) .await .unwrap(); + info!("done"); }); } diff --git a/orca_client/src/orca.rs b/orca_client/src/orca.rs index 458b928..b4723d1 100644 --- a/orca_client/src/orca.rs +++ b/orca_client/src/orca.rs @@ -5,6 +5,7 @@ use anchor_lang::{ AccountDeserialize, }; use anyhow::Result; +use log::info; use solana_client_helpers::{ spl_associated_token_account::get_associated_token_address, Client as SolanaClient, }; @@ -36,28 +37,28 @@ pub fn swap( .unwrap(); let whirlpool = Whirlpool::try_deserialize(&mut whirlpool_data).unwrap(); - println!( + info!( "whirlpool token_mint_a {}", whirlpool.token_mint_a.to_string() ); - println!( + info!( "whirlpool token_mint_b {}", whirlpool.token_mint_b.to_string() ); - println!( + info!( "whirlpool token_vault_a {}", whirlpool.token_vault_a.to_string() ); - println!( + info!( "whirlpool token_vault_b {}", whirlpool.token_vault_b.to_string() ); - println!("whirlpool tick_spacing {}", whirlpool.tick_spacing); - println!( + info!("whirlpool tick_spacing {}", whirlpool.tick_spacing); + info!( "whirlpool tick_current_index {}", whirlpool.tick_current_index ); - println!("whirlpool sqrt_price {}", whirlpool.sqrt_price); + info!("whirlpool sqrt_price {}", whirlpool.sqrt_price); // get tickarray for swap let tick_arrays = poolutil_get_tick_array_pubkeys_for_swap( @@ -74,9 +75,9 @@ pub fn swap( let ta1 = TickArray::try_deserialize(&mut ta1_data).unwrap(); let ta2 = TickArray::try_deserialize(&mut ta2_data).unwrap(); - println!("tick_arrays[0] {}", tick_arrays[0].to_string()); - println!("tick_arrays[1] {}", tick_arrays[1].to_string()); - println!("tick_arrays[2] {}", tick_arrays[2].to_string()); + info!("tick_arrays[0] {}", tick_arrays[0].to_string()); + info!("tick_arrays[1] {}", tick_arrays[1].to_string()); + info!("tick_arrays[2] {}", tick_arrays[2].to_string()); // get quote let [quote_amount_in, quote_amount_out] = get_swap_quote( @@ -87,9 +88,9 @@ pub fn swap( a_to_b, ); let amount_out = calc_slippage(quote_amount_out, 1, 100); // 1% - println!("quote amount_in {}", quote_amount_in); - println!("quote amount_out {}", quote_amount_out); - println!("amount_out (slippage included) {}", amount_out); + info!("quote amount_in {}", quote_amount_in); + info!("quote amount_out {}", quote_amount_out); + info!("amount_out (slippage included) {}", amount_out); // get oracle let oracle = pdautil_get_oracle(&ORCA_WHIRLPOOL_PROGRAM_ID, &SAMO_USDC_WHIRLPOOL_ADDRESS); @@ -99,8 +100,8 @@ pub fn swap( // - If one token of pair is SOL, the WSOL account must be processed (avoid SOL in this example) let ata_a = get_associated_token_address(&payer_pubkey, &whirlpool.token_mint_a); let ata_b = get_associated_token_address(&payer_pubkey, &whirlpool.token_mint_b); - println!("ata_a {}", ata_a.to_string()); - println!("ata_b {}", ata_b.to_string()); + info!("ata_a {}", ata_a.to_string()); + info!("ata_b {}", ata_b.to_string()); // execute proxy_swap let instructions = program