diff --git a/lazer/solana/Cargo.lock b/lazer/solana/Cargo.lock index f4555d8..895b84a 100644 --- a/lazer/solana/Cargo.lock +++ b/lazer/solana/Cargo.lock @@ -319,6 +319,55 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.93" @@ -1050,6 +1099,12 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "combine" version = "3.8.1" @@ -1552,6 +1607,16 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + [[package]] name = "env_logger" version = "0.9.3" @@ -1565,6 +1630,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -2275,6 +2353,12 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -3204,13 +3288,16 @@ name = "pyth-lazer-solana-example" version = "0.1.0" dependencies = [ "anchor-lang", + "anyhow", "bytemuck", "byteorder", + "env_logger 0.11.5", "hex", "num-derive 0.4.2", "num-traits", "pyth-lazer-sdk", "pyth-lazer-solana-contract", + "solana-client", "solana-program", "solana-program-test", "solana-sdk", @@ -4377,7 +4464,7 @@ version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "121d36ffb3c6b958763312cbc697fbccba46ee837d3a0aa4fc0e90fcb3b884f3" dependencies = [ - "env_logger", + "env_logger 0.9.3", "lazy_static", "log", ] @@ -6095,6 +6182,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.11.0" diff --git a/lazer/solana/Cargo.toml b/lazer/solana/Cargo.toml index f19eee7..6157572 100644 --- a/lazer/solana/Cargo.toml +++ b/lazer/solana/Cargo.toml @@ -7,6 +7,10 @@ edition = "2021" name = "pyth_lazer_solana_example" crate-type = ["cdylib", "lib"] +[[bin]] +name = "client" +required-features = ["client"] + [dependencies] pyth-lazer-sdk = "0.1.0" @@ -14,6 +18,12 @@ solana-program = "1.18.26" bytemuck = { version = "1.18.0", features = ["derive"] } num-traits = "0.2.19" num-derive = "0.4.2" +solana-client = { version = "1.18", optional = true } +anyhow = { version = "1.0.93", optional = true } +solana-sdk = { version = "1.18.26", optional = true } +hex = { version = "0.4.3", optional = true } +pyth-lazer-solana-contract = { version = "0.1.0", optional = true } +env_logger = { version = "0.11.5", optional = true } [dev-dependencies] hex = "0.4.3" @@ -23,3 +33,12 @@ tokio = { version = "1.40.0", features = ["full"] } byteorder = "1.5.0" pyth-lazer-solana-contract = "0.1.0" anchor-lang = "0.30.1" + +[features] +solana-client = ["dep:solana-client"] +anyhow = ["dep:anyhow"] +solana-sdk = ["dep:solana-sdk"] +hex = ["dep:hex"] +pyth-lazer-solana-contract = ["dep:pyth-lazer-solana-contract"] +env_logger = ["dep:env_logger"] +client = ["solana-client", "anyhow", "solana-sdk", "hex", "pyth-lazer-solana-contract", "env_logger"] diff --git a/lazer/solana/src/bin/client.rs b/lazer/solana/src/bin/client.rs new file mode 100644 index 0000000..e6b7951 --- /dev/null +++ b/lazer/solana/src/bin/client.rs @@ -0,0 +1,97 @@ +use { + anyhow::Context, + bytemuck::bytes_of, + pyth_lazer_sdk::ed25519_program_args, + pyth_lazer_solana_example::{InitializeArgs, Instruction as ExampleInstruction, UpdateArgs}, + solana_client::rpc_client::RpcClient, + solana_sdk::{ + instruction::{AccountMeta, Instruction}, + message::Message, + pubkey::Pubkey, + signature::read_keypair_file, + signer::Signer, + system_program, sysvar, + transaction::Transaction, + }, + std::env, +}; + +fn main() -> anyhow::Result<()> { + env_logger::init(); + let client = RpcClient::new(env::var("SOLANA_RPC_URL")?); + let latest_blockhash = client.get_latest_blockhash()?; + let keypair = read_keypair_file(env::var("SOLANA_KEYPAIR_FILE")?).unwrap(); + let program_id: Pubkey = env::var("EXAMPLE_PROGRAM_PUBKEY")?.parse()?; + + let (data_pda_key, _) = Pubkey::find_program_address(&[b"data"], &program_id); + let cmd = env::args().nth(1).context("missing arg")?; + + if cmd == "init" { + let mut init_data = vec![ExampleInstruction::Initialize as u8]; + init_data.extend_from_slice(bytes_of(&InitializeArgs { price_feed_id: 2 })); + + let tx = Transaction::new( + &[&keypair], + Message::new( + &[Instruction::new_with_bytes( + program_id, + &init_data, + vec![ + AccountMeta::new(keypair.pubkey(), true), + AccountMeta::new(data_pda_key, false), + AccountMeta::new_readonly(system_program::ID, false), + ], + )], + Some(&keypair.pubkey()), + ), + latest_blockhash, + ); + let signature = client.send_and_confirm_transaction(&tx)?; + println!("OK {signature:?}"); + } else if cmd == "update" { + let message = hex::decode(env::var("LAZER_UPDATE_HEX")?)?; + let mut update_data = vec![ExampleInstruction::Update as u8]; + update_data.extend_from_slice(bytes_of(&UpdateArgs { hello: 42 })); + update_data.extend_from_slice(&message); + + // Instruction #0 will be ed25519 instruction; + // Instruction #1 will be our contract instruction. + let instruction_index = 1; + // Total offset of Pyth Lazer update within the instruction data; + // 1 byte is the instruction type. + let message_offset = (size_of::() + 1).try_into().unwrap(); + let ed25519_args = + pyth_lazer_sdk::signature_offsets(&update_data, instruction_index, message_offset); + let tx = Transaction::new( + &[&keypair], + Message::new( + &[ + Instruction::new_with_bytes( + solana_program::ed25519_program::ID, + &ed25519_program_args(&[ed25519_args]), + vec![], + ), + Instruction::new_with_bytes( + program_id, + &update_data, + vec![ + AccountMeta::new_readonly(sysvar::instructions::ID, false), + AccountMeta::new(data_pda_key, false), + AccountMeta::new_readonly( + pyth_lazer_solana_contract::storage::ID, + false, + ), + ], + ), + ], + Some(&keypair.pubkey()), + ), + latest_blockhash, + ); + let signature = client.send_and_confirm_transaction(&tx)?; + println!("OK {signature:?}"); + } else { + panic!("unknown cmd"); + } + Ok(()) +}