use std::sync::Arc;
use std::time::Duration;
use hypersync_client_solana::config::{ClientConfig, StreamConfig};
use hypersync_client_solana::decode::{decode_instruction, metaplex_token_metadata, DecodeError};
use hypersync_client_solana::Client;
use hypersync_solana_net_types::query::{InstructionSelection, SolanaQuery};
const TOKEN_METADATA_PROGRAM: &str = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
fn make_client() -> Arc<Client> {
let cfg = ClientConfig {
url: std::env::var("HYPERSYNC_SOLANA_URL")
.unwrap_or_else(|_| "https://solana.hypersync.xyz".to_string()),
bearer_token: std::env::var("HYPERSYNC_SOLANA_TOKEN").ok(),
http_req_timeout: Duration::from_secs(60),
max_num_retries: 3,
retry_base_ms: 500,
retry_ceiling_ms: 5_000,
..Default::default()
};
Arc::new(Client::new(cfg).expect("build client"))
}
#[tokio::test]
#[ignore]
async fn decode_recent_metaplex_instructions() {
let client = make_client();
let height = client.get_height().await.expect("get_height");
let from = height.saturating_sub(10_000);
let q = SolanaQuery {
from_slot: from,
to_slot: Some(height),
instruction_calls: vec![InstructionSelection {
executing_account: vec![TOKEN_METADATA_PROGRAM.parse().unwrap()],
..Default::default()
}],
max_num_instructions: Some(200),
..Default::default()
};
let resp = client
.collect(q, StreamConfig::default())
.await
.expect("collect");
assert!(
!resp.instruction_calls.is_empty(),
"expected at least one Metaplex instruction in the last 10k slots"
);
eprintln!("pulled {} instructions", resp.instruction_calls.len());
let schema = metaplex_token_metadata();
let mut decoded_ok = 0usize;
let mut unknown_disc = 0usize;
let mut other_err = 0usize;
let mut create_v3_seen = 0usize;
for ix in &resp.instruction_calls {
match decode_instruction(schema, ix) {
Ok(d) => {
decoded_ok += 1;
if d.name == "CreateMetadataAccountV3" {
create_v3_seen += 1;
let data = d.args.get("data").expect("data field");
for key in ["name", "symbol", "uri"] {
let v = data.get(key).expect("DataV2 field");
assert!(v.is_string(), "{key} should be string, got {v:?}");
}
}
}
Err(DecodeError::UnknownDiscriminator(_)) => unknown_disc += 1,
Err(e) => {
other_err += 1;
eprintln!(
" decode error on disc {:02x?}: {e}",
&ix.data.as_deref().unwrap_or_default()[..1]
);
}
}
}
eprintln!(
"decoded {} / {} (unknown_disc={}, other_err={}, create_v3_seen={})",
decoded_ok,
resp.instruction_calls.len(),
unknown_disc,
other_err,
create_v3_seen
);
assert!(decoded_ok > 0, "no instructions decoded");
assert_eq!(
other_err, 0,
"decoder produced non-disc errors on real data"
);
}