From 25cc74b3379baf6ea9fd0e8dbb486c92d34bbc5c Mon Sep 17 00:00:00 2001 From: gaomingjun Date: Fri, 29 Nov 2024 16:42:07 +0800 Subject: [PATCH] refactor raydium swap --- Cargo.lock | 396 ++++++++++++++++-------------------------------- Cargo.toml | 5 +- examples/rpc.rs | 66 +++++++- src/main.rs | 2 +- src/pool.rs | 119 ++++++--------- src/pump.rs | 3 +- src/raydium.rs | 261 +++++++++++++++++++++---------- src/tx.rs | 2 +- 8 files changed, 429 insertions(+), 425 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aeeaef0..ef1843c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,6 +123,30 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b2d54853319fd101b8dd81de382bcbf3e03410a64d8928bbee85a3e7dcde483" +[[package]] +name = "amm-cli" +version = "0.1.0" +source = "git+https://github.com/raydium-io/raydium-library#51bb7998542a0baec2172950ea621e068e3e158a" +dependencies = [ + "anchor-client", + "anchor-lang", + "anyhow", + "arrayref", + "bs58 0.5.1", + "clap 4.5.7", + "common", + "hex", + "raydium_amm", + "safe-transmute", + "serum_dex", + "solana-client", + "solana-sdk", + "spl-associated-token-account", + "spl-token 4.0.0", + "spl-token-2022", + "toml 0.8.19", +] + [[package]] name = "anchor-attribute-access-control" version = "0.29.0" @@ -279,11 +303,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c4fd6e43b2ca6220d2ef1641539e678bfc31b6cc393cf892b373b5997b6a39a" dependencies = [ "anchor-lang", - "mpl-token-metadata 3.2.3", + "mpl-token-metadata", "solana-program", "spl-associated-token-account", + "spl-memo", "spl-token 4.0.0", - "spl-token-2022 0.9.0", + "spl-token-2022", ] [[package]] @@ -737,6 +762,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -1230,6 +1261,31 @@ dependencies = [ "unreachable", ] +[[package]] +name = "common" +version = "0.1.0" +source = "git+https://github.com/raydium-io/raydium-library#51bb7998542a0baec2172950ea621e068e3e158a" +dependencies = [ + "anchor-client", + "anchor-lang", + "anchor-spl", + "anyhow", + "base64 0.22.1", + "bincode", + "bs58 0.5.1", + "clap 4.5.7", + "serde", + "serde_json", + "solana-account-decoder", + "solana-client", + "solana-sdk", + "solana-transaction-status", + "spl-associated-token-account", + "spl-token 4.0.0", + "spl-token-2022", + "toml 0.8.19", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1463,12 +1519,6 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" -[[package]] -name = "debug_print" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f215f9b7224f49fb73256115331f677d868b34d18b65dbe4db392e6021eea90" - [[package]] name = "default-env" version = "0.1.1" @@ -2038,7 +2088,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.2.6", + "indexmap 2.6.0", "slab", "tokio", "tokio-util 0.7.11", @@ -2083,9 +2133,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -2397,12 +2447,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -2749,45 +2799,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "mpl-token-auth-rules" -version = "1.4.3-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a34d740606a10a9dac7507d0c9025d72e0ce311c68ae85b6634982cf69a9c6" -dependencies = [ - "borsh 0.9.3", - "bytemuck", - "mpl-token-metadata-context-derive 0.2.1", - "num-derive 0.3.3", - "num-traits", - "rmp-serde", - "serde", - "shank", - "solana-program", - "solana-zk-token-sdk", - "thiserror", -] - -[[package]] -name = "mpl-token-metadata" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "654976568c99887549e1291e7b7e55ae31a70732e56ebb25cb1cdfc08c018333" -dependencies = [ - "arrayref", - "borsh 0.9.3", - "mpl-token-auth-rules", - "mpl-token-metadata-context-derive 0.3.0", - "mpl-utils", - "num-derive 0.3.3", - "num-traits", - "shank", - "solana-program", - "spl-associated-token-account", - "spl-token 4.0.0", - "thiserror", -] - [[package]] name = "mpl-token-metadata" version = "3.2.3" @@ -2801,37 +2812,6 @@ dependencies = [ "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.36", - "syn 1.0.109", -] - -[[package]] -name = "mpl-token-metadata-context-derive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a739019e11d93661a64ef5fe108ab17c79b35961e944442ff6efdd460ad01a" -dependencies = [ - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "mpl-utils" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee1b830bfd014504a11b2234e2e7d6af535adda601f224cd519b923f593c91b" -dependencies = [ - "arrayref", - "solana-program", - "spl-token-2022 0.8.0", -] - [[package]] name = "native-tls" version = "0.2.12" @@ -3371,7 +3351,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml", + "toml 0.5.11", ] [[package]] @@ -3618,60 +3598,10 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "raydium-amm-v3" -version = "0.1.0" -source = "git+https://github.com/raydium-io/raydium-clmm#2eb458dfd634bb28521e25a81da3e93fa8926644" -dependencies = [ - "anchor-lang", - "anchor-spl", - "arrayref", - "bytemuck", - "mpl-token-metadata 1.13.2", - "solana-program", - "solana-security-txt", - "spl-memo", - "uint", -] - -[[package]] -name = "raydium-library" -version = "0.3.0" -source = "git+https://github.com/raydium-io/raydium-library#4768549609a69199e4be36eb2a7cc34130f16476" -dependencies = [ - "anchor-client", - "anchor-lang", - "anyhow", - "arrayref", - "base64 0.13.1", - "bincode", - "bs58 0.4.0", - "bytemuck", - "debug_print", - "mpl-token-metadata 1.13.2", - "rand 0.7.3", - "raydium-amm-v3", - "raydium_amm", - "safe-transmute", - "serde", - "serde_json", - "serum_dex", - "solana-account-decoder", - "solana-client", - "solana-program", - "solana-sdk", - "solana-transaction-status", - "spl-associated-token-account", - "spl-memo", - "spl-token 4.0.0", - "spl-token-2022 0.9.0", - "uint", -] - [[package]] name = "raydium_amm" -version = "0.3.0" -source = "git+https://github.com/raydium-io/raydium-amm#ae039d21cd49ef670d76b3a1cf5485ae0213dc5e" +version = "0.3.1" +source = "git+https://github.com/raydium-io/raydium-amm#ba702b65c1b4206478f1cefc8bb369fe2b891d44" dependencies = [ "arrayref", "arrform", @@ -3685,6 +3615,7 @@ dependencies = [ "serde_json", "serum_dex", "solana-program", + "solana-security-txt", "spl-associated-token-account", "spl-token 4.0.0", "thiserror", @@ -3715,16 +3646,17 @@ dependencies = [ name = "raytx" version = "0.1.0" dependencies = [ + "amm-cli", "anyhow", "axum", "clap 4.5.7", + "common", "ctor", "dotenvy", "futures-util", "indicatif", "jito-block-engine-json-rpc-client", "rand 0.8.5", - "raydium-library", "raydium_amm", "reqwest", "rust_decimal", @@ -3735,7 +3667,7 @@ dependencies = [ "solana-sdk", "spl-associated-token-account", "spl-token 4.0.0", - "spl-token-2022 0.9.0", + "spl-token-2022", "spl-token-client", "tokio", "tokio-tungstenite 0.23.1", @@ -3944,28 +3876,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "rmp" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" -dependencies = [ - "byteorder", - "num-traits", - "paste", -] - -[[package]] -name = "rmp-serde" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" -dependencies = [ - "byteorder", - "rmp", - "serde", -] - [[package]] name = "rpassword" version = "7.3.1" @@ -4258,6 +4168,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4298,7 +4217,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.6.0", "itoa", "ryu", "serde", @@ -4308,7 +4227,7 @@ dependencies = [ [[package]] name = "serum_dex" version = "0.5.10" -source = "git+https://github.com/raydium-io/openbook-dex#cff113afcec5bcf3c7aaace42d03b5d138d4328e" +source = "git+https://github.com/raydium-io/openbook-dex#8a755555b625748f9d9e32526a2772717906ee7f" dependencies = [ "anchor-lang", "arrayref", @@ -4399,40 +4318,6 @@ 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.86", - "quote 1.0.36", - "shank_macro_impl", - "syn 1.0.109", -] - -[[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.86", - "quote 1.0.36", - "serde", - "syn 1.0.109", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -4533,7 +4418,7 @@ dependencies = [ "solana-config-program", "solana-sdk", "spl-token 4.0.0", - "spl-token-2022 0.9.0", + "spl-token-2022", "spl-token-metadata-interface", "thiserror", "zstd", @@ -5161,7 +5046,7 @@ dependencies = [ "solana-sdk", "solana-transaction-status", "solana-version", - "spl-token-2022 0.9.0", + "spl-token-2022", "thiserror", ] @@ -5462,7 +5347,7 @@ dependencies = [ "spl-associated-token-account", "spl-memo", "spl-token 4.0.0", - "spl-token-2022 0.9.0", + "spl-token-2022", "thiserror", ] @@ -5616,7 +5501,7 @@ dependencies = [ "num-traits", "solana-program", "spl-token 4.0.0", - "spl-token-2022 0.9.0", + "spl-token-2022", "thiserror", ] @@ -5702,20 +5587,6 @@ dependencies = [ "syn 2.0.68", ] -[[package]] -name = "spl-tlv-account-resolution" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7960b1e1a41e4238807fca0865e72a341b668137a3f2ddcd770d04fd1b374c96" -dependencies = [ - "bytemuck", - "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-type-length-value", -] - [[package]] name = "spl-tlv-account-resolution" version = "0.4.0" @@ -5760,28 +5631,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "spl-token-2022" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84fc0c7a763c3f53fa12581d07ed324548a771bb648a1217e4f330b1d0a59331" -dependencies = [ - "arrayref", - "bytemuck", - "num-derive 0.4.2", - "num-traits", - "num_enum 0.7.2", - "solana-program", - "solana-zk-token-sdk", - "spl-memo", - "spl-pod", - "spl-token 4.0.0", - "spl-token-metadata-interface", - "spl-transfer-hook-interface 0.2.0", - "spl-type-length-value", - "thiserror", -] - [[package]] name = "spl-token-2022" version = "0.9.0" @@ -5799,7 +5648,7 @@ dependencies = [ "spl-pod", "spl-token 4.0.0", "spl-token-metadata-interface", - "spl-transfer-hook-interface 0.3.0", + "spl-transfer-hook-interface", "spl-type-length-value", "thiserror", ] @@ -5823,9 +5672,9 @@ dependencies = [ "spl-associated-token-account", "spl-memo", "spl-token 4.0.0", - "spl-token-2022 0.9.0", + "spl-token-2022", "spl-token-metadata-interface", - "spl-transfer-hook-interface 0.3.0", + "spl-transfer-hook-interface", "thiserror", ] @@ -5843,22 +5692,6 @@ dependencies = [ "spl-type-length-value", ] -[[package]] -name = "spl-transfer-hook-interface" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7489940049417ae5ce909314bead0670e2a5ea5c82d43ab96dc15c8fcbbccba" -dependencies = [ - "arrayref", - "bytemuck", - "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-tlv-account-resolution 0.3.0", - "spl-type-length-value", -] - [[package]] name = "spl-transfer-hook-interface" version = "0.3.0" @@ -5871,7 +5704,7 @@ dependencies = [ "spl-discriminator", "spl-pod", "spl-program-error", - "spl-tlv-account-resolution 0.4.0", + "spl-tlv-account-resolution", "spl-type-length-value", ] @@ -6386,11 +6219,26 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.22", +] + [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -6398,9 +6246,9 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.6.0", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] @@ -6409,9 +6257,22 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.6.0", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap 2.6.0", + "serde", + "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -7135,6 +6996,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" diff --git a/Cargo.toml b/Cargo.toml index 19976e8..597f4b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,10 @@ solana-client = "=1.16.27" solana-sdk = "=1.16.27" solana-account-decoder = "=1.16.27" spl-token-client = "=0.7.1" -raydium-library = { git = "https://github.com/raydium-io/raydium-library" } +# raydium-library = { git = "https://github.com/raydium-io/raydium-library" } +amm-cli = { git = "https://github.com/raydium-io/raydium-library" } +common = { git = "https://github.com/raydium-io/raydium-library" } + raydium_amm = { git = "https://github.com/raydium-io/raydium-amm", default-features = false, features = [ "client", ] } diff --git a/examples/rpc.rs b/examples/rpc.rs index 6ec9508..89b4dbd 100644 --- a/examples/rpc.rs +++ b/examples/rpc.rs @@ -1,6 +1,8 @@ use std::{env, str::FromStr}; +use amm_cli::load_amm_keys; use anyhow::{Context, Result}; +use common::common_utils; use futures_util::{SinkExt, StreamExt}; use raytx::{get_rpc_client_blocking, logger, pump::PUMP_PROGRAM}; use solana_client::rpc_client::GetConfirmedSignaturesForAddress2Config; @@ -13,7 +15,69 @@ async fn main() -> Result<()> { logger::init(); // get_signatures().await?; - connect_websocket().await?; + // connect_websocket().await?; + get_amm_info().await?; + Ok(()) +} + +pub async fn get_amm_info() -> Result<()> { + let client = get_rpc_client_blocking()?; + // let amm_pool_id = Pubkey::from_str("3vehHGc8J9doSo6gJoWYG23JG54hc2i7wjdFReX3Rcah")?; + let amm_pool_id = Pubkey::from_str("5WGx6mE9Xww3ocYzSenGVQMJLCVVwK7ePnYV6cXcpJtK")?; + + let pool_state = + common::rpc::get_account::(&client, &amm_pool_id)?.unwrap(); + + println!("pool_state : {:#?}", pool_state); + let amm_program = Pubkey::from_str("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8")?; + let native_mint = spl_token::native_mint::ID; + let amount_specified = 100_000_000; + let slippage_bps = 10; + let swap_base_in = true; + let user_input_token = if pool_state.coin_vault_mint == native_mint { + pool_state.pc_vault + } else { + pool_state.coin_vault + }; + + let amm_keys = load_amm_keys(&client, &amm_program, &amm_pool_id).unwrap(); + // reload accounts data to calculate amm pool vault amount + // get multiple accounts at the same time to ensure data consistency + let load_pubkeys = vec![ + amm_pool_id, + amm_keys.amm_pc_vault, + amm_keys.amm_coin_vault, + user_input_token, + ]; + let rsps = common::rpc::get_multiple_accounts(&client, &load_pubkeys).unwrap(); + + println!("rsps: {:#?}", rsps); + + let amm_pc_vault_account = rsps[1].clone(); + let amm_coin_vault_account = rsps[2].clone(); + let _token_in_account = rsps[3].clone(); + + let amm_pc_vault = + common_utils::unpack_token(&amm_pc_vault_account.as_ref().unwrap().data).unwrap(); + let amm_coin_vault = + common_utils::unpack_token(&amm_coin_vault_account.as_ref().unwrap().data).unwrap(); + + println!("amm_pc_vault: {:#?}", amm_pc_vault.base.amount); + println!("amm_coin_vault: {:#?}", amm_coin_vault.base.amount); + + let swap_info_result = amm_cli::calculate_swap_info( + &client, + amm_program, + amm_pool_id, + user_input_token, + amount_specified, + slippage_bps, + swap_base_in, + ) + .unwrap(); + + println!("swap_info_result : {:#?}", swap_info_result); + Ok(()) } diff --git a/src/main.rs b/src/main.rs index d72fc11..00bf430 100644 --- a/src/main.rs +++ b/src/main.rs @@ -230,7 +230,7 @@ async fn main() -> Result<()> { &token_account.mint, ) .await?; - let pool_id = pool_info.data.get_pool().unwrap().id; + let pool_id = pool_info.get_pool().unwrap().id; info!("pool id: {}", pool_id); } }, diff --git a/src/pool.rs b/src/pool.rs index 006457b..69d081e 100644 --- a/src/pool.rs +++ b/src/pool.rs @@ -1,15 +1,12 @@ use std::str::FromStr; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; +use common::common_utils; use solana_sdk::pubkey::Pubkey; -use solana_sdk::signer::Signer; use spl_token_2022::amount_to_ui_amount; -use tracing::{debug, info, warn}; +use tracing::{debug, warn}; -use crate::{ - helper::get_solana_price, - raydium::{get_pool_info_by_id, Raydium}, -}; +use crate::{helper::get_solana_price, raydium::Raydium}; impl Raydium { pub async fn get_pool(&self, pool_id: &str) -> Result<(f64, f64, f64, f64, f64)> { @@ -19,7 +16,7 @@ impl Raydium { .inspect_err(|err| warn!("failed get solana price: {}", err))?; let usd_price = ((price * sol_price) * 1_000_000_000.0).round() / 1_000_000_000.0; - info!("sol price: {}, usd_price: {} ", sol_price, usd_price); + debug!("sol price: {}, usd_price: {} ", sol_price, usd_price); Ok((base, quote, price, usd_price, sol_price)) } @@ -27,83 +24,59 @@ impl Raydium { pub async fn get_pool_price(&self, pool_id: &str) -> Result<(f64, f64, f64)> { let amm_pool_id = Pubkey::from_str(pool_id).inspect_err(|err| warn!("failed parse pool_id: {}", err))?; - let pool_info = get_pool_info_by_id(pool_id) - .await? - .get_pool() - .ok_or(anyhow!("pool is empty"))?; - debug!("amm pool id: {:?}", amm_pool_id); - let client = self .client_blocking .clone() .context("failed to get rpc client")?; - let owner = self.keypair.pubkey(); - let amm_program = Pubkey::from_str("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8")?; - let market_program = Pubkey::from_str("srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX")?; - let market_id = Pubkey::from_str(&pool_info.market_id)?; - let mint_a = ( - Pubkey::from_str(&pool_info.mint_a.address)?, - pool_info.mint_a.decimals, - pool_info.mint_a.symbol, - ); - let mint_b = ( - Pubkey::from_str(&pool_info.mint_b.address)?, - pool_info.mint_b.decimals, - pool_info.mint_b.symbol, - ); - debug!("{mint_a:#?}, {mint_b:#?}"); - let amm_keys = raydium_library::amm::utils::get_amm_pda_keys( - &amm_program, - &market_program, - &market_id, - &mint_a.0, - &mint_b.0, - )?; - debug!("amm_keys: {amm_keys:#?}"); - if amm_keys.amm_pool != amm_pool_id { - warn!("amm_keys's amm_pool not match input pool_id"); - return Err(anyhow!("internal error")); - } + let pool_state = + common::rpc::get_account::(&client, &amm_pool_id)? + .unwrap(); + + // debug!("pool_state : {:#?}", pool_state); - // load market keys - let market_keys = raydium_library::amm::openbook::get_keys_for_market( - &client, - &amm_keys.market_program, - &amm_keys.market, - ) - .inspect_err(|e| { - warn!("failed to get market_keys: {}", e); - })?; + let load_pubkeys = vec![pool_state.pc_vault, pool_state.coin_vault]; + let rsps = common::rpc::get_multiple_accounts(&client, &load_pubkeys).unwrap(); - // calculate amm pool vault with load data at the same time or use simulate to calculate - let calculate_result = raydium_library::amm::calculate_pool_vault_amounts( - &client, - &amm_program, - &amm_pool_id, - &amm_keys, - &market_keys, - raydium_library::amm::utils::CalculateMethod::Simulate(owner), - )?; - debug!("calculate_pool result: {:#?}", calculate_result); + let amm_pc_vault_account = rsps[0].clone(); + let amm_coin_vault_account = rsps[1].clone(); - let mut pool_pc = (mint_b.clone(), calculate_result.pool_pc_vault_amount); - let mut pool_coin = (mint_a.clone(), calculate_result.pool_coin_vault_amount); + let amm_pc_vault = + common_utils::unpack_token(&amm_pc_vault_account.as_ref().unwrap().data).unwrap(); + let amm_coin_vault = + common_utils::unpack_token(&amm_coin_vault_account.as_ref().unwrap().data).unwrap(); - if amm_keys.amm_pc_mint != spl_token::native_mint::ID { - pool_pc = (mint_a, calculate_result.pool_coin_vault_amount); - pool_coin = (mint_b, calculate_result.pool_pc_vault_amount) - } + let (base_account, quote_account) = if amm_coin_vault.base.is_native() { + ( + ( + pool_state.pc_vault_mint, + amount_to_ui_amount(amm_pc_vault.base.amount, pool_state.pc_decimals as u8), + ), + ( + pool_state.coin_vault_mint, + amount_to_ui_amount(amm_coin_vault.base.amount, pool_state.coin_decimals as u8), + ), + ) + } else { + ( + ( + pool_state.coin_vault_mint, + amount_to_ui_amount(amm_coin_vault.base.amount, pool_state.coin_decimals as u8), + ), + ( + pool_state.pc_vault_mint, + amount_to_ui_amount(amm_pc_vault.base.amount, pool_state.pc_decimals as u8), + ), + ) + }; - let pool_pc_ui_amount = amount_to_ui_amount(pool_pc.1, pool_pc.0 .1); - let pool_coin_ui_amount = amount_to_ui_amount(pool_coin.1, pool_coin.0 .1); - let price = pool_pc_ui_amount / pool_coin_ui_amount; + let price = quote_account.1 / base_account.1; - info!( - "calculate pool: {}: {}, {}: {}, price: {} sol", - pool_pc.0 .2, pool_pc_ui_amount, pool_coin.0 .2, pool_coin_ui_amount, price + debug!( + "calculate pool[{}]: {}: {}, {}: {}, price: {} sol", + amm_pool_id, base_account.0, base_account.1, quote_account.0, quote_account.1, price ); - Ok((pool_coin_ui_amount, pool_pc_ui_amount, price)) + Ok((base_account.1, quote_account.1, price)) } } diff --git a/src/pump.rs b/src/pump.rs index 3bfaa51..1e6f37b 100644 --- a/src/pump.rs +++ b/src/pump.rs @@ -2,7 +2,6 @@ use std::{str::FromStr, sync::Arc}; use anyhow::{anyhow, Context, Result}; use raydium_amm::math::U128; -use raydium_library::amm::TEN_THOUSAND; use serde::{Deserialize, Serialize}; use serde_json::Value; use solana_client::nonblocking::rpc_client::RpcClient; @@ -24,7 +23,7 @@ use crate::{ swap::{SwapDirection, SwapInType}, token, tx, }; - +pub const TEN_THOUSAND: u64 = 10000; pub const TOKEN_PROGRAM: &str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"; pub const RENT_PROGRAM: &str = "SysvarRent111111111111111111111111111111111"; pub const ASSOCIATED_TOKEN_PROGRAM: &str = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"; diff --git a/src/raydium.rs b/src/raydium.rs index c44b6ef..c56ce78 100644 --- a/src/raydium.rs +++ b/src/raydium.rs @@ -1,12 +1,17 @@ use std::env; +use amm_cli::AmmSwapInfoResult; use anyhow::{anyhow, Context, Result}; -use raydium_library::amm; +use raydium_amm::state::{AmmInfo, Loadable}; use reqwest::Proxy; use serde::Deserialize; -use solana_client::nonblocking::rpc_client::RpcClient; +use solana_client::{ + nonblocking::rpc_client::RpcClient, + rpc_filter::{Memcmp, RpcFilterType}, +}; use solana_sdk::{ // native_token::LAMPORTS_PER_SOL, + instruction::Instruction, program_pack::Pack, pubkey::Pubkey, signature::Keypair, @@ -27,6 +32,8 @@ use spl_token::state::Account; use tracing::{debug, error, info}; +pub const AMM_PROGRAM: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"; + pub struct Raydium { pub client: Arc, pub keypair: Arc, @@ -59,7 +66,7 @@ impl Raydium { pub async fn swap( &self, - mint: &str, + mint_str: &str, amount_in: f64, swap_direction: SwapDirection, in_type: SwapInType, @@ -69,24 +76,30 @@ impl Raydium { // slippage_bps = 50u64; // 0.5% let slippage_bps = slippage * 100; let owner = self.keypair.pubkey(); - let mint = - Pubkey::from_str(mint).map_err(|e| anyhow!("failed to parse mint pubkey: {}", e))?; + let mint = Pubkey::from_str(mint_str) + .map_err(|e| anyhow!("failed to parse mint pubkey: {}", e))?; let program_id = spl_token::ID; let native_mint = spl_token::native_mint::ID; - let (token_in, token_out) = match swap_direction { - SwapDirection::Buy => (native_mint, mint), - SwapDirection::Sell => (mint, native_mint), - }; - let pool_data = match self.pool_id.clone() { - Some(pool_id) => get_pool_info_by_id(&pool_id).await?, - None => { - get_pool_info(&token_in.to_string(), &token_out.to_string()) - .await? - .data - } + let (amm_pool_id, pool_state) = get_pool_state( + self.client_blocking.clone().unwrap(), + self.pool_id.as_deref(), + Some(mint_str), + ) + .await?; + // debug!("pool_state: {:#?}", pool_state); + + let (token_in, token_out, user_input_token, swap_base_in) = match ( + swap_direction.clone(), + pool_state.coin_vault_mint == native_mint, + ) { + (SwapDirection::Buy, true) => (native_mint, mint, pool_state.coin_vault, true), + (SwapDirection::Buy, false) => (native_mint, mint, pool_state.pc_vault, true), + (SwapDirection::Sell, true) => (mint, native_mint, pool_state.pc_vault, true), + (SwapDirection::Sell, false) => (mint, native_mint, pool_state.coin_vault, true), }; - let pool_info = pool_data.get_pool().ok_or(anyhow!("failed to get pool"))?; + + debug!("token_in:{token_in}, token_out:{token_out}, user_input_token:{user_input_token}, swap_base_in:{swap_base_in}"); let in_ata = token::get_associated_token_address( self.client.clone(), @@ -103,7 +116,6 @@ impl Raydium { let mut create_instruction = None; let mut close_instruction = None; - let swap_base_in = true; let (amount_specified, amount_ui_pretty) = match swap_direction { SwapDirection::Buy => { @@ -185,75 +197,26 @@ impl Raydium { } }; - let amm_program = Pubkey::from_str("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8")?; - let market_program = Pubkey::from_str("srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX")?; - let market_id = Pubkey::from_str(&pool_info.market_id)?; - let amm_pool_id = Pubkey::from_str(&pool_info.id)?; + let amm_program = Pubkey::from_str(AMM_PROGRAM)?; debug!("amm pool id: {amm_pool_id}"); let client = get_rpc_client_blocking()?; - - // load amm keys - // since load_amm_keys is not available, get_amm_pda_keys is used here, - // and the parameters(coin_mint, pc_mint) look a little strange. - let amm_keys = raydium_library::amm::utils::get_amm_pda_keys( - &amm_program, - &market_program, - &market_id, - &mint, - &native_mint, - )?; - debug!("amm_keys: {amm_keys:#?}"); - // load market keys - let market_keys = raydium_library::amm::openbook::get_keys_for_market( - &client, - &amm_keys.market_program, - &amm_keys.market, - ) - .inspect_err(|e| { - error!("failed to get market_keys: {}", e); - })?; - // calculate amm pool vault with load data at the same time or use simulate to calculate - let result = raydium_library::amm::calculate_pool_vault_amounts( + let swap_info_result = amm_cli::calculate_swap_info( &client, - &amm_program, - &amm_pool_id, - &amm_keys, - &market_keys, - raydium_library::amm::utils::CalculateMethod::Simulate(owner), + amm_program, + amm_pool_id, + user_input_token, + amount_specified, + slippage_bps, + swap_base_in, )?; - debug!("calculate_pool result: {:#?}", result); - // setting direction - let (mut direction, mut direction_str) = match swap_direction { - SwapDirection::Buy => (amm::utils::SwapDirection::PC2Coin, "PC2Coin"), - SwapDirection::Sell => (amm::utils::SwapDirection::Coin2PC, "Coin2PC"), - }; - // if mint_a is native mint, reverse direction - if pool_info.mint_a.address == native_mint.to_string() { - (direction, direction_str) = match direction { - amm::utils::SwapDirection::PC2Coin => { - (amm::utils::SwapDirection::Coin2PC, "Coin2PC") - } - amm::utils::SwapDirection::Coin2PC => { - (amm::utils::SwapDirection::PC2Coin, "PC2Coin") - } - }; - } - debug!("direction: {}", direction_str); + let other_amount_threshold = swap_info_result.other_amount_threshold; + + info!("swap_info_result: {:#?}", swap_info_result); info!( "swap: {}, value: {:?} -> {}", token_in, amount_ui_pretty, token_out ); - let other_amount_threshold = raydium_library::amm::swap_with_slippage( - result.pool_pc_vault_amount, - result.pool_coin_vault_amount, - result.swap_fee_numerator, - result.swap_fee_denominator, - direction, - amount_specified, - swap_base_in, - slippage_bps, - )?; // build instructions let mut instructions = vec![]; // sol <-> wsol support @@ -324,10 +287,9 @@ impl Raydium { } // build swap instruction - let build_swap_instruction = raydium_library::amm::swap( + let build_swap_instruction = amm_swap( &amm_program, - &amm_keys, - &market_keys, + swap_info_result, &owner, &final_in_ata, &final_out_ata, @@ -356,9 +318,142 @@ impl Raydium { } } +pub fn amm_swap( + amm_program: &Pubkey, + result: AmmSwapInfoResult, + user_owner: &Pubkey, + user_source: &Pubkey, + user_destination: &Pubkey, + amount_specified: u64, + other_amount_threshold: u64, + swap_base_in: bool, +) -> Result { + let swap_instruction = if swap_base_in { + raydium_amm::instruction::swap_base_in( + &amm_program, + &result.pool_id, + &result.amm_authority, + &result.amm_open_orders, + &result.amm_coin_vault, + &result.amm_pc_vault, + &result.market_program, + &result.market, + &result.market_bids, + &result.market_asks, + &result.market_event_queue, + &result.market_coin_vault, + &result.market_pc_vault, + &result.market_vault_signer, + user_source, + user_destination, + user_owner, + amount_specified, + other_amount_threshold, + )? + } else { + raydium_amm::instruction::swap_base_out( + &amm_program, + &result.pool_id, + &result.amm_authority, + &result.amm_open_orders, + &result.amm_coin_vault, + &result.amm_pc_vault, + &result.market_program, + &result.market, + &result.market_bids, + &result.market_asks, + &result.market_event_queue, + &result.market_coin_vault, + &result.market_pc_vault, + &result.market_vault_signer, + user_source, + user_destination, + user_owner, + other_amount_threshold, + amount_specified, + )? + }; + + Ok(swap_instruction) +} + +pub async fn get_pool_state( + rpc_client: Arc, + pool_id: Option<&str>, + mint: Option<&str>, +) -> Result<(Pubkey, AmmInfo)> { + if let Some(pool_id) = pool_id { + debug!("finding pool state by pool_id: {}", pool_id); + let amm_pool_id = Pubkey::from_str(pool_id)?; + let pool_state = + common::rpc::get_account::(&rpc_client, &amm_pool_id)? + .ok_or(anyhow!("NotFoundPool: pool state not found"))?; + Ok((amm_pool_id, pool_state)) + } else { + if let Some(mint) = mint { + let pool_data = get_pool_info(&spl_token::native_mint::ID.to_string(), mint).await; + if let Ok(pool_data) = pool_data { + let pool = pool_data + .get_pool() + .ok_or(anyhow!("NotFoundPool: pool not found in raydium api"))?; + let amm_pool_id = Pubkey::from_str(&pool.id)?; + debug!("finding pool state by raydium api: {}", amm_pool_id); + let pool_state = common::rpc::get_account::( + &rpc_client, + &amm_pool_id, + )? + .ok_or(anyhow!("NotFoundPool: pool state not found"))?; + return Ok((amm_pool_id, pool_state)); + } else { + get_pool_state_by_mint(rpc_client, mint).await + } + } else { + Err(anyhow!("NotFoundPool: pool state not found")) + } + } +} + +pub async fn get_pool_state_by_mint( + rpc_client: Arc, + mint: &str, +) -> Result<(Pubkey, AmmInfo)> { + debug!("finding pool state by mint: {}", mint); + let pc_mint = Some(spl_token::native_mint::ID); + let coin_mint = Pubkey::from_str(mint).ok(); + // fetch pool by filters + let pool_len = core::mem::size_of::() as u64; + let filters = match (coin_mint, pc_mint) { + (None, None) => Some(vec![RpcFilterType::DataSize(pool_len)]), + (Some(coin_mint), None) => Some(vec![ + RpcFilterType::Memcmp(Memcmp::new_base58_encoded(400, &coin_mint.to_bytes())), + RpcFilterType::DataSize(pool_len), + ]), + (None, Some(pc_mint)) => Some(vec![ + RpcFilterType::Memcmp(Memcmp::new_base58_encoded(432, &pc_mint.to_bytes())), + RpcFilterType::DataSize(pool_len), + ]), + (Some(coin_mint), Some(pc_mint)) => Some(vec![ + RpcFilterType::Memcmp(Memcmp::new_base58_encoded(400, &coin_mint.to_bytes())), + RpcFilterType::Memcmp(Memcmp::new_base58_encoded(432, &pc_mint.to_bytes())), + RpcFilterType::DataSize(pool_len), + ]), + }; + let amm_program = Pubkey::from_str(AMM_PROGRAM)?; + let pools = + common::rpc::get_program_accounts_with_filters(&rpc_client, amm_program, filters).unwrap(); + + if pools.len() > 1 { + let pool = &pools[0]; + let pool_state = raydium_amm::state::AmmInfo::load_from_bytes(&pools[0].1.data)?; + Ok((pool.0, pool_state.clone())) + } else { + return Err(anyhow!("NotFoundPool: pool state not found")); + } +} + // get pool info // https://api-v3.raydium.io/pools/info/mint?mint1=So11111111111111111111111111111111111111112&mint2=EzM2d8JVpzfhV7km3tUsR1U1S4xwkrPnWkM4QFeTpump&poolType=standard&poolSortField=default&sortType=desc&pageSize=10&page=1 -pub async fn get_pool_info(mint1: &str, mint2: &str) -> Result { +pub async fn get_pool_info(mint1: &str, mint2: &str) -> Result { let mut client_builder = reqwest::Client::builder(); if let Ok(http_proxy) = env::var("HTTP_PROXY") { let proxy = Proxy::all(http_proxy)?; @@ -382,7 +477,7 @@ pub async fn get_pool_info(mint1: &str, mint2: &str) -> Result { .json::() .await .context("Failed to parse pool info JSON")?; - Ok(result) + Ok(result.data) } // get pool info by ids // https://api-v3.raydium.io/pools/info/ids?ids=3RHg85W1JtKeqFQSxBfd2RX13aBFvvy6gcATkHU657mL diff --git a/src/tx.rs b/src/tx.rs index 27e25f1..ca40e10 100644 --- a/src/tx.rs +++ b/src/tx.rs @@ -107,7 +107,7 @@ pub async fn new_signed_and_send( ) .await?; } else { - let sig = raydium_library::common::rpc::send_txn(&client, &txn, true)?; + let sig = common::rpc::send_txn(&client, &txn, true)?; info!("signature: {:?}", sig); txs.push(sig.to_string()); }