diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 864e7964..9f138223 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -42,6 +42,12 @@ jobs: run: cargo login ${{ secrets.CARGO_REGISTRY_TOKEN }} # Publish in dependency order with waits for crates.io index propagation + - name: Publish hyperstack-idl + run: | + cd hyperstack-idl + cargo publish --allow-dirty || echo "Package may already be published" + sleep 30 + - name: Publish hyperstack-macros run: | cd hyperstack-macros diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 62ae299d..06af0be9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -9,5 +9,6 @@ "typescript/core": "0.5.3", "stacks/sdk/typescript": "0.5.3", "stacks/sdk/rust": "0.5.3", - "packages/hyperstack": "0.5.3" + "packages/hyperstack": "0.5.3", + "hyperstack-idl": "0.1.0" } diff --git a/Cargo.lock b/Cargo.lock index a36ed444..486c66da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1276,6 +1276,7 @@ dependencies = [ "dialoguer", "dirs", "flate2", + "hyperstack-idl", "hyperstack-interpreter", "indicatif", "regex", @@ -1288,6 +1289,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "hyperstack-idl" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "sha2", + "strsim", +] + [[package]] name = "hyperstack-interpreter" version = "0.5.3" @@ -1296,6 +1307,7 @@ dependencies = [ "dashmap", "futures", "hex", + "hyperstack-idl", "hyperstack-macros", "lru", "opentelemetry", @@ -1320,6 +1332,7 @@ version = "0.5.3" dependencies = [ "bs58", "hex", + "hyperstack-idl", "proc-macro2", "quote", "serde", diff --git a/Cargo.toml b/Cargo.toml index 3950ba8f..941c862f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "hyperstack", "interpreter", "hyperstack-macros", + "hyperstack-idl", "cli", "rust/hyperstack-server", "rust/hyperstack-sdk", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ea43935d..7d15ee2e 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -32,6 +32,7 @@ chrono = "0.4" indicatif = "0.17" console = "0.15" hyperstack-interpreter = { version = "0.5.3", path = "../interpreter" } +hyperstack-idl = { path = "../hyperstack-idl" } reqwest = { version = "0.11", default-features = false, features = ["json", "blocking", "rustls-tls"] } dirs = "5.0" rpassword = "7.3" diff --git a/cli/src/commands/idl.rs b/cli/src/commands/idl.rs new file mode 100644 index 00000000..052ca6ef --- /dev/null +++ b/cli/src/commands/idl.rs @@ -0,0 +1,1510 @@ +use std::path::{Path, PathBuf}; + +use anyhow::Result; +use clap::{Parser, Subcommand}; +use colored::Colorize; +use serde::Serialize; + +use hyperstack_idl::analysis::{ + build_account_index, classify_accounts, extract_pda_graph, extract_type_graph, + find_account_usage, find_connections, find_links, AccountCategory, SeedKind, +}; +use hyperstack_idl::discriminator::compute_discriminator; +use hyperstack_idl::parse::parse_idl_file; +use hyperstack_idl::search::{search_idl, suggest_similar, IdlSection, MatchType, SearchResult}; +use hyperstack_idl::types::{ + IdlAccount, IdlField, IdlInstruction, IdlSpec, IdlType, IdlTypeArrayElement, IdlTypeDef, + IdlTypeDefKind, IdlTypeDefinedInner, +}; + +/// Load and parse an IDL file, returning a clear error on failure. +fn load_idl(path: &Path) -> Result { + parse_idl_file(path) + .map_err(|e| anyhow::anyhow!("Failed to load IDL file '{}': {}", path.display(), e)) +} + +/// Print a value as pretty-printed JSON. +#[allow(dead_code)] +fn print_json(val: &T) -> Result<()> { + println!("{}", serde_json::to_string_pretty(val)?); + Ok(()) +} + +fn format_discriminator(bytes: &[u8]) -> String { + let hex = bytes + .iter() + .map(|b| format!("{:02x}", b)) + .collect::>() + .join(", "); + format!("[{}]", hex) +} + +fn instruction_discriminator(ix: &IdlInstruction) -> Vec { + if !ix.discriminator.is_empty() { + return ix.discriminator.clone(); + } + + if let Some(disc) = &ix.discriminant { + let value = disc.value as u8; + return vec![value, 0, 0, 0, 0, 0, 0, 0]; + } + + compute_discriminator("global", &ix.name).to_vec() +} + +fn account_discriminator(account: &IdlAccount) -> Vec { + if !account.discriminator.is_empty() { + return account.discriminator.clone(); + } + + compute_discriminator("account", &account.name).to_vec() +} + +fn format_idl_type(ty: &IdlType) -> String { + match ty { + IdlType::Simple(s) => s.clone(), + IdlType::Array(arr) => { + if arr.array.len() == 2 { + let item = match &arr.array[0] { + IdlTypeArrayElement::Nested(t) => format_idl_type(t), + IdlTypeArrayElement::Type(t) => t.clone(), + IdlTypeArrayElement::Size(n) => n.to_string(), + }; + let size = match &arr.array[1] { + IdlTypeArrayElement::Size(n) => n.to_string(), + IdlTypeArrayElement::Nested(t) => format_idl_type(t), + IdlTypeArrayElement::Type(t) => t.clone(), + }; + format!("[{}; {}]", item, size) + } else { + let parts = arr + .array + .iter() + .map(|el| match el { + IdlTypeArrayElement::Nested(t) => format_idl_type(t), + IdlTypeArrayElement::Type(t) => t.clone(), + IdlTypeArrayElement::Size(n) => n.to_string(), + }) + .collect::>() + .join(", "); + format!("array({})", parts) + } + } + IdlType::Option(o) => format!("Option<{}>", format_idl_type(&o.option)), + IdlType::Vec(v) => format!("Vec<{}>", format_idl_type(&v.vec)), + IdlType::HashMap(m) => format!( + "HashMap<{}, {}>", + format_idl_type(&m.hash_map.0), + format_idl_type(&m.hash_map.1) + ), + IdlType::Defined(d) => match &d.defined { + IdlTypeDefinedInner::Named { name } => name.clone(), + IdlTypeDefinedInner::Simple(name) => name.clone(), + }, + } +} + +fn type_kind_label(kind: &IdlTypeDefKind) -> &'static str { + match kind { + IdlTypeDefKind::Struct { .. } => "Struct", + IdlTypeDefKind::TupleStruct { .. } => "TupleStruct", + IdlTypeDefKind::Enum { .. } => "Enum", + } +} + +fn type_member_count(kind: &IdlTypeDefKind) -> usize { + match kind { + IdlTypeDefKind::Struct { fields, .. } => fields.len(), + IdlTypeDefKind::TupleStruct { fields, .. } => fields.len(), + IdlTypeDefKind::Enum { variants, .. } => variants.len(), + } +} + +fn account_field_count(account: &IdlAccount) -> usize { + account + .type_def + .as_ref() + .map(type_member_count) + .unwrap_or(0) +} + +fn find_instruction<'a>(idl: &'a IdlSpec, name: &str) -> Option<&'a IdlInstruction> { + idl.instructions + .iter() + .find(|ix| ix.name.eq_ignore_ascii_case(name)) +} + +fn find_account<'a>(idl: &'a IdlSpec, name: &str) -> Option<&'a IdlAccount> { + idl.accounts + .iter() + .find(|account| account.name.eq_ignore_ascii_case(name)) +} + +fn find_type<'a>(idl: &'a IdlSpec, name: &str) -> Option<&'a IdlTypeDef> { + idl.types + .iter() + .find(|type_def| type_def.name.eq_ignore_ascii_case(name)) +} + +fn not_found_error(section: &str, name: &str, candidates: &[String]) -> anyhow::Error { + let candidate_refs = candidates.iter().map(String::as_str).collect::>(); + if let Some(suggestion) = suggest_similar(name, &candidate_refs, 3).first() { + anyhow::anyhow!( + "{} '{}' not found, did you mean: {}?", + section, + name, + suggestion.candidate + ) + } else { + anyhow::anyhow!("{} '{}' not found", section, name) + } +} + +fn print_fields(fields: &[IdlField]) { + if fields.is_empty() { + println!(" {}", "(none)".dimmed()); + return; + } + + for field in fields { + println!( + " {:<30} {}", + field.name.green(), + format_idl_type(&field.type_).cyan() + ); + } +} + +#[derive(Serialize)] +struct SearchResultJson { + name: String, + section: String, + match_type: String, +} + +#[derive(Serialize)] +struct AccountRelationJson { + account_name: String, + matched_type: Option, + instruction_count: usize, + category: String, +} + +#[derive(Serialize)] +struct InstructionUsageJson { + instruction_name: String, + writable: bool, + signer: bool, + readonly: bool, + pda: bool, +} + +#[derive(Serialize)] +struct InstructionLinkJson { + instruction_name: String, + account_a_writable: bool, + account_b_writable: bool, +} + +#[derive(Serialize)] +struct PdaSeedInfoJson { + kind: String, + value: String, +} + +#[derive(Serialize)] +struct PdaNodeJson { + account_name: String, + instruction_name: String, + seeds: Vec, +} + +#[derive(Serialize)] +struct PubkeyFieldRefJson { + field_name: String, + likely_target: Option, +} + +#[derive(Serialize)] +struct TypeNodeJson { + type_name: String, + pubkey_fields: Vec, +} + +#[derive(Serialize)] +struct AccountRoleJson { + writable: bool, + signer: bool, + pda: bool, +} + +#[derive(Serialize)] +struct InstructionContextJson { + instruction_name: String, + from_role: AccountRoleJson, + to_role: AccountRoleJson, + all_accounts: Vec, +} + +#[derive(Serialize)] +struct DirectConnectionJson { + from: String, + to: String, + instructions: Vec, +} + +#[derive(Serialize)] +struct TransitiveConnectionJson { + from: String, + intermediary: String, + to: String, + hop1_instruction: String, + hop2_instruction: String, +} + +#[derive(Serialize)] +struct InvalidExistingJson { + account: String, + suggestions: Vec, +} + +#[derive(Serialize)] +struct ConnectionReportJson { + new_account: String, + direct: Vec, + transitive: Vec, + invalid_existing: Vec, +} + +fn format_section(section: &IdlSection) -> String { + match section { + IdlSection::Instruction => "instruction".to_string(), + IdlSection::Account => "account".to_string(), + IdlSection::Type => "type".to_string(), + IdlSection::Error => "error".to_string(), + IdlSection::Event => "event".to_string(), + IdlSection::Constant => "constant".to_string(), + } +} + +fn format_match_type(mt: &MatchType) -> String { + match mt { + MatchType::Exact => "exact".to_string(), + MatchType::CaseInsensitive => "case-insensitive".to_string(), + MatchType::Contains => "contains".to_string(), + MatchType::Fuzzy(d) => format!("fuzzy({})", d), + } +} + +fn format_account_category(category: &AccountCategory) -> &'static str { + match category { + AccountCategory::Entity => "Entity", + AccountCategory::Infrastructure => "Infrastructure", + AccountCategory::Role => "Role", + AccountCategory::Other => "Other", + } +} + +fn format_seed_kind(kind: &SeedKind) -> &'static str { + match kind { + SeedKind::Const => "Const", + SeedKind::Account => "Account", + SeedKind::Arg => "Arg", + } +} + +fn collect_account_names(idl: &IdlSpec) -> Vec { + let mut names = build_account_index(idl).into_keys().collect::>(); + names.sort(); + names +} + +fn resolve_account_name<'a>(name: &str, candidates: &'a [String]) -> Result<&'a str> { + candidates + .iter() + .find(|candidate| candidate.eq_ignore_ascii_case(name)) + .map(String::as_str) + .ok_or_else(|| not_found_error("account", name, candidates)) +} + +fn collect_instruction_usage(idl: &IdlSpec, account_name: &str) -> Vec { + let mut usage = Vec::new(); + for ix in &idl.instructions { + if let Some(account) = ix + .accounts + .iter() + .find(|account| account.name == account_name) + { + usage.push(InstructionUsageJson { + instruction_name: ix.name.clone(), + writable: account.is_mut, + signer: account.is_signer, + readonly: !account.is_mut && !account.is_signer, + pda: account.pda.is_some(), + }); + } + } + usage +} + +fn format_account_role_flags(writable: bool, signer: bool, pda: bool) -> String { + format!( + "writable={}, signer={}, pda={}", + writable.to_string().cyan(), + signer.to_string().cyan(), + pda.to_string().cyan() + ) +} + +#[derive(Parser)] +#[command(about = "Inspect and analyze Anchor/Shank IDL files")] +pub struct IdlArgs { + #[command(subcommand)] + pub command: IdlCommands, +} + +#[derive(Subcommand)] +pub enum IdlCommands { + /// Show a high-level summary of the IDL (instruction count, accounts, types, etc.) + Summary { + /// Path to the IDL JSON file + path: PathBuf, + }, + + /// List all instructions + Instructions { + /// Path to the IDL JSON file + path: PathBuf, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// Show details for a single instruction + Instruction { + /// Path to the IDL JSON file + path: PathBuf, + + /// Instruction name + name: String, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// List all account types + Accounts { + /// Path to the IDL JSON file + path: PathBuf, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// Show details for a single account type + Account { + /// Path to the IDL JSON file + path: PathBuf, + + /// Account name + name: String, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// List all type definitions + Types { + /// Path to the IDL JSON file + path: PathBuf, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// Show details for a single type definition + Type { + /// Path to the IDL JSON file + path: PathBuf, + + /// Type name + name: String, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// List all error codes + Errors { + /// Path to the IDL JSON file + path: PathBuf, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// List all events + Events { + /// Path to the IDL JSON file + path: PathBuf, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// List all constants + Constants { + /// Path to the IDL JSON file + path: PathBuf, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// Fuzzy-search instructions, accounts, types, and errors + Search { + /// Path to the IDL JSON file + path: PathBuf, + + /// Search query + query: String, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// Compute the Anchor discriminator for an instruction or account + Discriminator { + /// Path to the IDL JSON file + path: PathBuf, + + /// Instruction or account name + name: String, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// Show cross-instruction account relationships + Relations { + /// Path to the IDL JSON file + path: PathBuf, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// Show which instructions use a given account + AccountUsage { + /// Path to the IDL JSON file + path: PathBuf, + + /// Account name + name: String, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// Show instructions that link two accounts together + Links { + /// Path to the IDL JSON file + path: PathBuf, + + /// First account name + a: String, + + /// Second account name + b: String, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// Extract PDA seed graph from instructions + PdaGraph { + /// Path to the IDL JSON file + path: PathBuf, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// Extract type-level pubkey reference graph + TypeGraph { + /// Path to the IDL JSON file + path: PathBuf, + + /// Output as JSON + #[arg(long)] + json: bool, + }, + + /// Find how a new account connects to existing accounts + Connect { + /// Path to the IDL JSON file + path: PathBuf, + + /// New account name to connect + new_account: String, + + /// Existing account names (comma-separated) + #[arg(long, value_delimiter = ',')] + existing: Vec, + + /// Output as JSON + #[arg(long)] + json: bool, + + /// Suggest HyperStack integration points + #[arg(long)] + suggest_hs: bool, + }, +} + +pub fn run(args: IdlArgs) -> Result<()> { + match args.command { + IdlCommands::Summary { ref path } => { + let idl = load_idl(path)?; + let format = if idl.address.is_some() { + "modern" + } else { + "legacy" + }; + let address = idl + .address + .as_deref() + .or_else(|| idl.metadata.as_ref().and_then(|m| m.address.as_deref())) + .unwrap_or("-"); + + println!("{}", "IDL Summary".bold()); + println!(" {} {}", "Name:".bold(), idl.get_name().green()); + println!(" {} {}", "Format:".bold(), format.cyan()); + println!(" {} {}", "Address:".bold(), address.yellow()); + println!(" {} {}", "Version:".bold(), idl.get_version().cyan()); + println!(); + println!("{}", "Counts".bold()); + println!(" {:<14} {}", "Instructions".bold(), idl.instructions.len()); + println!(" {:<14} {}", "Accounts".bold(), idl.accounts.len()); + println!(" {:<14} {}", "Types".bold(), idl.types.len()); + println!(" {:<14} {}", "Events".bold(), idl.events.len()); + println!(" {:<14} {}", "Errors".bold(), idl.errors.len()); + println!(" {:<14} {}", "Constants".bold(), idl.constants.len()); + } + IdlCommands::Instructions { ref path, json } => { + let idl = load_idl(path)?; + if json { + return print_json(&idl.instructions); + } + + println!("{}", "Instructions".bold()); + println!( + " {:<32} {:>8} {:>6} {}", + "Name".bold(), + "Accounts".bold(), + "Args".bold(), + "Discriminator".bold() + ); + println!(" {}", "-".repeat(76).dimmed()); + for ix in &idl.instructions { + let disc = format_discriminator(&instruction_discriminator(ix)); + println!( + " {:<32} {:>8} {:>6} {}", + ix.name.green(), + ix.accounts.len().to_string().cyan(), + ix.args.len().to_string().cyan(), + disc.yellow() + ); + } + } + IdlCommands::Instruction { + ref path, + ref name, + json, + } => { + let idl = load_idl(path)?; + let ix = find_instruction(&idl, name).ok_or_else(|| { + let candidates = idl + .instructions + .iter() + .map(|it| it.name.clone()) + .collect::>(); + not_found_error("instruction", name, &candidates) + })?; + + if json { + return print_json(ix); + } + + println!("{} {}", "Instruction:".bold(), ix.name.green().bold()); + println!( + " {} {}", + "Discriminator:".bold(), + format_discriminator(&instruction_discriminator(ix)).yellow() + ); + println!(); + println!("{}", "Accounts".bold()); + if ix.accounts.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + println!( + " {:<26} {:<8} {:<8} {}", + "Name".bold(), + "Writable".bold(), + "Signer".bold(), + "PDA".bold() + ); + println!(" {}", "-".repeat(70).dimmed()); + for account in &ix.accounts { + let pda = if account.pda.is_some() { "yes" } else { "no" }; + println!( + " {:<26} {:<8} {:<8} {}", + account.name.green(), + account.is_mut.to_string().cyan(), + account.is_signer.to_string().cyan(), + pda.yellow() + ); + } + } + + println!(); + println!("{}", "Args".bold()); + print_fields(&ix.args); + } + IdlCommands::Accounts { ref path, json } => { + let idl = load_idl(path)?; + if json { + return print_json(&idl.accounts); + } + + println!("{}", "Accounts".bold()); + println!( + " {:<32} {:>8} {}", + "Name".bold(), + "Fields".bold(), + "Discriminator".bold() + ); + println!(" {}", "-".repeat(70).dimmed()); + for account in &idl.accounts { + println!( + " {:<32} {:>8} {}", + account.name.green(), + account_field_count(account).to_string().cyan(), + format_discriminator(&account_discriminator(account)).yellow() + ); + } + } + IdlCommands::Account { + ref path, + ref name, + json, + } => { + let idl = load_idl(path)?; + let account = find_account(&idl, name).ok_or_else(|| { + let candidates = idl + .accounts + .iter() + .map(|it| it.name.clone()) + .collect::>(); + not_found_error("account", name, &candidates) + })?; + + if json { + return print_json(account); + } + + println!("{} {}", "Account:".bold(), account.name.green().bold()); + println!( + " {} {}", + "Discriminator:".bold(), + format_discriminator(&account_discriminator(account)).yellow() + ); + println!("{}", "Fields".bold()); + match &account.type_def { + Some(IdlTypeDefKind::Struct { fields, .. }) => print_fields(fields), + Some(IdlTypeDefKind::TupleStruct { fields, .. }) => { + if fields.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for (idx, field_type) in fields.iter().enumerate() { + println!( + " {:<30} {}", + format!("field_{}", idx).green(), + format_idl_type(field_type).cyan() + ); + } + } + } + Some(IdlTypeDefKind::Enum { variants, .. }) => { + if variants.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for variant in variants { + println!(" {}", variant.name.green()); + } + } + } + None => println!(" {}", "(no embedded fields in IDL account type)".dimmed()), + } + } + IdlCommands::Types { ref path, json } => { + let idl = load_idl(path)?; + if json { + return print_json(&idl.types); + } + + println!("{}", "Types".bold()); + println!( + " {:<32} {:<12} {}", + "Name".bold(), + "Kind".bold(), + "Fields/Variants".bold() + ); + println!(" {}", "-".repeat(60).dimmed()); + for type_def in &idl.types { + println!( + " {:<32} {:<12} {}", + type_def.name.green(), + type_kind_label(&type_def.type_def).cyan(), + type_member_count(&type_def.type_def).to_string().yellow() + ); + } + } + IdlCommands::Type { + ref path, + ref name, + json, + } => { + let idl = load_idl(path)?; + let type_def = find_type(&idl, name).ok_or_else(|| { + let candidates = idl + .types + .iter() + .map(|it| it.name.clone()) + .collect::>(); + not_found_error("type", name, &candidates) + })?; + + if json { + return print_json(type_def); + } + + println!("{} {}", "Type:".bold(), type_def.name.green().bold()); + println!( + " {} {}", + "Kind:".bold(), + type_kind_label(&type_def.type_def).cyan() + ); + match &type_def.type_def { + IdlTypeDefKind::Struct { fields, .. } => { + println!("{}", "Fields".bold()); + print_fields(fields); + } + IdlTypeDefKind::TupleStruct { fields, .. } => { + println!("{}", "Tuple Fields".bold()); + if fields.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for (idx, field_type) in fields.iter().enumerate() { + println!( + " {:<30} {}", + format!("item_{}", idx).green(), + format_idl_type(field_type).cyan() + ); + } + } + } + IdlTypeDefKind::Enum { variants, .. } => { + println!("{}", "Variants".bold()); + if variants.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for variant in variants { + println!(" {}", variant.name.green()); + } + } + } + } + } + IdlCommands::Errors { ref path, json } => { + let idl = load_idl(path)?; + if json { + return print_json(&idl.errors); + } + + println!("{}", "Errors".bold()); + if idl.errors.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + println!( + " {:<8} {:<32} {}", + "Code".bold(), + "Name".bold(), + "Message".bold() + ); + println!(" {}", "-".repeat(70).dimmed()); + for error in &idl.errors { + let msg = error.msg.as_deref().unwrap_or("-"); + println!( + " {:<8} {:<32} {}", + error.code.to_string().cyan(), + error.name.green(), + msg.yellow() + ); + } + } + } + IdlCommands::Events { ref path, json } => { + let idl = load_idl(path)?; + if json { + return print_json(&idl.events); + } + + println!("{}", "Events".bold()); + if idl.events.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + println!(" {:<32} {}", "Name".bold(), "Discriminator".bold()); + println!(" {}", "-".repeat(60).dimmed()); + for event in &idl.events { + let disc = format_discriminator(&event.get_discriminator()); + println!(" {:<32} {}", event.name.green(), disc.yellow()); + } + } + } + IdlCommands::Constants { ref path, json } => { + let idl = load_idl(path)?; + if json { + return print_json(&idl.constants); + } + + println!("{}", "Constants".bold()); + if idl.constants.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + println!( + " {:<32} {:<16} {}", + "Name".bold(), + "Type".bold(), + "Value".bold() + ); + println!(" {}", "-".repeat(70).dimmed()); + for constant in &idl.constants { + println!( + " {:<32} {:<16} {}", + constant.name.green(), + format_idl_type(&constant.type_).cyan(), + constant.value.yellow() + ); + } + } + } + IdlCommands::Search { + ref path, + ref query, + json, + } => { + let idl = load_idl(path)?; + let results = search_idl(&idl, query); + + if json { + let json_results: Vec = results + .iter() + .map(|r| SearchResultJson { + name: r.name.clone(), + section: format_section(&r.section), + match_type: format_match_type(&r.match_type), + }) + .collect(); + return print_json(&json_results); + } + + if results.is_empty() { + println!(" {} '{}'", "No results found for".dimmed(), query); + } else { + // Group by section + let sections = [ + ("Instructions", IdlSection::Instruction), + ("Accounts", IdlSection::Account), + ("Types", IdlSection::Type), + ("Errors", IdlSection::Error), + ("Events", IdlSection::Event), + ("Constants", IdlSection::Constant), + ]; + for (label, section) in §ions { + let section_results: Vec<&SearchResult> = results + .iter() + .filter(|r| { + std::mem::discriminant(&r.section) == std::mem::discriminant(section) + }) + .collect(); + if !section_results.is_empty() { + println!("{}", label.bold()); + for r in §ion_results { + println!( + " {} {}", + r.name.green(), + format!("({})", format_match_type(&r.match_type)).dimmed() + ); + } + println!(); + } + } + } + } + IdlCommands::Discriminator { + ref path, + ref name, + json, + } => { + let idl = load_idl(path)?; + + #[derive(Serialize)] + struct DiscriminatorResult { + name: String, + namespace: String, + hex: String, + bytes: Vec, + } + + let mut results: Vec = Vec::new(); + + // Check instructions + if let Some(ix) = find_instruction(&idl, name) { + let disc = instruction_discriminator(ix); + results.push(DiscriminatorResult { + name: ix.name.clone(), + namespace: "global".to_string(), + hex: format_discriminator(&disc), + bytes: disc, + }); + } + + // Check accounts + if let Some(acc) = find_account(&idl, name) { + let disc = account_discriminator(acc); + results.push(DiscriminatorResult { + name: acc.name.clone(), + namespace: "account".to_string(), + hex: format_discriminator(&disc), + bytes: disc, + }); + } + + if results.is_empty() { + let mut candidates: Vec = + idl.instructions.iter().map(|ix| ix.name.clone()).collect(); + candidates.extend(idl.accounts.iter().map(|a| a.name.clone())); + return Err(not_found_error("instruction or account", name, &candidates)); + } + + if json { + return print_json(&results); + } + + for r in &results { + println!("{} {}", "Name:".bold(), r.name.green()); + println!("{} {}", "Namespace:".bold(), r.namespace.cyan()); + println!("{} {}", "Discriminator:".bold(), r.hex.yellow()); + println!("{} {:?}", "Bytes:".bold(), r.bytes); + println!(); + } + } + IdlCommands::Relations { ref path, json } => { + let idl = load_idl(path)?; + let mut relations = classify_accounts(&idl); + relations.sort_by(|a, b| a.account_name.cmp(&b.account_name)); + + if json { + let out = relations + .iter() + .map(|relation| AccountRelationJson { + account_name: relation.account_name.clone(), + matched_type: relation.matched_type.clone(), + instruction_count: relation.instruction_count, + category: format_account_category(&relation.category).to_string(), + }) + .collect::>(); + return print_json(&out); + } + + println!("{}", "Account Relations".bold()); + println!( + " {:<32} {:<16} {}", + "Account".bold(), + "Category".bold(), + "Instruction Count".bold() + ); + println!(" {}", "-".repeat(72).dimmed()); + for relation in &relations { + println!( + " {:<32} {:<16} {}", + relation.account_name.green(), + format_account_category(&relation.category).cyan(), + relation.instruction_count.to_string().yellow() + ); + } + } + IdlCommands::AccountUsage { + ref path, + ref name, + json, + } => { + let idl = load_idl(path)?; + let candidates = collect_account_names(&idl); + let account_name = resolve_account_name(name, &candidates)?; + let _usage_summary = find_account_usage(&idl, account_name).ok_or_else(|| { + anyhow::anyhow!( + "Account '{}' exists in index but no usage was found", + account_name + ) + })?; + let usage = collect_instruction_usage(&idl, account_name); + + if json { + return print_json(&usage); + } + + println!( + "{} {}", + "Account Usage:".bold(), + account_name.green().bold() + ); + println!( + " {} {}", + "Total Instructions:".bold(), + usage.len().to_string().cyan() + ); + + let writable = usage + .iter() + .filter(|entry| entry.writable) + .map(|entry| entry.instruction_name.as_str()) + .collect::>(); + let signer = usage + .iter() + .filter(|entry| entry.signer) + .map(|entry| entry.instruction_name.as_str()) + .collect::>(); + let readonly = usage + .iter() + .filter(|entry| entry.readonly) + .map(|entry| entry.instruction_name.as_str()) + .collect::>(); + + println!(); + println!( + "{} ({})", + "Writable".bold(), + writable.len().to_string().cyan() + ); + if writable.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for instruction_name in &writable { + println!(" {}", instruction_name.green()); + } + } + + println!(); + println!("{} ({})", "Signer".bold(), signer.len().to_string().cyan()); + if signer.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for instruction_name in &signer { + println!(" {}", instruction_name.green()); + } + } + + println!(); + println!( + "{} ({})", + "Readonly".bold(), + readonly.len().to_string().cyan() + ); + if readonly.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for instruction_name in &readonly { + println!(" {}", instruction_name.green()); + } + } + } + IdlCommands::Links { + ref path, + ref a, + ref b, + json, + } => { + let idl = load_idl(path)?; + let candidates = collect_account_names(&idl); + let account_a = resolve_account_name(a, &candidates)?; + let account_b = resolve_account_name(b, &candidates)?; + let links = find_links(&idl, account_a, account_b); + + if json { + let out = links + .iter() + .map(|link| InstructionLinkJson { + instruction_name: link.instruction_name.clone(), + account_a_writable: link.account_a_writable, + account_b_writable: link.account_b_writable, + }) + .collect::>(); + return print_json(&out); + } + + println!( + "{} {} {} {}", + "Links between".bold(), + account_a.green().bold(), + "and".bold(), + account_b.green().bold() + ); + if links.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + println!( + " {:<32} {:<14} {}", + "Instruction".bold(), + format!("{} Writable", account_a).bold(), + format!("{} Writable", account_b).bold() + ); + println!(" {}", "-".repeat(72).dimmed()); + for link in &links { + println!( + " {:<32} {:<14} {}", + link.instruction_name.green(), + link.account_a_writable.to_string().cyan(), + link.account_b_writable.to_string().cyan() + ); + } + } + } + IdlCommands::PdaGraph { ref path, json } => { + let idl = load_idl(path)?; + let graph = extract_pda_graph(&idl); + + if json { + let out = graph + .iter() + .map(|node| PdaNodeJson { + account_name: node.account_name.clone(), + instruction_name: node.instruction_name.clone(), + seeds: node + .seeds + .iter() + .map(|seed| PdaSeedInfoJson { + kind: format_seed_kind(&seed.kind).to_string(), + value: seed.value.clone(), + }) + .collect(), + }) + .collect::>(); + return print_json(&out); + } + + println!("{}", "PDA Graph".bold()); + if graph.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for node in &graph { + println!( + " {} {} {}", + node.account_name.green().bold(), + "in".dimmed(), + node.instruction_name.cyan() + ); + if node.seeds.is_empty() { + println!(" {}", "(no seeds)".dimmed()); + } else { + for seed in &node.seeds { + println!( + " - {:<8} {}", + format_seed_kind(&seed.kind).yellow(), + seed.value + ); + } + } + } + } + } + IdlCommands::TypeGraph { ref path, json } => { + let idl = load_idl(path)?; + let graph = extract_type_graph(&idl); + + if json { + let out = graph + .iter() + .map(|node| TypeNodeJson { + type_name: node.type_name.clone(), + pubkey_fields: node + .pubkey_fields + .iter() + .map(|field| PubkeyFieldRefJson { + field_name: field.field_name.clone(), + likely_target: field.likely_target.clone(), + }) + .collect(), + }) + .collect::>(); + return print_json(&out); + } + + println!("{}", "Type Graph".bold()); + if graph.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for node in &graph { + println!(" {}", node.type_name.green().bold()); + for field in &node.pubkey_fields { + let target = field.likely_target.as_deref().unwrap_or("?"); + println!( + " - {:<24} {} {}", + field.field_name.cyan(), + "->".dimmed(), + target.yellow() + ); + } + } + } + } + IdlCommands::Connect { + ref path, + ref new_account, + ref existing, + json, + suggest_hs, + } => { + let idl = load_idl(path)?; + let candidates = collect_account_names(&idl); + let resolved_new = resolve_account_name(new_account, &candidates)?; + + let mut valid_existing = Vec::new(); + let mut invalid_existing: Vec<(String, Vec)> = Vec::new(); + for name in existing { + if let Some(canonical) = candidates + .iter() + .find(|candidate| candidate.eq_ignore_ascii_case(name)) + { + if !valid_existing.iter().any(|entry| entry == canonical) { + valid_existing.push(canonical.clone()); + } + } else { + eprintln!("Warning: account '{}' not found in IDL, skipping", name); + let candidate_refs = candidates.iter().map(String::as_str).collect::>(); + let suggestions = suggest_similar(name, &candidate_refs, 3) + .iter() + .map(|s| s.candidate.clone()) + .collect::>(); + invalid_existing.push((name.clone(), suggestions)); + } + } + + let existing_refs = valid_existing + .iter() + .map(String::as_str) + .collect::>(); + let report = find_connections(&idl, resolved_new, &existing_refs); + + let mut invalid_all = invalid_existing; + for (name, suggestions) in &report.invalid_existing { + if !invalid_all + .iter() + .any(|(existing_name, _)| existing_name == name) + { + invalid_all.push((name.clone(), suggestions.clone())); + } + } + + if json { + let out = ConnectionReportJson { + new_account: report.new_account.clone(), + direct: report + .direct + .iter() + .map(|connection| DirectConnectionJson { + from: connection.from.clone(), + to: connection.to.clone(), + instructions: connection + .instructions + .iter() + .map(|instruction| InstructionContextJson { + instruction_name: instruction.instruction_name.clone(), + from_role: AccountRoleJson { + writable: instruction.from_role.writable, + signer: instruction.from_role.signer, + pda: instruction.from_role.pda, + }, + to_role: AccountRoleJson { + writable: instruction.to_role.writable, + signer: instruction.to_role.signer, + pda: instruction.to_role.pda, + }, + all_accounts: instruction.all_accounts.clone(), + }) + .collect(), + }) + .collect(), + transitive: report + .transitive + .iter() + .map(|connection| TransitiveConnectionJson { + from: connection.from.clone(), + intermediary: connection.intermediary.clone(), + to: connection.to.clone(), + hop1_instruction: connection.hop1_instruction.clone(), + hop2_instruction: connection.hop2_instruction.clone(), + }) + .collect(), + invalid_existing: invalid_all + .iter() + .map(|(account, suggestions)| InvalidExistingJson { + account: account.clone(), + suggestions: suggestions.clone(), + }) + .collect(), + }; + return print_json(&out); + } + + println!( + "{} {}", + "Connect: new account".bold(), + report.new_account.green().bold() + ); + println!( + " {} {}", + "Valid existing inputs:".bold(), + valid_existing.len().to_string().cyan() + ); + if !valid_existing.is_empty() { + println!(" {}", valid_existing.join(", ").green()); + } + + println!(); + println!("{}", "Direct Connections".bold()); + if report.direct.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for connection in &report.direct { + println!(" {} {}", "Existing: ".bold(), connection.to.green().bold()); + for instruction in &connection.instructions { + println!( + " - {} {}", + instruction.instruction_name.cyan(), + format_account_role_flags( + instruction.from_role.writable, + instruction.from_role.signer, + instruction.from_role.pda + ) + ); + } + } + } + + println!(); + println!("{}", "Transitive Connections".bold()); + if report.transitive.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for connection in &report.transitive { + println!( + " {} {} {} {} {}", + connection.from.green(), + format!("--({})-->", connection.hop1_instruction).dimmed(), + connection.intermediary.yellow(), + format!("--({})-->", connection.hop2_instruction).dimmed(), + connection.to.green() + ); + } + } + + if !invalid_all.is_empty() { + println!(); + println!("{}", "Invalid Existing Accounts".bold()); + for (name, suggestions) in &invalid_all { + if suggestions.is_empty() { + println!(" - {} {}", name.red(), "(no suggestions)".dimmed()); + } else { + println!( + " - {} {} {}", + name.red(), + "->".dimmed(), + suggestions.join(", ").yellow() + ); + } + } + } + + if suggest_hs { + let mut register_from = Vec::new(); + let mut aggregate = Vec::new(); + + for connection in &report.direct { + for instruction in &connection.instructions { + if instruction.from_role.writable + && !register_from + .iter() + .any(|name: &String| name == &instruction.instruction_name) + { + register_from.push(instruction.instruction_name.clone()); + } + if instruction.from_role.signer + && !aggregate + .iter() + .any(|name: &String| name == &instruction.instruction_name) + { + aggregate.push(instruction.instruction_name.clone()); + } + } + } + + register_from.sort(); + aggregate.sort(); + + println!(); + println!("{}", "HyperStack Suggestions".bold()); + if register_from.is_empty() && aggregate.is_empty() { + println!(" {}", "(none)".dimmed()); + } else { + for instruction_name in ®ister_from { + println!( + " {} {}", + "register_from:".green().bold(), + instruction_name.cyan() + ); + } + for instruction_name in &aggregate { + println!( + " {} {}", + "aggregate:".green().bold(), + instruction_name.cyan() + ); + } + } + println!( + " {}", + "These are HyperStack-specific integration suggestions. Use `--suggest-hs` to see them.".dimmed() + ); + } + } + } + + Ok(()) +} diff --git a/cli/src/commands/mod.rs b/cli/src/commands/mod.rs index 10417561..b855471f 100644 --- a/cli/src/commands/mod.rs +++ b/cli/src/commands/mod.rs @@ -3,6 +3,7 @@ pub mod build; pub mod config; pub mod create; pub mod explore; +pub mod idl; pub mod sdk; pub mod stack; pub mod status; diff --git a/cli/src/main.rs b/cli/src/main.rs index 10b5e034..28594bf4 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -143,6 +143,9 @@ enum Commands { /// Manage anonymous usage telemetry #[command(subcommand)] Telemetry(TelemetryCommands), + + /// Inspect and analyze Anchor/Shank IDL files + Idl(commands::idl::IdlArgs), } #[derive(Subcommand)] @@ -414,6 +417,7 @@ fn command_name(cmd: &Commands) -> &'static str { Commands::Stack(_) => "stack", Commands::Build(_) => "build", Commands::Telemetry(_) => "telemetry", + Commands::Idl(_) => "idl", } } @@ -535,6 +539,7 @@ fn run(cli: Cli) -> anyhow::Result<()> { json, } => commands::build::status(build_id, watch, json || cli.json), }, + Commands::Idl(args) => commands::idl::run(args), Commands::Telemetry(telemetry_cmd) => match telemetry_cmd { TelemetryCommands::Status => commands::telemetry::status(), TelemetryCommands::Enable => commands::telemetry::enable(), diff --git a/docs/src/content/docs/building-stacks/finding-idls.mdx b/docs/src/content/docs/building-stacks/finding-idls.mdx index c8dfb444..bc624f04 100644 --- a/docs/src/content/docs/building-stacks/finding-idls.mdx +++ b/docs/src/content/docs/building-stacks/finding-idls.mdx @@ -78,3 +78,25 @@ pub struct MyStack; | ORE | `oreo7nRnU86QCen6Np3iH6q8C6c6K6c6K6c6K6c6K6c` | [GitHub](https://github.com/regolith-labs/ore) | | System Program | `11111111111111111111111111111111` | Built-in | | Token Program | `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` | Built-in | + +--- + +## Exploring IDLs with the CLI + +Once you have an IDL file, use `hs idl` to explore it before writing your stack: + +```bash +# Get a quick overview +hs idl summary idl/program.json + +# Browse all instructions +hs idl instructions idl/program.json + +# Find how accounts relate to each other +hs idl relations idl/program.json + +# Search for anything +hs idl search idl/program.json +``` + +See the [hs idl reference](/cli/idl/) for the full command list. diff --git a/docs/src/content/docs/cli/idl.mdx b/docs/src/content/docs/cli/idl.mdx new file mode 100644 index 00000000..3250df41 --- /dev/null +++ b/docs/src/content/docs/cli/idl.mdx @@ -0,0 +1,380 @@ +--- +title: hs idl — IDL Explorer +sidebar: + order: 2 +--- + +Explore and analyze Solana IDL (Interface Definition Language) files directly from the command line. + +The `hs idl` suite helps you understand a program's structure, account layouts, and relationships before you start building your stack. + +--- + +## Global Behavior + +These rules apply to all `hs idl` subcommands: + +- **Path argument**: Every subcommand takes `` as its first positional argument (the path to the IDL JSON file). +- **JSON output**: Most commands support the `--json` flag for machine-readable output. +- **Fuzzy matching**: Lookups for instructions, accounts, and types are case-insensitive. If you make a typo, the CLI suggests the closest match. +- **Error handling**: File-not-found or invalid names exit with code 1 and a clear message. + +--- + +## Quick Reference + +| Command | Description | +| ---------------------------------------- | ---------------------------------------- | +| [`summary`](#hs-idl-summary) | Quick overview of the IDL structure | +| [`instructions`](#hs-idl-instructions) | List all instructions | +| [`instruction`](#hs-idl-instruction) | Detail view of a specific instruction | +| [`accounts`](#hs-idl-accounts) | List all account types | +| [`account`](#hs-idl-account) | Detail view of a specific account | +| [`types`](#hs-idl-types) | List all custom types | +| [`type`](#hs-idl-type) | Detail view of a specific custom type | +| [`errors`](#hs-idl-errors) | List all program errors | +| [`events`](#hs-idl-events) | List all program events | +| [`constants`](#hs-idl-constants) | List all defined constants | +| [`search`](#hs-idl-search) | Fuzzy search across the entire IDL | +| [`discriminator`](#hs-idl-discriminator) | Compute Anchor discriminators | +| [`relations`](#hs-idl-relations) | Categorize accounts by their role | +| [`account-usage`](#hs-idl-account-usage) | Find all instructions using an account | +| [`links`](#hs-idl-links) | Find instructions linking two accounts | +| [`pda-graph`](#hs-idl-pda-graph) | Visualize PDA derivation paths | +| [`type-graph`](#hs-idl-type-graph) | Visualize field-to-account relationships | +| [`connect`](#hs-idl-connect) | Analyze how to connect new accounts | + +--- + +## Data Commands + +### hs idl summary + +Show a high-level overview of the program. + +```bash +# Get a quick summary +hs idl summary meteora_dlmm.json +# Output: Name: meteora_dlmm, Format: modern, Instructions: 74, Constants: 30 +``` + +Shows program name, IDL format (modern or legacy), program address, version, and counts for all sections. + +### hs idl instructions + +List all instructions defined in the IDL. + +```bash +# List all instructions +hs idl instructions ore.json + +# Count instructions via JSON +hs idl instructions ore.json --json | jq length +# Output: 19 +``` + +**Options:** + +| Flag | Description | +| -------- | --------------------------------- | +| `--json` | Output as JSON (machine-readable) | + +### hs idl instruction + +Show detailed information about a specific instruction, including its discriminator, accounts, and arguments. + +```bash +# Detail view for an instruction +hs idl instruction ore.json deposit + +# Fuzzy matching on typos +hs idl instruction ore.json depositt +# Error: instruction 'depositt' not found, did you mean: deposit? +``` + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl accounts + +List all account structures defined in the IDL. + +```bash +# List all accounts +hs idl accounts meteora_dlmm.json +``` + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl account + +Show the detailed field layout of a specific account type. + +```bash +# View account fields and types +hs idl account meteora_dlmm.json lb_pair +``` + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl types + +List all custom types and enums. + +```bash +# List all custom types +hs idl types ore.json +``` + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl type + +Show the full definition of a custom type or enum. + +```bash +# View type details +hs idl type ore.json Config +``` + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl errors + +List all custom program errors with their codes and messages. + +```bash +# List all errors +hs idl errors meteora_dlmm.json +``` + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl events + +List all events emitted by the program. + +```bash +# List all events +hs idl events meteora_dlmm.json +``` + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl constants + +List all constants defined in the program. + +```bash +# List all constants +hs idl constants ore.json +``` + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl search + +Perform a fuzzy search across instructions, accounts, types, errors, events, and constants. + +```bash +# Search for anything related to "swap" +hs idl search meteora_dlmm.json swap + +# Use JSON to see where matches were found +hs idl search meteora_dlmm.json swap --json | jq '.[].section' | sort -u +# Output: "error", "event", "instruction", "type" +``` + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl discriminator + +Compute the Anchor discriminator for an instruction or account. + +```bash +# Compute instruction discriminator (global namespace) +hs idl discriminator pump.json buy +# Output: Name: buy, Namespace: global, Discriminator: [66, 06, 3d, 12, 01, da, eb, ea] + +# Compute account discriminator (account namespace) +hs idl discriminator pump.json LastIdlBlock +``` + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +--- + +## Relationship Commands + +### hs idl relations + +Analyze and categorize all accounts in the IDL. + +```bash +# See how accounts are categorized +hs idl relations meteora_dlmm.json + +# Find core entity accounts +hs idl relations meteora_dlmm.json --json | jq '.[] | select(.category == "Entity") | .account_name' +# Output includes: "lb_pair" +``` + +Accounts are classified into: + +- **Entity**: Core data accounts that appear across many instructions. +- **Infrastructure**: System programs or token programs. +- **Role**: Authorities, signers, or administrative accounts. +- **Other**: Miscellaneous accounts. + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl account-usage + +Find every instruction that uses a specific account. + +```bash +# See where 'lb_pair' is used +hs idl account-usage meteora_dlmm.json lb_pair + +# Count usages +hs idl account-usage meteora_dlmm.json lb_pair --json | jq length +# Output: 59 +``` + +Instructions are grouped by the role the account plays: Writable, Signer, or Readonly. + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl links + +Find instructions that involve a specific pair of accounts. This is useful for discovering how two entities interact. + +```bash +# Find instructions linking 'round' and 'entropyVar' +hs idl links ore.json round entropyVar + +# Count shared instructions +hs idl links ore.json round entropyVar --json | jq length +# Output: 2 +``` + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl pda-graph + +Extract and visualize the PDA (Program Derived Address) derivation graph. + +```bash +# Extract PDA graph +hs idl pda-graph idl.json +``` + +Shows seeds (constants, accounts, or arguments) used to derive each PDA account. + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +### hs idl type-graph + +Analyze field types to find implicit references to account types. + +```bash +# Extract type reference graph +hs idl type-graph idl.json +``` + +Useful for identifying when a field named `lb_pair` (type `Pubkey`) refers to an account of type `LbPair`. + +**Options:** + +| Flag | Description | +| -------- | -------------- | +| `--json` | Output as JSON | + +--- + +## Connection Command + +### hs idl connect + +Analyze how a new account can be connected to existing accounts in the program. + +```bash +# Analyze connection between reward_vault and lb_pair +hs idl connect meteora_dlmm.json reward_vault --existing lb_pair + +# With HyperStack-specific suggestions +hs idl connect meteora_dlmm.json reward_vault --existing lb_pair --suggest-hs +# Shows: register_from suggestions + +# Partial success handling +hs idl connect ore.json entropyVar --existing round,bogus_account +# Warning: account 'bogus_account' not found in IDL, skipping +# (then shows connections to round) +``` + +**Options:** + +| Flag | Description | +| -------------------- | ---------------------------------------------------------------------- | +| `--existing ` | Comma-separated list of existing account names | +| `--json` | Output as JSON | +| `--suggest-hs` | Show HyperStack integration suggestions (`register_from`, `aggregate`) | + +:::note[HyperStack Suggestions] +The `--suggest-hs` flag is specifically for HyperStack stack builders. It suggests the most appropriate projection patterns based on the detected relationship. +::: diff --git a/hyperstack-idl/Cargo.toml b/hyperstack-idl/Cargo.toml new file mode 100644 index 00000000..4613453f --- /dev/null +++ b/hyperstack-idl/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "hyperstack-idl" +version = "0.1.0" +edition.workspace = true +license-file = "LICENSE" +repository.workspace = true +authors.workspace = true +description = "IDL parsing and type system for HyperStack" +readme = "README.md" +documentation = "https://docs.rs/hyperstack-idl" +keywords = ["hyperstack", "idl", "types", "parsing"] +categories = ["development-tools"] + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +sha2 = "0.10" +strsim = "0.11" diff --git a/hyperstack-idl/src/analysis/connect.rs b/hyperstack-idl/src/analysis/connect.rs new file mode 100644 index 00000000..81ae898e --- /dev/null +++ b/hyperstack-idl/src/analysis/connect.rs @@ -0,0 +1,284 @@ +use crate::analysis::relations::build_account_index; +use crate::search::suggest_similar; +use crate::types::IdlSpec; +use std::collections::HashSet; + +#[derive(Debug, Clone)] +pub struct AccountRole { + pub writable: bool, + pub signer: bool, + pub pda: bool, +} + +#[derive(Debug, Clone)] +pub struct InstructionContext { + pub instruction_name: String, + pub from_role: AccountRole, + pub to_role: AccountRole, + pub all_accounts: Vec, +} + +#[derive(Debug, Clone)] +pub struct DirectConnection { + pub from: String, + pub to: String, + pub instructions: Vec, +} + +#[derive(Debug, Clone)] +pub struct TransitiveConnection { + pub from: String, + pub intermediary: String, + pub to: String, + pub hop1_instruction: String, + pub hop2_instruction: String, +} + +#[derive(Debug, Clone)] +pub struct ConnectionReport { + pub new_account: String, + pub direct: Vec, + pub transitive: Vec, + pub invalid_existing: Vec<(String, Vec)>, +} + +const INFRASTRUCTURE_ACCOUNTS: &[&str] = &[ + "system_program", + "token_program", + "rent", + "event_authority", + "program", + "associated_token_program", + "memo_program", + "token_2022_program", + "clock", + "instructions", + "sysvar_instructions", +]; + +pub fn find_connections(idl: &IdlSpec, new_account: &str, existing: &[&str]) -> ConnectionReport { + let all_account_names: Vec<&str> = idl + .instructions + .iter() + .flat_map(|ix| ix.accounts.iter().map(|a| a.name.as_str())) + .collect::>() + .into_iter() + .collect(); + + let new_account_exists = all_account_names.contains(&new_account); + + let mut invalid_existing = Vec::new(); + let mut valid_existing = Vec::new(); + + for &account in existing { + if all_account_names.contains(&account) { + valid_existing.push(account); + } else { + let suggestions = suggest_similar(account, &all_account_names, 3); + let suggestion_names: Vec = + suggestions.iter().map(|s| s.candidate.clone()).collect(); + invalid_existing.push((account.to_string(), suggestion_names)); + } + } + + if !new_account_exists { + return ConnectionReport { + new_account: new_account.to_string(), + direct: Vec::new(), + transitive: Vec::new(), + invalid_existing, + }; + } + + let mut direct = Vec::new(); + for &existing_account in &valid_existing { + let mut instructions = Vec::new(); + + for instruction in &idl.instructions { + let account_names: Vec<&str> = instruction + .accounts + .iter() + .map(|account| account.name.as_str()) + .collect(); + + if account_names.contains(&new_account) && account_names.contains(&existing_account) { + let from_account = instruction + .accounts + .iter() + .find(|account| account.name == new_account); + let to_account = instruction + .accounts + .iter() + .find(|account| account.name == existing_account); + + if let (Some(from_account), Some(to_account)) = (from_account, to_account) { + instructions.push(InstructionContext { + instruction_name: instruction.name.clone(), + from_role: AccountRole { + writable: from_account.is_mut, + signer: from_account.is_signer, + pda: from_account.pda.is_some(), + }, + to_role: AccountRole { + writable: to_account.is_mut, + signer: to_account.is_signer, + pda: to_account.pda.is_some(), + }, + all_accounts: account_names.iter().map(|name| name.to_string()).collect(), + }); + } + } + } + + if !instructions.is_empty() { + direct.push(DirectConnection { + from: new_account.to_string(), + to: existing_account.to_string(), + instructions, + }); + } + } + + let mut transitive = Vec::new(); + let directly_connected: HashSet<&str> = direct + .iter() + .map(|connection| connection.to.as_str()) + .collect(); + let unconnected: Vec<&str> = valid_existing + .iter() + .filter(|&&account| !directly_connected.contains(account)) + .copied() + .collect(); + + if !unconnected.is_empty() { + let index = build_account_index(idl); + let new_account_instructions: HashSet<&str> = index + .get(new_account) + .map(|usage| { + usage + .instructions + .iter() + .map(|instruction| instruction.name.as_str()) + .collect() + }) + .unwrap_or_default(); + + for &target in &unconnected { + let target_instructions: HashSet<&str> = index + .get(target) + .map(|usage| { + usage + .instructions + .iter() + .map(|instruction| instruction.name.as_str()) + .collect() + }) + .unwrap_or_default(); + + for (intermediary, usage) in &index { + if intermediary == new_account || intermediary == target { + continue; + } + if INFRASTRUCTURE_ACCOUNTS.contains(&intermediary.as_str()) { + continue; + } + + let intermediary_instructions: HashSet<&str> = usage + .instructions + .iter() + .map(|instruction| instruction.name.as_str()) + .collect(); + + let hop1 = new_account_instructions + .iter() + .find(|instruction| intermediary_instructions.contains(**instruction)); + let hop2 = target_instructions + .iter() + .find(|instruction| intermediary_instructions.contains(**instruction)); + + if let (Some(hop1), Some(hop2)) = (hop1, hop2) { + transitive.push(TransitiveConnection { + from: new_account.to_string(), + intermediary: intermediary.clone(), + to: target.to_string(), + hop1_instruction: (*hop1).to_string(), + hop2_instruction: (*hop2).to_string(), + }); + break; + } + } + } + } + + ConnectionReport { + new_account: new_account.to_string(), + direct, + transitive, + invalid_existing, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parse::parse_idl_file; + use std::path::PathBuf; + + fn meteora_fixture() -> IdlSpec { + let path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/meteora_dlmm.json"); + parse_idl_file(&path).expect("should parse meteora_dlmm.json") + } + + fn ore_fixture() -> IdlSpec { + let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ore.json"); + parse_idl_file(&path).expect("should parse ore.json") + } + + #[test] + fn test_connect_reward_vault() { + let idl = meteora_fixture(); + let report = find_connections(&idl, "reward_vault", &["lb_pair", "position"]); + assert!( + !report.direct.is_empty(), + "reward_vault should have direct connections" + ); + + let lb_pair_connection = report + .direct + .iter() + .find(|connection| connection.to == "lb_pair"); + assert!( + lb_pair_connection.is_some(), + "reward_vault should connect to lb_pair" + ); + assert!(!lb_pair_connection + .expect("connection should exist") + .instructions + .is_empty()); + } + + #[test] + fn test_connect_invalid_name() { + let idl = meteora_fixture(); + let report = find_connections(&idl, "lb_pair", &["bogus_account_xyz"]); + assert!( + !report.invalid_existing.is_empty(), + "bogus_account_xyz should be invalid" + ); + + let (name, suggestions) = &report.invalid_existing[0]; + assert_eq!(name, "bogus_account_xyz"); + let _ = suggestions; + } + + #[test] + fn test_connect_ore_entropyvar() { + let idl = ore_fixture(); + let report = find_connections(&idl, "entropyVar", &["round"]); + assert!( + !report.direct.is_empty() || !report.transitive.is_empty(), + "entropyVar should connect to round somehow" + ); + } +} diff --git a/hyperstack-idl/src/analysis/mod.rs b/hyperstack-idl/src/analysis/mod.rs new file mode 100644 index 00000000..2bbfaef6 --- /dev/null +++ b/hyperstack-idl/src/analysis/mod.rs @@ -0,0 +1,11 @@ +//! Analysis utilities + +pub mod connect; +pub mod relations; +pub mod pda_graph; +pub mod type_graph; + +pub use connect::*; +pub use relations::*; +pub use pda_graph::*; +pub use type_graph::*; diff --git a/hyperstack-idl/src/analysis/pda_graph.rs b/hyperstack-idl/src/analysis/pda_graph.rs new file mode 100644 index 00000000..96206875 --- /dev/null +++ b/hyperstack-idl/src/analysis/pda_graph.rs @@ -0,0 +1,188 @@ +//! PDA graph analysis — extracts PDA derivation info from IDL instructions. + +use crate::types::{IdlPdaSeed, IdlSpec}; + +/// Classification of a PDA seed. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SeedKind { + /// Constant byte array seed (often a UTF-8 string like "pool", "lb_pair"). + Const, + /// Reference to another account in the instruction. + Account, + /// Reference to an instruction argument. + Arg, +} + +/// A single seed in a PDA derivation. +#[derive(Debug, Clone)] +pub struct PdaSeedInfo { + pub kind: SeedKind, + /// For `Const`: UTF-8 decoded string or hex representation. + /// For `Account`/`Arg`: the path field (e.g. "lb_pair", "base_mint"). + pub value: String, +} + +/// A PDA node linking an account, its instruction context, and derivation seeds. +#[derive(Debug, Clone)] +pub struct PdaNode { + pub account_name: String, + pub instruction_name: String, + pub seeds: Vec, +} + +/// Extract all PDA derivation nodes from an IDL spec. +/// +/// Iterates every instruction's account list, collecting accounts that have +/// a `pda` field with seeds. Each seed is classified and its value extracted. +pub fn extract_pda_graph(idl: &IdlSpec) -> Vec { + let mut nodes = Vec::new(); + + for ix in &idl.instructions { + for acc in &ix.accounts { + if let Some(pda) = &acc.pda { + let seeds = pda + .seeds + .iter() + .map(extract_seed_info) + .collect(); + + nodes.push(PdaNode { + account_name: acc.name.clone(), + instruction_name: ix.name.clone(), + seeds, + }); + } + } + } + nodes +} + +/// Extract kind and human-readable value from an `IdlPdaSeed`. +fn extract_seed_info(seed: &IdlPdaSeed) -> PdaSeedInfo { + match seed { + IdlPdaSeed::Const { value } => { + // Try to decode byte array as UTF-8; fall back to hex representation + let decoded = String::from_utf8(value.clone()).unwrap_or_else(|_| hex_encode(value)); + PdaSeedInfo { + kind: SeedKind::Const, + value: decoded, + } + } + IdlPdaSeed::Account { path, .. } => PdaSeedInfo { + kind: SeedKind::Account, + value: path.clone(), + }, + IdlPdaSeed::Arg { path, .. } => PdaSeedInfo { + kind: SeedKind::Arg, + value: path.clone(), + }, + } +} + +/// Simple hex encoding for non-UTF-8 byte arrays. +fn hex_encode(bytes: &[u8]) -> String { + bytes.iter().map(|b| format!("{:02x}", b)).collect() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parse::parse_idl_file; + use std::path::PathBuf; + + #[test] + fn test_pda_graph() { + let path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/meteora_dlmm.json"); + let idl = parse_idl_file(&path).expect("should parse"); + let graph = extract_pda_graph(&idl); + + // meteora_dlmm has many PDA accounts + assert!( + !graph.is_empty(), + "should extract PDA nodes from meteora_dlmm" + ); + + // Check that at least some nodes have seeds + let with_seeds = graph.iter().filter(|n| !n.seeds.is_empty()).count(); + assert!(with_seeds > 0, "some PDA nodes should have seeds"); + + // Verify seed kinds are present + let has_account_seed = graph + .iter() + .flat_map(|n| &n.seeds) + .any(|s| s.kind == SeedKind::Account); + let has_arg_seed = graph + .iter() + .flat_map(|n| &n.seeds) + .any(|s| s.kind == SeedKind::Arg); + let has_const_seed = graph + .iter() + .flat_map(|n| &n.seeds) + .any(|s| s.kind == SeedKind::Const); + + assert!(has_account_seed, "should have Account seeds"); + assert!(has_arg_seed, "should have Arg seeds"); + assert!(has_const_seed, "should have Const seeds"); + + // Const seeds should decode to readable strings (e.g. "oracle", "preset_parameter") + let const_seeds: Vec<&str> = graph + .iter() + .flat_map(|n| &n.seeds) + .filter(|s| s.kind == SeedKind::Const) + .map(|s| s.value.as_str()) + .collect(); + assert!( + const_seeds + .iter() + .any(|s| s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')), + "at least one const seed should be a readable ASCII string, got: {:?}", + const_seeds + ); + + // Print summary for evidence + println!("PDA graph nodes: {}", graph.len()); + println!("Nodes with seeds: {}", with_seeds); + println!( + "Account seeds: {}", + graph + .iter() + .flat_map(|n| &n.seeds) + .filter(|s| s.kind == SeedKind::Account) + .count() + ); + println!( + "Arg seeds: {}", + graph + .iter() + .flat_map(|n| &n.seeds) + .filter(|s| s.kind == SeedKind::Arg) + .count() + ); + println!( + "Const seeds: {}", + graph + .iter() + .flat_map(|n| &n.seeds) + .filter(|s| s.kind == SeedKind::Const) + .count() + ); + println!( + "Sample const values: {:?}", + &const_seeds[..const_seeds.len().min(10)] + ); + + // Print a few sample nodes + for node in graph.iter().take(5) { + println!( + " {} in {}: {:?}", + node.account_name, + node.instruction_name, + node.seeds + .iter() + .map(|s| format!("{:?}={}", s.kind, s.value)) + .collect::>() + ); + } + } +} diff --git a/hyperstack-idl/src/analysis/relations.rs b/hyperstack-idl/src/analysis/relations.rs new file mode 100644 index 00000000..5880e41a --- /dev/null +++ b/hyperstack-idl/src/analysis/relations.rs @@ -0,0 +1,229 @@ +use crate::types::IdlSpec; +use crate::utils::to_pascal_case; +use std::collections::HashMap; + +#[derive(Debug, Clone)] +pub struct InstructionRef { + pub name: String, + pub account_count: usize, + pub arg_count: usize, +} + +#[derive(Debug, Clone)] +pub struct AccountUsage { + pub account_name: String, + pub instructions: Vec, + pub is_writable: bool, + pub is_signer: bool, + pub is_pda: bool, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum AccountCategory { + Entity, + Infrastructure, + Role, + Other, +} + +#[derive(Debug, Clone)] +pub struct AccountRelation { + pub account_name: String, + pub matched_type: Option, + pub instruction_count: usize, + pub category: AccountCategory, +} + +#[derive(Debug, Clone)] +pub struct InstructionLink { + pub instruction_name: String, + pub account_a_writable: bool, + pub account_b_writable: bool, +} + +const INFRASTRUCTURE_ACCOUNTS: &[&str] = &[ + "system_program", + "token_program", + "rent", + "event_authority", + "program", + "associated_token_program", + "memo_program", + "token_2022_program", + "clock", + "instructions", + "sysvar_instructions", +]; + +const ROLE_ACCOUNTS: &[&str] = &[ + "authority", + "owner", + "payer", + "signer", + "admin", + "user", + "sender", + "receiver", +]; + +pub fn build_account_index(idl: &IdlSpec) -> HashMap { + let mut index: HashMap = HashMap::new(); + + for ix in &idl.instructions { + let ix_ref = InstructionRef { + name: ix.name.clone(), + account_count: ix.accounts.len(), + arg_count: ix.args.len(), + }; + for acc in &ix.accounts { + let entry = index + .entry(acc.name.clone()) + .or_insert_with(|| AccountUsage { + account_name: acc.name.clone(), + instructions: Vec::new(), + is_writable: false, + is_signer: false, + is_pda: false, + }); + entry.instructions.push(ix_ref.clone()); + if acc.is_mut { + entry.is_writable = true; + } + if acc.is_signer { + entry.is_signer = true; + } + if acc.pda.is_some() { + entry.is_pda = true; + } + } + } + index +} + +pub fn classify_accounts(idl: &IdlSpec) -> Vec { + let index = build_account_index(idl); + let type_names: Vec = idl.accounts.iter().map(|a| a.name.clone()).collect(); + + index + .into_values() + .map(|usage| { + let pascal = to_pascal_case(&usage.account_name); + let matched_type = type_names + .iter() + .find(|t| **t == pascal || **t == usage.account_name) + .cloned(); + + let category = if INFRASTRUCTURE_ACCOUNTS.contains(&usage.account_name.as_str()) { + AccountCategory::Infrastructure + } else if matched_type.is_some() { + AccountCategory::Entity + } else if ROLE_ACCOUNTS.iter().any(|r| usage.account_name.contains(r)) + || usage.is_signer + { + AccountCategory::Role + } else { + AccountCategory::Other + }; + + AccountRelation { + account_name: usage.account_name.clone(), + matched_type, + instruction_count: usage.instructions.len(), + category, + } + }) + .collect() +} + +pub fn find_account_usage(idl: &IdlSpec, account_name: &str) -> Option { + let index = build_account_index(idl); + index.into_values().find(|u| u.account_name == account_name) +} + +pub fn find_links(idl: &IdlSpec, account_a: &str, account_b: &str) -> Vec { + idl.instructions + .iter() + .filter(|ix| { + let names: Vec<&str> = ix.accounts.iter().map(|a| a.name.as_str()).collect(); + names.contains(&account_a) && names.contains(&account_b) + }) + .map(|ix| { + let a_writable = ix + .accounts + .iter() + .find(|a| a.name == account_a) + .map(|a| a.is_mut) + .unwrap_or(false); + let b_writable = ix + .accounts + .iter() + .find(|a| a.name == account_b) + .map(|a| a.is_mut) + .unwrap_or(false); + InstructionLink { + instruction_name: ix.name.clone(), + account_a_writable: a_writable, + account_b_writable: b_writable, + } + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parse::parse_idl_file; + use std::path::PathBuf; + + fn meteora_fixture() -> IdlSpec { + let path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/meteora_dlmm.json"); + parse_idl_file(&path).expect("should parse meteora_dlmm.json") + } + + #[test] + fn test_classify_accounts_dlmm() { + let idl = meteora_fixture(); + let relations = classify_accounts(&idl); + let lb_pair = relations.iter().find(|r| r.account_name == "lb_pair"); + assert!(lb_pair.is_some(), "lb_pair should be in relations"); + assert_eq!( + lb_pair.expect("lb_pair relation should exist").category, + AccountCategory::Entity, + "lb_pair should be Entity" + ); + + let sys = relations + .iter() + .find(|r| r.account_name == "system_program"); + if let Some(sys) = sys { + assert_eq!(sys.category, AccountCategory::Infrastructure); + } + } + + #[test] + fn test_find_links() { + let idl = meteora_fixture(); + let links = find_links(&idl, "lb_pair", "position"); + assert!( + !links.is_empty(), + "lb_pair and position should share instructions" + ); + } + + #[test] + fn test_build_account_index() { + let idl = meteora_fixture(); + let index = build_account_index(&idl); + let lb_pair = index.get("lb_pair"); + assert!(lb_pair.is_some(), "lb_pair should be in index"); + assert!( + lb_pair + .expect("lb_pair account usage should exist") + .instructions + .len() + > 10, + "lb_pair should appear in many instructions" + ); + } +} diff --git a/hyperstack-idl/src/analysis/type_graph.rs b/hyperstack-idl/src/analysis/type_graph.rs new file mode 100644 index 00000000..e7192be8 --- /dev/null +++ b/hyperstack-idl/src/analysis/type_graph.rs @@ -0,0 +1,220 @@ +//! Type graph analysis — extracts pubkey field references from IDL type definitions. + +use crate::types::{IdlSpec, IdlType, IdlTypeDefKind}; +use crate::utils::to_pascal_case; + +/// A reference to a pubkey field within a type definition. +#[derive(Debug, Clone)] +pub struct PubkeyFieldRef { + /// The field name (e.g. "lb_pair", "owner"). + pub field_name: String, + /// Inferred target account type, matched by converting the field name + /// (or field name stripped of `_id`/`_key` suffix) to PascalCase and + /// comparing against account type names. + pub likely_target: Option, +} + +/// A node in the type graph representing a type that contains pubkey fields. +#[derive(Debug, Clone)] +pub struct TypeNode { + /// The type name (e.g. "Position", "LbPair"). + pub type_name: String, + /// All pubkey fields found in this type. + pub pubkey_fields: Vec, +} + +/// Extract a type graph from an IDL spec. +/// +/// Scans all type definitions (`idl.types`) for struct fields of type `pubkey`. +/// For each pubkey field, attempts to infer the target account type by matching +/// the field name (or field name with `_id`/`_key` suffix stripped) against +/// account type names (from `idl.accounts`). +/// +/// Only types with at least one pubkey field are included in the result. +pub fn extract_type_graph(idl: &IdlSpec) -> Vec { + let account_names: Vec<&str> = idl.accounts.iter().map(|a| a.name.as_str()).collect(); + + let mut nodes = Vec::new(); + + for type_def in &idl.types { + let fields = match &type_def.type_def { + IdlTypeDefKind::Struct { fields, .. } => fields, + _ => continue, + }; + + let pubkey_fields: Vec = fields + .iter() + .filter(|f| is_pubkey_type(&f.type_)) + .map(|f| { + let likely_target = infer_target(&f.name, &account_names); + PubkeyFieldRef { + field_name: f.name.clone(), + likely_target, + } + }) + .collect(); + + if !pubkey_fields.is_empty() { + nodes.push(TypeNode { + type_name: type_def.name.clone(), + pubkey_fields, + }); + } + } + + nodes +} + +/// Check if an `IdlType` represents a public key. +fn is_pubkey_type(ty: &IdlType) -> bool { + matches!(ty, IdlType::Simple(s) if s == "pubkey" || s == "publicKey") +} + +/// Attempt to match a field name to an account type name. +/// +/// Strategy: convert the field name (and variants with `_id`/`_key` stripped) +/// to PascalCase and compare against known account type names (case-insensitive). +fn infer_target(field_name: &str, account_names: &[&str]) -> Option { + let candidates = stripped_candidates(field_name); + + for candidate in &candidates { + let pascal = to_pascal_case(candidate); + for &acct in account_names { + if acct.eq_ignore_ascii_case(&pascal) { + return Some(acct.to_string()); + } + } + } + + None +} + +/// Generate candidate base names by stripping common suffixes. +fn stripped_candidates(field_name: &str) -> Vec<&str> { + let mut candidates = vec![field_name]; + + for suffix in &["_id", "_key"] { + if let Some(stripped) = field_name.strip_suffix(suffix) { + if !stripped.is_empty() { + candidates.push(stripped); + } + } + } + + candidates +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parse::parse_idl_file; + use std::path::PathBuf; + + fn meteora_fixture() -> IdlSpec { + let path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/meteora_dlmm.json"); + parse_idl_file(&path).expect("should parse meteora_dlmm.json") + } + + #[test] + fn test_type_graph() { + let idl = meteora_fixture(); + let graph = extract_type_graph(&idl); + + // Should have at least one TypeNode + assert!( + !graph.is_empty(), + "should extract type nodes with pubkey fields" + ); + + // Find the Position type + let position = graph.iter().find(|n| n.type_name == "Position"); + assert!( + position.is_some(), + "Position type should be in the type graph" + ); + let position = position.unwrap(); + + // Position should have lb_pair as a pubkey field + let lb_pair_field = position + .pubkey_fields + .iter() + .find(|f| f.field_name == "lb_pair"); + assert!( + lb_pair_field.is_some(), + "Position should have lb_pair pubkey field" + ); + assert_eq!( + lb_pair_field.unwrap().likely_target.as_deref(), + Some("LbPair"), + "lb_pair should resolve to LbPair account type" + ); + + // Position should also have owner as a pubkey field + let owner_field = position + .pubkey_fields + .iter() + .find(|f| f.field_name == "owner"); + assert!( + owner_field.is_some(), + "Position should have owner pubkey field" + ); + + // Print summary for evidence + println!("Type graph nodes: {}", graph.len()); + for node in &graph { + println!( + " {} — pubkey fields: {:?}", + node.type_name, + node.pubkey_fields + .iter() + .map(|f| format!( + "{} -> {}", + f.field_name, + f.likely_target.as_deref().unwrap_or("?") + )) + .collect::>() + ); + } + } + + #[test] + fn test_is_pubkey_type() { + assert!(is_pubkey_type(&IdlType::Simple("pubkey".to_string()))); + assert!(is_pubkey_type(&IdlType::Simple("publicKey".to_string()))); + assert!(!is_pubkey_type(&IdlType::Simple("u64".to_string()))); + assert!(!is_pubkey_type(&IdlType::Simple("bool".to_string()))); + } + + #[test] + fn test_stripped_candidates() { + assert_eq!(stripped_candidates("lb_pair"), vec!["lb_pair"]); + assert_eq!(stripped_candidates("pool_id"), vec!["pool_id", "pool"]); + assert_eq!(stripped_candidates("mint_key"), vec!["mint_key", "mint"]); + assert_eq!(stripped_candidates("_id"), vec!["_id"]); + } + + #[test] + fn test_infer_target() { + let accounts = vec!["LbPair", "Position", "BinArray"]; + + assert_eq!( + infer_target("lb_pair", &accounts), + Some("LbPair".to_string()) + ); + assert_eq!( + infer_target("position", &accounts), + Some("Position".to_string()) + ); + assert_eq!( + infer_target("bin_array_id", &accounts), + Some("BinArray".to_string()), + "should match after stripping _id suffix" + ); + assert_eq!( + infer_target("unknown_field", &accounts), + None, + "should return None for non-matching fields" + ); + } +} diff --git a/hyperstack-idl/src/discriminator.rs b/hyperstack-idl/src/discriminator.rs new file mode 100644 index 00000000..1fba9bb3 --- /dev/null +++ b/hyperstack-idl/src/discriminator.rs @@ -0,0 +1,57 @@ +//! Discriminator utilities + +use sha2::{Digest, Sha256}; + +pub fn anchor_discriminator(preimage: &str) -> Vec { + let hash = Sha256::digest(preimage.as_bytes()); + hash[..8].to_vec() +} + +/// Compute an Anchor-compatible discriminator for a given namespace and name. +/// +/// The discriminator is the first 8 bytes of SHA256("namespace:name"). +/// This is used to uniquely identify instructions and accounts in Anchor programs. +/// +/// # Arguments +/// * `namespace` - The namespace (e.g., "global" for instructions, "account" for accounts) +/// * `name` - The name in snake_case for instructions or PascalCase for accounts +/// +/// # Returns +/// An 8-byte array containing the discriminator +pub fn compute_discriminator(namespace: &str, name: &str) -> [u8; 8] { + let preimage = format!("{}:{}", namespace, name); + let bytes = anchor_discriminator(&preimage); + let mut result = [0u8; 8]; + result.copy_from_slice(&bytes); + result +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_discriminator_global_initialize() { + // Known Anchor discriminator for "global:initialize" + let disc = compute_discriminator("global", "initialize"); + // Verify it's 8 bytes and non-zero + assert_eq!(disc.len(), 8); + assert!(disc.iter().any(|&b| b != 0)); + } + + #[test] + fn test_discriminator_consistency() { + // Same inputs always produce same output + let disc1 = compute_discriminator("global", "deposit"); + let disc2 = compute_discriminator("global", "deposit"); + assert_eq!(disc1, disc2); + } + + #[test] + fn test_discriminator_different_names() { + // Different names produce different discriminators + let disc1 = compute_discriminator("global", "deposit"); + let disc2 = compute_discriminator("global", "withdraw"); + assert_ne!(disc1, disc2); + } +} diff --git a/hyperstack-idl/src/error.rs b/hyperstack-idl/src/error.rs new file mode 100644 index 00000000..bd515fcc --- /dev/null +++ b/hyperstack-idl/src/error.rs @@ -0,0 +1,49 @@ +//! Error types for IDL search and lookup operations + +use crate::search::Suggestion; + +/// Structured error type for IDL lookup operations. +/// +/// Named `IdlSearchError` to avoid conflict with `types::IdlError` +/// which represents error definitions within an IDL spec. +#[derive(Debug, Clone)] +pub enum IdlSearchError { + NotFound { + input: String, + section: String, + suggestions: Vec, + available: Vec, + }, + ParseError { + path: String, + source: String, + }, + InvalidPath { + path: String, + }, +} + +impl std::fmt::Display for IdlSearchError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + IdlSearchError::NotFound { + input, + section, + suggestions, + .. + } => { + write!(f, "Not found: '{}' in {}", input, section)?; + if !suggestions.is_empty() { + write!(f, ". Did you mean: {}?", suggestions[0].candidate)?; + } + Ok(()) + } + IdlSearchError::ParseError { path, source } => { + write!(f, "Parse error in {}: {}", path, source) + } + IdlSearchError::InvalidPath { path } => write!(f, "Invalid path: {}", path), + } + } +} + +impl std::error::Error for IdlSearchError {} diff --git a/hyperstack-idl/src/lib.rs b/hyperstack-idl/src/lib.rs new file mode 100644 index 00000000..e8b03b06 --- /dev/null +++ b/hyperstack-idl/src/lib.rs @@ -0,0 +1,19 @@ +//! IDL parsing and type system for HyperStack +//! +//! This crate provides types and utilities for parsing and working with +//! HyperStack IDL (Interface Definition Language) specifications. + +pub mod analysis; +pub mod discriminator; +pub mod error; +pub mod parse; +pub mod search; +pub mod snapshot; +pub mod types; +pub mod utils; + +pub use discriminator::*; +pub use error::*; +pub use search::*; +pub use snapshot::*; +pub use types::*; diff --git a/hyperstack-idl/src/parse.rs b/hyperstack-idl/src/parse.rs new file mode 100644 index 00000000..bd6f1dc0 --- /dev/null +++ b/hyperstack-idl/src/parse.rs @@ -0,0 +1,273 @@ +//! IDL parsing utilities + +use crate::types::IdlSpec; +use std::fs; +use std::path::Path; + +pub fn parse_idl_file>(path: P) -> Result { + let content = fs::read_to_string(&path) + .map_err(|e| format!("Failed to read IDL file {:?}: {}", path.as_ref(), e))?; + + parse_idl_content(&content) +} + +pub fn parse_idl_content(content: &str) -> Result { + serde_json::from_str(content).map_err(|e| format!("Failed to parse IDL JSON: {}", e)) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::discriminator::anchor_discriminator; + use sha2::{Digest, Sha256}; + + #[test] + fn test_anchor_discriminator_known_values() { + let disc = anchor_discriminator("global:initialize"); + assert_eq!(disc.len(), 8); + assert_eq!(disc, &Sha256::digest(b"global:initialize")[..8]); + } + + #[test] + fn test_anchor_account_discriminator() { + let disc = anchor_discriminator("account:LendingMarket"); + assert_eq!(disc.len(), 8); + assert_eq!(disc, &Sha256::digest(b"account:LendingMarket")[..8]); + } + + #[test] + fn test_legacy_idl_parses_without_discriminator() { + let json = r#"{ + "address": "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", + "version": "0.3.0", + "name": "raydium_amm", + "instructions": [ + { + "name": "initialize", + "accounts": [ + { "name": "tokenProgram", "isMut": false, "isSigner": false } + ], + "args": [ + { "name": "nonce", "type": "u8" } + ] + } + ], + "accounts": [ + { + "name": "TargetOrders", + "type": { + "kind": "struct", + "fields": [ + { "name": "owner", "type": { "array": ["u64", 4] } } + ] + } + } + ], + "types": [], + "errors": [] + }"#; + let idl = parse_idl_content(json).expect("legacy IDL should parse"); + + assert_eq!(idl.instructions.len(), 1); + assert_eq!(idl.accounts.len(), 1); + assert!(idl.accounts[0].discriminator.is_empty()); + assert!(idl.instructions[0].discriminator.is_empty()); + assert!(idl.instructions[0].discriminant.is_none()); + } + + #[test] + fn test_legacy_instruction_computes_discriminator() { + let json = r#"{ + "name": "raydium_amm", + "instructions": [ + { + "name": "initialize", + "accounts": [], + "args": [] + } + ], + "accounts": [], + "types": [], + "errors": [] + }"#; + let idl = parse_idl_content(json).unwrap(); + let disc = idl.instructions[0].get_discriminator(); + + assert_eq!(disc.len(), 8); + let expected = anchor_discriminator("global:initialize"); + assert_eq!(disc, expected); + } + + #[test] + fn test_legacy_account_computes_discriminator() { + let json = r#"{ + "name": "test", + "instructions": [], + "accounts": [ + { + "name": "LendingMarket", + "type": { "kind": "struct", "fields": [] } + } + ], + "types": [], + "errors": [] + }"#; + let idl = parse_idl_content(json).unwrap(); + let disc = idl.accounts[0].get_discriminator(); + + assert_eq!(disc.len(), 8); + let expected = anchor_discriminator("account:LendingMarket"); + assert_eq!(disc, expected); + } + + #[test] + fn test_explicit_discriminator_not_overridden() { + let json = r#"{ + "name": "test", + "instructions": [ + { + "name": "transfer", + "discriminator": [1, 2, 3, 4, 5, 6, 7, 8], + "accounts": [], + "args": [] + } + ], + "accounts": [ + { + "name": "TokenAccount", + "discriminator": [10, 20, 30, 40, 50, 60, 70, 80] + } + ], + "types": [], + "errors": [] + }"#; + let idl = parse_idl_content(json).unwrap(); + + assert_eq!( + idl.instructions[0].get_discriminator(), + vec![1, 2, 3, 4, 5, 6, 7, 8] + ); + assert_eq!( + idl.accounts[0].get_discriminator(), + vec![10, 20, 30, 40, 50, 60, 70, 80] + ); + } + + #[test] + fn test_steel_discriminant_still_works() { + let json = r#"{ + "name": "test", + "instructions": [ + { + "name": "CreateMetadataAccount", + "accounts": [], + "args": [], + "discriminant": { "type": "u8", "value": 0 } + }, + { + "name": "UpdateMetadataAccount", + "accounts": [], + "args": [], + "discriminant": { "type": "u8", "value": 1 } + } + ], + "accounts": [], + "types": [], + "errors": [] + }"#; + let idl = parse_idl_content(json).unwrap(); + + assert_eq!( + idl.instructions[0].get_discriminator(), + vec![0, 0, 0, 0, 0, 0, 0, 0] + ); + assert_eq!( + idl.instructions[1].get_discriminator(), + vec![1, 0, 0, 0, 0, 0, 0, 0] + ); + } + + #[test] + fn test_legacy_event_computes_discriminator() { + let json = r#"{ + "name": "test", + "instructions": [], + "accounts": [], + "types": [], + "events": [ + { "name": "TransferEvent" } + ], + "errors": [] + }"#; + let idl = parse_idl_content(json).unwrap(); + let disc = idl.events[0].get_discriminator(); + + assert_eq!(disc.len(), 8); + let expected = anchor_discriminator("event:TransferEvent"); + assert_eq!(disc, expected); + } + + #[test] + fn test_is_mut_is_signer_aliases() { + let json = r#"{ + "name": "test", + "instructions": [ + { + "name": "do_thing", + "accounts": [ + { "name": "payer", "isMut": true, "isSigner": true }, + { "name": "dest", "writable": true, "signer": false } + ], + "args": [] + } + ], + "accounts": [], + "types": [], + "errors": [] + }"#; + let idl = parse_idl_content(json).unwrap(); + let accounts = &idl.instructions[0].accounts; + + assert!(accounts[0].is_mut); + assert!(accounts[0].is_signer); + assert!(accounts[1].is_mut); + assert!(!accounts[1].is_signer); + } + + #[test] + fn test_constants() { + let json = r#"{ + "address": "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo", + "metadata": { + "name": "lb_clmm", + "version": "0.11.0", + "spec": "0.1.0", + "description": "Created with Anchor" + }, + "instructions": [], + "accounts": [], + "types": [], + "events": [], + "errors": [], + "constants": [ + { + "name": "BASIS_POINT_MAX", + "type": "i32", + "value": "10000" + }, + { + "name": "MAX_BIN_PER_ARRAY", + "type": "u64", + "value": "70" + } + ] + }"#; + let idl = parse_idl_content(json).expect("IDL with constants should parse"); + + assert_eq!(idl.constants.len(), 2); + assert_eq!(idl.constants[0].name, "BASIS_POINT_MAX"); + assert_eq!(idl.constants[0].value, "10000"); + assert_eq!(idl.constants[1].name, "MAX_BIN_PER_ARRAY"); + assert_eq!(idl.constants[1].value, "70"); + } +} diff --git a/hyperstack-idl/src/search.rs b/hyperstack-idl/src/search.rs new file mode 100644 index 00000000..7459078b --- /dev/null +++ b/hyperstack-idl/src/search.rs @@ -0,0 +1,198 @@ +//! Search utilities for IDL specs with fuzzy matching + +use crate::types::IdlSpec; +use strsim::levenshtein; + +/// A fuzzy match suggestion with candidate name and edit distance. +#[derive(Debug, Clone)] +pub struct Suggestion { + pub candidate: String, + pub distance: usize, +} + +/// Which section of the IDL a search result came from. +#[derive(Debug, Clone)] +pub enum IdlSection { + Instruction, + Account, + Type, + Error, + Event, + Constant, +} + +/// How a search result was matched. +#[derive(Debug, Clone)] +pub enum MatchType { + Exact, + CaseInsensitive, + Contains, + Fuzzy(usize), +} + +/// A single search result from `search_idl`. +#[derive(Debug, Clone)] +pub struct SearchResult { + pub name: String, + pub section: IdlSection, + pub match_type: MatchType, +} + +/// Suggest similar names from a list of candidates using fuzzy matching. +/// +/// Returns candidates sorted by edit distance (closest first). +/// Exact matches are excluded. Case-insensitive matches get distance 0, +/// substring matches get distance 1, and Levenshtein matches use their +/// actual edit distance. +pub fn suggest_similar(name: &str, candidates: &[&str], max_distance: usize) -> Vec { + let name_lower = name.to_lowercase(); + let mut suggestions: Vec = candidates + .iter() + .filter_map(|&candidate| { + // Skip exact matches + if candidate == name { + return None; + } + let candidate_lower = candidate.to_lowercase(); + // Case-insensitive match + if candidate_lower == name_lower { + return Some(Suggestion { + candidate: candidate.to_string(), + distance: 0, + }); + } + // Substring match + if candidate_lower.contains(&name_lower) || name_lower.contains(&candidate_lower) { + return Some(Suggestion { + candidate: candidate.to_string(), + distance: 1, + }); + } + // Levenshtein distance + let dist = levenshtein(name, candidate); + if dist <= max_distance { + Some(Suggestion { + candidate: candidate.to_string(), + distance: dist, + }) + } else { + None + } + }) + .collect(); + suggestions.sort_by_key(|s| s.distance); + suggestions +} + +/// Search across all sections of an IDL spec for names matching the query. +/// +/// Performs case-insensitive substring matching against instruction names, +/// account names, type names, error names, event names, and constant names. +pub fn search_idl(idl: &IdlSpec, query: &str) -> Vec { + let mut results = Vec::new(); + let q = query.to_lowercase(); + + for ix in &idl.instructions { + if ix.name.to_lowercase().contains(&q) { + results.push(SearchResult { + name: ix.name.clone(), + section: IdlSection::Instruction, + match_type: MatchType::Contains, + }); + } + } + for acc in &idl.accounts { + if acc.name.to_lowercase().contains(&q) { + results.push(SearchResult { + name: acc.name.clone(), + section: IdlSection::Account, + match_type: MatchType::Contains, + }); + } + } + for ty in &idl.types { + if ty.name.to_lowercase().contains(&q) { + results.push(SearchResult { + name: ty.name.clone(), + section: IdlSection::Type, + match_type: MatchType::Contains, + }); + } + } + for err in &idl.errors { + if err.name.to_lowercase().contains(&q) { + results.push(SearchResult { + name: err.name.clone(), + section: IdlSection::Error, + match_type: MatchType::Contains, + }); + } + } + for ev in &idl.events { + if ev.name.to_lowercase().contains(&q) { + results.push(SearchResult { + name: ev.name.clone(), + section: IdlSection::Event, + match_type: MatchType::Contains, + }); + } + } + for c in &idl.constants { + if c.name.to_lowercase().contains(&q) { + results.push(SearchResult { + name: c.name.clone(), + section: IdlSection::Constant, + match_type: MatchType::Contains, + }); + } + } + results +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_fuzzy_suggestions() { + let candidates = ["initialize", "close", "deposit"]; + let suggestions = suggest_similar("initlize", &candidates, 3); + assert!(!suggestions.is_empty()); + assert_eq!(suggestions[0].candidate, "initialize"); + } + + #[test] + fn test_fuzzy_case_insensitive() { + let candidates = ["Initialize", "close"]; + let suggestions = suggest_similar("initialize", &candidates, 3); + assert!(!suggestions.is_empty()); + assert_eq!(suggestions[0].candidate, "Initialize"); + assert_eq!(suggestions[0].distance, 0); + } + + #[test] + fn test_fuzzy_no_exact_match() { + let candidates = ["initialize"]; + let suggestions = suggest_similar("initialize", &candidates, 3); + assert!(suggestions.is_empty(), "exact matches should be excluded"); + } + + #[test] + fn test_fuzzy_substring() { + let candidates = ["swap_exact_in", "close"]; + let suggestions = suggest_similar("swap", &candidates, 3); + assert!(!suggestions.is_empty()); + assert_eq!(suggestions[0].candidate, "swap_exact_in"); + } + + #[test] + fn test_search_idl() { + use crate::parse::parse_idl_file; + use std::path::PathBuf; + let path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/meteora_dlmm.json"); + let idl = parse_idl_file(&path).expect("should parse"); + let results = search_idl(&idl, "swap"); + assert!(!results.is_empty(), "should find results for 'swap'"); + } +} diff --git a/hyperstack-idl/src/snapshot.rs b/hyperstack-idl/src/snapshot.rs new file mode 100644 index 00000000..68448d15 --- /dev/null +++ b/hyperstack-idl/src/snapshot.rs @@ -0,0 +1,283 @@ +//! Snapshot type definitions + +use serde::{de::Error, Deserialize, Deserializer, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlSnapshot { + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub program_id: Option, + pub version: String, + pub accounts: Vec, + pub instructions: Vec, + #[serde(default)] + pub types: Vec, + #[serde(default)] + pub events: Vec, + #[serde(default)] + pub errors: Vec, + #[serde(default = "default_discriminant_size")] + pub discriminant_size: usize, +} + +fn default_discriminant_size() -> usize { + 8 +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlAccountSnapshot { + pub name: String, + pub discriminator: Vec, + #[serde(default)] + pub docs: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub serialization: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlInstructionSnapshot { + pub name: String, + pub discriminator: Vec, + #[serde(default)] + pub docs: Vec, + pub accounts: Vec, + pub args: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlInstructionAccountSnapshot { + pub name: String, + #[serde(default)] + pub writable: bool, + #[serde(default)] + pub signer: bool, + #[serde(default)] + pub optional: bool, + #[serde(default)] + pub address: Option, + #[serde(default)] + pub docs: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlFieldSnapshot { + pub name: String, + #[serde(rename = "type")] + pub type_: IdlTypeSnapshot, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum IdlTypeSnapshot { + Simple(String), + Array(IdlArrayTypeSnapshot), + Option(IdlOptionTypeSnapshot), + Vec(IdlVecTypeSnapshot), + HashMap(IdlHashMapTypeSnapshot), + Defined(IdlDefinedTypeSnapshot), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlHashMapTypeSnapshot { + #[serde(rename = "hashMap", deserialize_with = "deserialize_hash_map")] + pub hash_map: (Box, Box), +} + +fn deserialize_hash_map<'de, D>( + deserializer: D, +) -> Result<(Box, Box), D::Error> +where + D: Deserializer<'de>, +{ + let values: Vec = Vec::deserialize(deserializer)?; + if values.len() != 2 { + return Err(D::Error::custom("hashMap must have exactly 2 elements")); + } + let mut iter = values.into_iter(); + Ok(( + Box::new(iter.next().expect("length checked")), + Box::new(iter.next().expect("length checked")), + )) +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlArrayTypeSnapshot { + pub array: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum IdlArrayElementSnapshot { + Type(IdlTypeSnapshot), + TypeName(String), + Size(u32), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlOptionTypeSnapshot { + pub option: Box, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlVecTypeSnapshot { + pub vec: Box, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlDefinedTypeSnapshot { + pub defined: IdlDefinedInnerSnapshot, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum IdlDefinedInnerSnapshot { + Named { name: String }, + Simple(String), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum IdlSerializationSnapshot { + Borsh, + Bytemuck, + #[serde(alias = "bytemuckunsafe")] + BytemuckUnsafe, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlTypeDefSnapshot { + pub name: String, + #[serde(default)] + pub docs: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub serialization: Option, + #[serde(rename = "type")] + pub type_def: IdlTypeDefKindSnapshot, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum IdlTypeDefKindSnapshot { + Struct { + kind: String, + fields: Vec, + }, + TupleStruct { + kind: String, + fields: Vec, + }, + Enum { + kind: String, + variants: Vec, + }, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlEnumVariantSnapshot { + pub name: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct IdlEventSnapshot { + pub name: String, + pub discriminator: Vec, + #[serde(default)] + pub docs: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct IdlErrorSnapshot { + pub code: u32, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub msg: Option, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_snapshot_serde() { + let snapshot = IdlSnapshot { + name: "test_program".to_string(), + program_id: Some("11111111111111111111111111111111".to_string()), + version: "0.1.0".to_string(), + accounts: vec![IdlAccountSnapshot { + name: "ExampleAccount".to_string(), + discriminator: vec![1, 2, 3, 4, 5, 6, 7, 8], + docs: vec!["Example account".to_string()], + serialization: Some(IdlSerializationSnapshot::Borsh), + }], + instructions: vec![IdlInstructionSnapshot { + name: "example_instruction".to_string(), + discriminator: vec![8, 7, 6, 5, 4, 3, 2, 1], + docs: vec!["Example instruction".to_string()], + accounts: vec![IdlInstructionAccountSnapshot { + name: "payer".to_string(), + writable: true, + signer: true, + optional: false, + address: None, + docs: vec![], + }], + args: vec![IdlFieldSnapshot { + name: "amount".to_string(), + type_: IdlTypeSnapshot::HashMap(IdlHashMapTypeSnapshot { + hash_map: ( + Box::new(IdlTypeSnapshot::Simple("u64".to_string())), + Box::new(IdlTypeSnapshot::Simple("string".to_string())), + ), + }), + }], + }], + types: vec![IdlTypeDefSnapshot { + name: "ExampleType".to_string(), + docs: vec![], + serialization: None, + type_def: IdlTypeDefKindSnapshot::Struct { + kind: "struct".to_string(), + fields: vec![IdlFieldSnapshot { + name: "value".to_string(), + type_: IdlTypeSnapshot::Simple("u64".to_string()), + }], + }, + }], + events: vec![IdlEventSnapshot { + name: "ExampleEvent".to_string(), + discriminator: vec![0, 0, 0, 0, 0, 0, 0, 1], + docs: vec![], + }], + errors: vec![IdlErrorSnapshot { + code: 6000, + name: "ExampleError".to_string(), + msg: Some("example".to_string()), + }], + discriminant_size: default_discriminant_size(), + }; + + let serialized = serde_json::to_value(&snapshot).expect("serialize snapshot"); + let deserialized: IdlSnapshot = + serde_json::from_value(serialized.clone()).expect("deserialize snapshot"); + let round_trip = serde_json::to_value(&deserialized).expect("re-serialize snapshot"); + + assert_eq!(serialized, round_trip); + assert_eq!(deserialized.name, "test_program"); + } + + #[test] + fn test_hashmap_compat() { + let json = r#"{"hashMap":["u64","string"]}"#; + let parsed: IdlHashMapTypeSnapshot = + serde_json::from_str(json).expect("deserialize hashMap"); + + assert!(matches!( + parsed.hash_map.0.as_ref(), + IdlTypeSnapshot::Simple(value) if value == "u64" + )); + assert!(matches!( + parsed.hash_map.1.as_ref(), + IdlTypeSnapshot::Simple(value) if value == "string" + )); + } +} diff --git a/hyperstack-idl/src/types.rs b/hyperstack-idl/src/types.rs new file mode 100644 index 00000000..65881346 --- /dev/null +++ b/hyperstack-idl/src/types.rs @@ -0,0 +1,403 @@ +//! Core type definitions for IDL + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlSpec { + #[serde(default)] + pub version: Option, + #[serde(default)] + pub name: Option, + #[serde(default)] + pub address: Option, + pub instructions: Vec, + pub accounts: Vec, + #[serde(default)] + pub types: Vec, + #[serde(default)] + pub events: Vec, + #[serde(default)] + pub errors: Vec, + #[serde(default)] + pub constants: Vec, + pub metadata: Option, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlConstant { + pub name: String, + #[serde(rename = "type")] + pub type_: IdlType, + pub value: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlMetadata { + #[serde(default)] + pub name: Option, + #[serde(default)] + pub version: Option, + #[serde(default)] + pub address: Option, + #[serde(default)] + pub spec: Option, + #[serde(default)] + pub description: Option, + #[serde(default)] + pub origin: Option, +} + +/// Steel-style discriminant format: {"type": "u8", "value": N} +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct SteelDiscriminant { + #[serde(rename = "type")] + pub type_: String, + pub value: u64, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlInstruction { + pub name: String, + /// Anchor-style discriminator: 8-byte array + #[serde(default)] + pub discriminator: Vec, + /// Steel-style discriminant: {"type": "u8", "value": N} + #[serde(default)] + pub discriminant: Option, + #[serde(default)] + pub docs: Vec, + pub accounts: Vec, + pub args: Vec, +} + +impl IdlInstruction { + pub fn get_discriminator(&self) -> Vec { + if !self.discriminator.is_empty() { + return self.discriminator.clone(); + } + + if let Some(disc) = &self.discriminant { + let value = disc.value as u8; + return vec![value, 0, 0, 0, 0, 0, 0, 0]; + } + + crate::discriminator::anchor_discriminator(&format!("global:{}", self.name)) + } +} + +/// PDA definition in Anchor IDL format +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlPda { + pub seeds: Vec, + #[serde(default)] + pub program: Option, +} + +/// PDA seed in Anchor IDL format +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(tag = "kind", rename_all = "lowercase")] +pub enum IdlPdaSeed { + /// Constant byte array seed + Const { value: Vec }, + /// Reference to another account in the instruction + Account { + path: String, + #[serde(default)] + account: Option, + }, + /// Reference to an instruction argument + Arg { + path: String, + #[serde(rename = "type", default)] + arg_type: Option, + }, +} + +/// Program reference for cross-program PDAs +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum IdlPdaProgram { + /// Reference to another account that holds the program ID + Account { kind: String, path: String }, + /// Literal program ID + Literal { kind: String, value: String }, + /// Constant program ID as bytes + Const { kind: String, value: Vec }, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlAccountArg { + pub name: String, + #[serde(rename = "isMut", alias = "writable", default)] + pub is_mut: bool, + #[serde(rename = "isSigner", alias = "signer", default)] + pub is_signer: bool, + #[serde(default)] + pub address: Option, + #[serde(default)] + pub optional: bool, + #[serde(default)] + pub docs: Vec, + #[serde(default)] + pub pda: Option, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlAccount { + pub name: String, + #[serde(default)] + pub discriminator: Vec, + #[serde(default)] + pub docs: Vec, + /// Steel format embedded type definition + #[serde(rename = "type", default)] + pub type_def: Option, +} + +impl IdlAccount { + pub fn get_discriminator(&self) -> Vec { + if !self.discriminator.is_empty() { + return self.discriminator.clone(); + } + + crate::discriminator::anchor_discriminator(&format!("account:{}", self.name)) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlTypeDefStruct { + pub kind: String, + pub fields: Vec, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlField { + pub name: String, + #[serde(rename = "type")] + pub type_: IdlType, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum IdlType { + Simple(String), + Array(IdlTypeArray), + Option(IdlTypeOption), + Vec(IdlTypeVec), + HashMap(IdlTypeHashMap), + Defined(IdlTypeDefined), +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlTypeOption { + pub option: Box, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlTypeVec { + pub vec: Box, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlTypeHashMap { + #[serde(alias = "bTreeMap")] + #[serde(rename = "hashMap")] + pub hash_map: (Box, Box), +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlTypeArray { + pub array: Vec, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum IdlTypeArrayElement { + Nested(IdlType), + Type(String), + Size(u32), +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlTypeDefined { + pub defined: IdlTypeDefinedInner, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum IdlTypeDefinedInner { + Named { name: String }, + Simple(String), +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlRepr { + pub kind: String, +} + +/// Account serialization format as specified in the IDL. +/// Defaults to Borsh when not specified. +#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum IdlSerialization { + #[default] + Borsh, + Bytemuck, + #[serde(alias = "bytemuckunsafe")] + BytemuckUnsafe, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlTypeDef { + pub name: String, + #[serde(default)] + pub docs: Vec, + /// Serialization format: "borsh" (default), "bytemuck", or "bytemuckunsafe" + #[serde(default)] + pub serialization: Option, + /// Repr annotation for zero-copy types (e.g., {"kind": "c"}) + #[serde(default)] + pub repr: Option, + #[serde(rename = "type")] + pub type_def: IdlTypeDefKind, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum IdlTypeDefKind { + Struct { + kind: String, + fields: Vec, + }, + TupleStruct { + kind: String, + fields: Vec, + }, + Enum { + kind: String, + variants: Vec, + }, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlEnumVariant { + pub name: String, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlEvent { + pub name: String, + #[serde(default)] + pub discriminator: Vec, + #[serde(default)] + pub docs: Vec, +} + +impl IdlEvent { + pub fn get_discriminator(&self) -> Vec { + if !self.discriminator.is_empty() { + return self.discriminator.clone(); + } + crate::discriminator::anchor_discriminator(&format!("event:{}", self.name)) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct IdlError { + pub code: u32, + pub name: String, + #[serde(default)] + pub msg: Option, +} + +pub type IdlInstructionAccount = IdlAccountArg; +pub type IdlInstructionArg = IdlField; +pub type IdlTypeDefTy = IdlTypeDefKind; + +impl IdlSpec { + pub fn get_name(&self) -> &str { + self.name + .as_deref() + .or_else(|| self.metadata.as_ref().and_then(|m| m.name.as_deref())) + .unwrap_or("unknown") + } + + pub fn get_version(&self) -> &str { + self.version + .as_deref() + .or_else(|| self.metadata.as_ref().and_then(|m| m.version.as_deref())) + .unwrap_or("0.1.0") + } + + /// Check if a field is an account (vs an arg/data field) for a given instruction + /// Returns Some("accounts") if it's an account, Some("data") if it's an arg, None if not found + pub fn get_instruction_field_prefix( + &self, + instruction_name: &str, + field_name: &str, + ) -> Option<&'static str> { + let normalized_name = to_snake_case(instruction_name); + + for instruction in &self.instructions { + if instruction.name == normalized_name + || instruction.name.eq_ignore_ascii_case(instruction_name) + { + for account in &instruction.accounts { + if account.name == field_name { + return Some("accounts"); + } + } + for arg in &instruction.args { + if arg.name == field_name { + return Some("data"); + } + } + return None; + } + } + None + } + + /// Get the discriminator bytes for an instruction by name + pub fn get_instruction_discriminator(&self, instruction_name: &str) -> Option> { + let normalized_name = to_snake_case(instruction_name); + for instruction in &self.instructions { + if instruction.name == normalized_name { + let disc = instruction.get_discriminator(); + if !disc.is_empty() { + return Some(disc); + } + } + } + None + } +} + +pub fn to_snake_case(s: &str) -> String { + let mut result = String::new(); + + for c in s.chars() { + if c.is_uppercase() { + if !result.is_empty() { + result.push('_'); + } + result.push(c.to_lowercase().next().unwrap()); + } else { + result.push(c); + } + } + + result +} + +pub fn to_pascal_case(s: &str) -> String { + s.split('_') + .map(|word| { + let mut chars = word.chars(); + match chars.next() { + None => String::new(), + Some(f) => f.to_uppercase().collect::() + chars.as_str(), + } + }) + .collect() +} diff --git a/hyperstack-idl/src/utils.rs b/hyperstack-idl/src/utils.rs new file mode 100644 index 00000000..034f23f5 --- /dev/null +++ b/hyperstack-idl/src/utils.rs @@ -0,0 +1,49 @@ +//! Utility functions + +/// Convert PascalCase or camelCase to snake_case +pub fn to_snake_case(s: &str) -> String { + let mut result = String::new(); + + for c in s.chars() { + if c.is_uppercase() { + if !result.is_empty() { + result.push('_'); + } + result.push(c.to_lowercase().next().unwrap()); + } else { + result.push(c); + } + } + + result +} + +/// Convert snake_case to PascalCase +pub fn to_pascal_case(s: &str) -> String { + s.split('_') + .map(|word| { + let mut chars = word.chars(); + match chars.next() { + None => String::new(), + Some(f) => f.to_uppercase().collect::() + chars.as_str(), + } + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_case_conversion() { + assert_eq!(to_snake_case("PoolState"), "pool_state"); + assert_eq!(to_snake_case("LbPair"), "lb_pair"); + assert_eq!(to_snake_case("initialize"), "initialize"); + assert_eq!(to_snake_case("MyIDL"), "my_i_d_l"); + + assert_eq!(to_pascal_case("pool_state"), "PoolState"); + assert_eq!(to_pascal_case("lb_pair"), "LbPair"); + assert_eq!(to_pascal_case("initialize"), "Initialize"); + } +} diff --git a/hyperstack-idl/tests/fixtures/entropy.json b/hyperstack-idl/tests/fixtures/entropy.json new file mode 100644 index 00000000..77ad5542 --- /dev/null +++ b/hyperstack-idl/tests/fixtures/entropy.json @@ -0,0 +1,332 @@ +{ + "version": "0.1.5", + "name": "entropy", + "instructions": [ + { + "name": "open", + "discriminant": { + "type": "u8", + "value": 0 + }, + "docs": [ + "Creates a new entropy var account.", + "Var PDA seeds: [\"var\", authority, id]." + ], + "accounts": [ + { + "name": "authority", + "isMut": true, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "provider", + "isMut": false, + "isSigner": false + }, + { + "name": "var", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "id", + "type": "u64" + }, + { + "name": "commit", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "isAuto", + "type": "u64" + }, + { + "name": "samples", + "type": "u64" + }, + { + "name": "endAt", + "type": "u64" + } + ] + }, + { + "name": "close", + "discriminant": { + "type": "u8", + "value": 1 + }, + "docs": [ + "Closes an entropy var account and returns rent to the authority." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "var", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "next", + "discriminant": { + "type": "u8", + "value": 2 + }, + "docs": [ + "Updates the var for the next random value sample.", + "Resets the commit to the previous seed and clears slot_hash, seed, and value." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "var", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "endAt", + "type": "u64" + } + ] + }, + { + "name": "reveal", + "discriminant": { + "type": "u8", + "value": 4 + }, + "docs": [ + "Reveals the seed and finalizes the random value.", + "The seed must hash to the commit stored in the var account." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "var", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "seed", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + }, + { + "name": "sample", + "discriminant": { + "type": "u8", + "value": 5 + }, + "docs": [ + "Samples the slot hash at the end_at slot.", + "Must be called after the end_at slot has passed." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "var", + "isMut": true, + "isSigner": false + }, + { + "name": "slotHashesSysvar", + "isMut": false, + "isSigner": false, + "address": "SysvarS1otHashes111111111111111111111111111" + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "Var", + "discriminator": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "docs": [ + "An entropy variable account that stores randomness generation state." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "docs": [ + "The creator of the variable." + ], + "type": "publicKey" + }, + { + "name": "id", + "docs": [ + "The id of the variable." + ], + "type": "u64" + }, + { + "name": "provider", + "docs": [ + "The provider of the entropy data." + ], + "type": "publicKey" + }, + { + "name": "commit", + "docs": [ + "The commit provided by the Entropy provider." + ], + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "seed", + "docs": [ + "The revealed seed." + ], + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "slot_hash", + "docs": [ + "The slot hash." + ], + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "value", + "docs": [ + "The current value of the variable." + ], + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "samples", + "docs": [ + "The number of random variables remaining to be sampled." + ], + "type": "u64" + }, + { + "name": "is_auto", + "docs": [ + "Whether or not the Entropy provider should automatically sample the slot hash." + ], + "type": "u64" + }, + { + "name": "start_at", + "docs": [ + "The slot at which the variable was opened." + ], + "type": "u64" + }, + { + "name": "end_at", + "docs": [ + "The slot at which the variable should sample the slothash." + ], + "type": "u64" + } + ] + } + } + ], + "types": [], + "events": [], + "errors": [ + { + "code": 0, + "name": "IncompleteDigest", + "msg": "Incomplete digest" + }, + { + "code": 1, + "name": "InvalidSeed", + "msg": "Invalid seed" + } + ], + "metadata": { + "address": "3jSkUuYBoJzQPMEzTvkDFXCZUBksPamrVhrnHR9igu2X", + "origin": "steel" + } +} diff --git a/hyperstack-idl/tests/fixtures/meteora_dlmm.json b/hyperstack-idl/tests/fixtures/meteora_dlmm.json new file mode 100644 index 00000000..a343f86c --- /dev/null +++ b/hyperstack-idl/tests/fixtures/meteora_dlmm.json @@ -0,0 +1,11127 @@ +{ + "address": "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo", + "metadata": { + "name": "lb_clmm", + "version": "0.11.0", + "spec": "0.1.0", + "description": "Created with Anchor" + }, + "instructions": [ + { + "name": "add_liquidity", + "discriminator": [ + 181, + 157, + 89, + 67, + 143, + 182, + 52, + 72 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_x_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_y_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "liquidity_parameter", + "type": { + "defined": { + "name": "LiquidityParameter" + } + } + } + ] + }, + { + "name": "add_liquidity2", + "discriminator": [ + 228, + 162, + 78, + 28, + 70, + 219, + 116, + 115 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_x_program" + }, + { + "name": "token_y_program" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "liquidity_parameter", + "type": { + "defined": { + "name": "LiquidityParameter" + } + } + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "add_liquidity_by_strategy", + "discriminator": [ + 7, + 3, + 150, + 127, + 148, + 40, + 61, + 200 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_x_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_y_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "liquidity_parameter", + "type": { + "defined": { + "name": "LiquidityParameterByStrategy" + } + } + } + ] + }, + { + "name": "add_liquidity_by_strategy2", + "discriminator": [ + 3, + 221, + 149, + 218, + 111, + 141, + 118, + 213 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_x_program" + }, + { + "name": "token_y_program" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "liquidity_parameter", + "type": { + "defined": { + "name": "LiquidityParameterByStrategy" + } + } + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "add_liquidity_by_strategy_one_side", + "discriminator": [ + 41, + 5, + 238, + 175, + 100, + 225, + 6, + 205 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token", + "writable": true + }, + { + "name": "reserve", + "writable": true + }, + { + "name": "token_mint" + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "liquidity_parameter", + "type": { + "defined": { + "name": "LiquidityParameterByStrategyOneSide" + } + } + } + ] + }, + { + "name": "add_liquidity_by_weight", + "discriminator": [ + 28, + 140, + 238, + 99, + 231, + 162, + 21, + 149 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_x_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_y_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "liquidity_parameter", + "type": { + "defined": { + "name": "LiquidityParameterByWeight" + } + } + } + ] + }, + { + "name": "add_liquidity_one_side", + "discriminator": [ + 94, + 155, + 103, + 151, + 70, + 95, + 220, + 165 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token", + "writable": true + }, + { + "name": "reserve", + "writable": true + }, + { + "name": "token_mint" + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "liquidity_parameter", + "type": { + "defined": { + "name": "LiquidityOneSideParameter" + } + } + } + ] + }, + { + "name": "add_liquidity_one_side_precise", + "discriminator": [ + 161, + 194, + 103, + 84, + 171, + 71, + 250, + 154 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token", + "writable": true + }, + { + "name": "reserve", + "writable": true + }, + { + "name": "token_mint" + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "parameter", + "type": { + "defined": { + "name": "AddLiquiditySingleSidePreciseParameter" + } + } + } + ] + }, + { + "name": "add_liquidity_one_side_precise2", + "discriminator": [ + 33, + 51, + 163, + 201, + 117, + 98, + 125, + 231 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token", + "writable": true + }, + { + "name": "reserve", + "writable": true + }, + { + "name": "token_mint" + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_program" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "liquidity_parameter", + "type": { + "defined": { + "name": "AddLiquiditySingleSidePreciseParameter2" + } + } + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "claim_fee", + "discriminator": [ + 169, + 32, + 79, + 137, + 136, + 232, + 70, + 137 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "position", + "writable": true + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "claim_fee2", + "discriminator": [ + 112, + 191, + 101, + 171, + 28, + 144, + 127, + 187 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position" + ] + }, + { + "name": "position", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_program_x" + }, + { + "name": "token_program_y" + }, + { + "name": "memo_program", + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "min_bin_id", + "type": "i32" + }, + { + "name": "max_bin_id", + "type": "i32" + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "claim_reward", + "discriminator": [ + 149, + 95, + 181, + 242, + 94, + 90, + 158, + 162 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "position", + "writable": true + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "reward_vault", + "writable": true + }, + { + "name": "reward_mint" + }, + { + "name": "user_token_account", + "writable": true + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "reward_index", + "type": "u64" + } + ] + }, + { + "name": "claim_reward2", + "discriminator": [ + 190, + 3, + 127, + 119, + 178, + 87, + 157, + 183 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position" + ] + }, + { + "name": "position", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "reward_vault", + "writable": true + }, + { + "name": "reward_mint" + }, + { + "name": "user_token_account", + "writable": true + }, + { + "name": "token_program" + }, + { + "name": "memo_program", + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "min_bin_id", + "type": "i32" + }, + { + "name": "max_bin_id", + "type": "i32" + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "close_claim_fee_operator_account", + "discriminator": [ + 184, + 213, + 88, + 31, + 179, + 101, + 130, + 36 + ], + "accounts": [ + { + "name": "claim_fee_operator", + "writable": true + }, + { + "name": "rent_receiver", + "writable": true + }, + { + "name": "signer", + "signer": true + } + ], + "args": [] + }, + { + "name": "close_operator_account", + "discriminator": [ + 171, + 9, + 213, + 74, + 120, + 23, + 3, + 29 + ], + "accounts": [ + { + "name": "operator", + "writable": true + }, + { + "name": "signer", + "signer": true + }, + { + "name": "rent_receiver", + "writable": true + } + ], + "args": [] + }, + { + "name": "close_position", + "discriminator": [ + 123, + 134, + 81, + 0, + 49, + 68, + 98, + 98 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "rent_receiver", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "close_position2", + "discriminator": [ + 174, + 90, + 35, + 115, + 186, + 40, + 147, + 226 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "rent_receiver", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "close_position_if_empty", + "discriminator": [ + 59, + 124, + 212, + 118, + 91, + 152, + 110, + 157 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "rent_receiver", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "close_preset_parameter", + "discriminator": [ + 4, + 148, + 145, + 100, + 134, + 26, + 181, + 61 + ], + "accounts": [ + { + "name": "preset_parameter", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + }, + { + "name": "rent_receiver", + "writable": true + } + ], + "args": [] + }, + { + "name": "close_preset_parameter2", + "discriminator": [ + 39, + 25, + 95, + 107, + 116, + 17, + 115, + 28 + ], + "accounts": [ + { + "name": "preset_parameter", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + }, + { + "name": "rent_receiver", + "writable": true + } + ], + "args": [] + }, + { + "name": "close_token_badge", + "discriminator": [ + 108, + 146, + 86, + 110, + 179, + 254, + 10, + 104 + ], + "accounts": [ + { + "name": "token_badge", + "writable": true + }, + { + "name": "rent_receiver", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + } + ], + "args": [] + }, + { + "name": "create_operator_account", + "discriminator": [ + 221, + 64, + 246, + 149, + 240, + 153, + 229, + 163 + ], + "accounts": [ + { + "name": "operator", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 111, + 112, + 101, + 114, + 97, + 116, + 111, + 114 + ] + }, + { + "kind": "account", + "path": "whitelisted_signer" + } + ] + } + }, + { + "name": "whitelisted_signer" + }, + { + "name": "signer", + "signer": true + }, + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "permission", + "type": "u128" + } + ] + }, + { + "name": "decrease_position_length", + "discriminator": [ + 194, + 219, + 136, + 32, + 25, + 96, + 105, + 37 + ], + "accounts": [ + { + "name": "rent_receiver", + "writable": true + }, + { + "name": "position", + "writable": true + }, + { + "name": "owner", + "signer": true, + "relations": [ + "position" + ] + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "length_to_remove", + "type": "u16" + }, + { + "name": "side", + "type": "u8" + } + ] + }, + { + "name": "for_idl_type_generation_do_not_call", + "discriminator": [ + 180, + 105, + 69, + 80, + 95, + 50, + 73, + 108 + ], + "accounts": [ + { + "name": "dummy_zc_account" + } + ], + "args": [ + { + "name": "_ix", + "type": { + "defined": { + "name": "DummyIx" + } + } + } + ] + }, + { + "name": "fund_reward", + "discriminator": [ + 188, + 50, + 249, + 165, + 93, + 151, + 38, + 63 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "bin_array" + ] + }, + { + "name": "reward_vault", + "writable": true + }, + { + "name": "reward_mint" + }, + { + "name": "funder_token_account", + "writable": true + }, + { + "name": "funder", + "signer": true + }, + { + "name": "bin_array", + "writable": true + }, + { + "name": "token_program" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "amount", + "type": "u64" + }, + { + "name": "carry_forward", + "type": "bool" + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "go_to_a_bin", + "discriminator": [ + 146, + 72, + 174, + 224, + 40, + 253, + 84, + 174 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "bin_array_bitmap_extension", + "from_bin_array", + "to_bin_array" + ] + }, + { + "name": "bin_array_bitmap_extension", + "optional": true + }, + { + "name": "from_bin_array", + "optional": true + }, + { + "name": "to_bin_array", + "optional": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "bin_id", + "type": "i32" + } + ] + }, + { + "name": "increase_oracle_length", + "discriminator": [ + 190, + 61, + 125, + 87, + 103, + 79, + 158, + 173 + ], + "accounts": [ + { + "name": "oracle", + "writable": true + }, + { + "name": "funder", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "length_to_add", + "type": "u64" + } + ] + }, + { + "name": "increase_position_length", + "discriminator": [ + 80, + 83, + 117, + 211, + 66, + 13, + 33, + 149 + ], + "accounts": [ + { + "name": "funder", + "writable": true, + "signer": true + }, + { + "name": "lb_pair", + "relations": [ + "position" + ] + }, + { + "name": "position", + "writable": true + }, + { + "name": "owner", + "signer": true, + "relations": [ + "position" + ] + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "length_to_add", + "type": "u16" + }, + { + "name": "side", + "type": "u8" + } + ] + }, + { + "name": "increase_position_length2", + "discriminator": [ + 255, + 210, + 204, + 71, + 115, + 137, + 225, + 113 + ], + "accounts": [ + { + "name": "funder", + "writable": true, + "signer": true + }, + { + "name": "lb_pair", + "relations": [ + "position" + ] + }, + { + "name": "position", + "writable": true + }, + { + "name": "owner", + "signer": true, + "relations": [ + "position" + ] + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "minimum_upper_bin_id", + "type": "i32" + } + ] + }, + { + "name": "initialize_bin_array", + "discriminator": [ + 35, + 86, + 19, + 185, + 78, + 212, + 75, + 211 + ], + "accounts": [ + { + "name": "lb_pair" + }, + { + "name": "bin_array", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 105, + 110, + 95, + 97, + 114, + 114, + 97, + 121 + ] + }, + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "arg", + "path": "index" + } + ] + } + }, + { + "name": "funder", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "index", + "type": "i64" + } + ] + }, + { + "name": "initialize_bin_array_bitmap_extension", + "discriminator": [ + 47, + 157, + 226, + 180, + 12, + 240, + 33, + 71 + ], + "accounts": [ + { + "name": "lb_pair" + }, + { + "name": "bin_array_bitmap_extension", + "docs": [ + "Initialize an account to store if a bin array is initialized." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 105, + 116, + 109, + 97, + 112 + ] + }, + { + "kind": "account", + "path": "lb_pair" + } + ] + } + }, + { + "name": "funder", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "rent" + } + ], + "args": [] + }, + { + "name": "initialize_customizable_permissionless_lb_pair", + "discriminator": [ + 46, + 39, + 41, + 135, + 111, + 183, + 200, + 64 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 105, + 116, + 109, + 97, + 112 + ] + }, + { + "kind": "account", + "path": "lb_pair" + } + ] + } + }, + { + "name": "token_mint_x" + }, + { + "name": "token_mint_y" + }, + { + "name": "reserve_x", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "token_mint_x" + } + ] + } + }, + { + "name": "reserve_y", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "token_mint_y" + } + ] + } + }, + { + "name": "oracle", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 111, + 114, + 97, + 99, + 108, + 101 + ] + }, + { + "kind": "account", + "path": "lb_pair" + } + ] + } + }, + { + "name": "user_token_x" + }, + { + "name": "funder", + "writable": true, + "signer": true + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "user_token_y" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "CustomizableParams" + } + } + } + ] + }, + { + "name": "initialize_customizable_permissionless_lb_pair2", + "discriminator": [ + 243, + 73, + 129, + 126, + 51, + 19, + 241, + 107 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 105, + 116, + 109, + 97, + 112 + ] + }, + { + "kind": "account", + "path": "lb_pair" + } + ] + } + }, + { + "name": "token_mint_x" + }, + { + "name": "token_mint_y" + }, + { + "name": "reserve_x", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "token_mint_x" + } + ] + } + }, + { + "name": "reserve_y", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "token_mint_y" + } + ] + } + }, + { + "name": "oracle", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 111, + 114, + 97, + 99, + 108, + 101 + ] + }, + { + "kind": "account", + "path": "lb_pair" + } + ] + } + }, + { + "name": "user_token_x" + }, + { + "name": "funder", + "writable": true, + "signer": true + }, + { + "name": "token_badge_x", + "optional": true + }, + { + "name": "token_badge_y", + "optional": true + }, + { + "name": "token_program_x" + }, + { + "name": "token_program_y" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "user_token_y" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "CustomizableParams" + } + } + } + ] + }, + { + "name": "initialize_lb_pair", + "discriminator": [ + 45, + 154, + 237, + 210, + 221, + 15, + 166, + 92 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 105, + 116, + 109, + 97, + 112 + ] + }, + { + "kind": "account", + "path": "lb_pair" + } + ] + } + }, + { + "name": "token_mint_x" + }, + { + "name": "token_mint_y" + }, + { + "name": "reserve_x", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "token_mint_x" + } + ] + } + }, + { + "name": "reserve_y", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "token_mint_y" + } + ] + } + }, + { + "name": "oracle", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 111, + 114, + 97, + 99, + 108, + 101 + ] + }, + { + "kind": "account", + "path": "lb_pair" + } + ] + } + }, + { + "name": "preset_parameter" + }, + { + "name": "funder", + "writable": true, + "signer": true + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "rent" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "active_id", + "type": "i32" + }, + { + "name": "bin_step", + "type": "u16" + } + ] + }, + { + "name": "initialize_lb_pair2", + "discriminator": [ + 73, + 59, + 36, + 120, + 237, + 83, + 108, + 198 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 105, + 116, + 109, + 97, + 112 + ] + }, + { + "kind": "account", + "path": "lb_pair" + } + ] + } + }, + { + "name": "token_mint_x" + }, + { + "name": "token_mint_y" + }, + { + "name": "reserve_x", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "token_mint_x" + } + ] + } + }, + { + "name": "reserve_y", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "token_mint_y" + } + ] + } + }, + { + "name": "oracle", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 111, + 114, + 97, + 99, + 108, + 101 + ] + }, + { + "kind": "account", + "path": "lb_pair" + } + ] + } + }, + { + "name": "preset_parameter" + }, + { + "name": "funder", + "writable": true, + "signer": true + }, + { + "name": "token_badge_x", + "optional": true + }, + { + "name": "token_badge_y", + "optional": true + }, + { + "name": "token_program_x" + }, + { + "name": "token_program_y" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "InitializeLbPair2Params" + } + } + } + ] + }, + { + "name": "initialize_permission_lb_pair", + "discriminator": [ + 108, + 102, + 213, + 85, + 251, + 3, + 53, + 21 + ], + "accounts": [ + { + "name": "base", + "signer": true + }, + { + "name": "lb_pair", + "writable": true + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 105, + 116, + 109, + 97, + 112 + ] + }, + { + "kind": "account", + "path": "lb_pair" + } + ] + } + }, + { + "name": "token_mint_x" + }, + { + "name": "token_mint_y" + }, + { + "name": "reserve_x", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "token_mint_x" + } + ] + } + }, + { + "name": "reserve_y", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "token_mint_y" + } + ] + } + }, + { + "name": "oracle", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 111, + 114, + 97, + 99, + 108, + 101 + ] + }, + { + "kind": "account", + "path": "lb_pair" + } + ] + } + }, + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + }, + { + "name": "token_badge_x", + "optional": true + }, + { + "name": "token_badge_y", + "optional": true + }, + { + "name": "token_program_x" + }, + { + "name": "token_program_y" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "ix_data", + "type": { + "defined": { + "name": "InitPermissionPairIx" + } + } + } + ] + }, + { + "name": "initialize_position", + "discriminator": [ + 219, + 192, + 234, + 71, + 190, + 191, + 102, + 80 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "position", + "writable": true, + "signer": true + }, + { + "name": "lb_pair" + }, + { + "name": "owner", + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "rent" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "lower_bin_id", + "type": "i32" + }, + { + "name": "width", + "type": "i32" + } + ] + }, + { + "name": "initialize_position2", + "discriminator": [ + 143, + 19, + 242, + 145, + 213, + 15, + 104, + 115 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "position", + "writable": true, + "signer": true + }, + { + "name": "lb_pair" + }, + { + "name": "owner", + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "lower_bin_id", + "type": "i32" + }, + { + "name": "width", + "type": "i32" + } + ] + }, + { + "name": "initialize_position_by_operator", + "discriminator": [ + 251, + 189, + 190, + 244, + 117, + 254, + 35, + 148 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "base", + "signer": true + }, + { + "name": "position", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 115, + 105, + 116, + 105, + 111, + 110 + ] + }, + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "base" + }, + { + "kind": "arg", + "path": "lower_bin_id" + }, + { + "kind": "arg", + "path": "width" + } + ] + } + }, + { + "name": "lb_pair" + }, + { + "name": "owner" + }, + { + "name": "operator", + "docs": [ + "operator" + ], + "signer": true + }, + { + "name": "operator_token_x" + }, + { + "name": "owner_token_x" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "lower_bin_id", + "type": "i32" + }, + { + "name": "width", + "type": "i32" + }, + { + "name": "fee_owner", + "type": "pubkey" + }, + { + "name": "lock_release_point", + "type": "u64" + } + ] + }, + { + "name": "initialize_position_pda", + "discriminator": [ + 46, + 82, + 125, + 146, + 85, + 141, + 228, + 153 + ], + "accounts": [ + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "base", + "signer": true + }, + { + "name": "position", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 111, + 115, + 105, + 116, + 105, + 111, + 110 + ] + }, + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "account", + "path": "base" + }, + { + "kind": "arg", + "path": "lower_bin_id" + }, + { + "kind": "arg", + "path": "width" + } + ] + } + }, + { + "name": "lb_pair" + }, + { + "name": "owner", + "docs": [ + "owner" + ], + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "rent" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "lower_bin_id", + "type": "i32" + }, + { + "name": "width", + "type": "i32" + } + ] + }, + { + "name": "initialize_preset_parameter", + "discriminator": [ + 66, + 188, + 71, + 211, + 98, + 109, + 14, + 186 + ], + "accounts": [ + { + "name": "preset_parameter", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 112, + 114, + 101, + 115, + 101, + 116, + 95, + 112, + 97, + 114, + 97, + 109, + 101, + 116, + 101, + 114, + 50 + ] + }, + { + "kind": "arg", + "path": "ix.index" + } + ] + } + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + }, + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "ix", + "type": { + "defined": { + "name": "InitPresetParametersIx" + } + } + } + ] + }, + { + "name": "initialize_reward", + "discriminator": [ + 95, + 135, + 192, + 196, + 242, + 129, + 230, + 68 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "reward_vault", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "lb_pair" + }, + { + "kind": "arg", + "path": "reward_index" + } + ] + } + }, + { + "name": "reward_mint" + }, + { + "name": "token_badge", + "optional": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + }, + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "token_program" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "reward_duration", + "type": "u64" + }, + { + "name": "funder", + "type": "pubkey" + } + ] + }, + { + "name": "initialize_token_badge", + "discriminator": [ + 253, + 77, + 205, + 95, + 27, + 224, + 89, + 223 + ], + "accounts": [ + { + "name": "token_mint" + }, + { + "name": "token_badge", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 116, + 111, + 107, + 101, + 110, + 95, + 98, + 97, + 100, + 103, + 101 + ] + }, + { + "kind": "account", + "path": "token_mint" + } + ] + } + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + }, + { + "name": "payer", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "migrate_position", + "discriminator": [ + 15, + 132, + 59, + 50, + 199, + 6, + 251, + 46 + ], + "accounts": [ + { + "name": "position_v2", + "writable": true, + "signer": true + }, + { + "name": "position_v1", + "writable": true + }, + { + "name": "lb_pair" + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "signer_and_payer", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "rent_receiver", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "rebalance_liquidity", + "discriminator": [ + 92, + 4, + 176, + 193, + 119, + 185, + 83, + 9 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "owner", + "signer": true, + "relations": [ + "position" + ] + }, + { + "name": "rent_payer", + "writable": true, + "signer": true + }, + { + "name": "token_x_program" + }, + { + "name": "token_y_program" + }, + { + "name": "memo_program", + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "RebalanceLiquidityParams" + } + } + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "remove_all_liquidity", + "discriminator": [ + 10, + 51, + 61, + 35, + 112, + 105, + 24, + 85 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_x_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_y_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + }, + { + "name": "remove_liquidity", + "discriminator": [ + 80, + 85, + 209, + 72, + 24, + 206, + 177, + 108 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_x_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_y_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "bin_liquidity_removal", + "type": { + "vec": { + "defined": { + "name": "BinLiquidityReduction" + } + } + } + } + ] + }, + { + "name": "remove_liquidity2", + "discriminator": [ + 230, + 215, + 82, + 127, + 241, + 101, + 227, + 146 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_x_program" + }, + { + "name": "token_y_program" + }, + { + "name": "memo_program", + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "bin_liquidity_removal", + "type": { + "vec": { + "defined": { + "name": "BinLiquidityReduction" + } + } + } + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "remove_liquidity_by_range", + "discriminator": [ + 26, + 82, + 102, + 152, + 240, + 74, + 105, + 26 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_x_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_y_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "from_bin_id", + "type": "i32" + }, + { + "name": "to_bin_id", + "type": "i32" + }, + { + "name": "bps_to_remove", + "type": "u16" + } + ] + }, + { + "name": "remove_liquidity_by_range2", + "discriminator": [ + 204, + 2, + 195, + 145, + 53, + 145, + 145, + 205 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "writable": true, + "optional": true + }, + { + "name": "user_token_x", + "writable": true + }, + { + "name": "user_token_y", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "sender", + "signer": true + }, + { + "name": "token_x_program" + }, + { + "name": "token_y_program" + }, + { + "name": "memo_program", + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "from_bin_id", + "type": "i32" + }, + { + "name": "to_bin_id", + "type": "i32" + }, + { + "name": "bps_to_remove", + "type": "u16" + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "reset_bin_array_tombstone_fields", + "discriminator": [ + 54, + 90, + 252, + 63, + 41, + 206, + 63, + 63 + ], + "accounts": [ + { + "name": "lb_pair", + "relations": [ + "bin_array" + ] + }, + { + "name": "bin_array", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + } + ], + "args": [] + }, + { + "name": "reset_pool_tombstone_fields", + "discriminator": [ + 246, + 109, + 19, + 120, + 108, + 113, + 68, + 252 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + } + ], + "args": [] + }, + { + "name": "reset_position_tombstone_fields", + "discriminator": [ + 206, + 6, + 51, + 218, + 211, + 30, + 159, + 84 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + } + ], + "args": [] + }, + { + "name": "set_activation_point", + "discriminator": [ + 91, + 249, + 15, + 165, + 26, + 129, + 254, + 125 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "signer", + "signer": true + } + ], + "args": [ + { + "name": "activation_point", + "type": "u64" + } + ] + }, + { + "name": "set_pair_status", + "discriminator": [ + 67, + 248, + 231, + 137, + 154, + 149, + 217, + 174 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + } + ], + "args": [ + { + "name": "status", + "type": "u8" + } + ] + }, + { + "name": "set_pair_status_permissionless", + "discriminator": [ + 78, + 59, + 152, + 211, + 70, + 183, + 46, + 208 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "signer", + "signer": true + } + ], + "args": [ + { + "name": "status", + "type": "u8" + } + ] + }, + { + "name": "set_pre_activation_duration", + "discriminator": [ + 165, + 61, + 201, + 244, + 130, + 159, + 22, + 100 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "signer", + "signer": true + } + ], + "args": [ + { + "name": "pre_activation_duration", + "type": "u64" + } + ] + }, + { + "name": "set_pre_activation_swap_address", + "discriminator": [ + 57, + 139, + 47, + 123, + 216, + 80, + 223, + 10 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "signer", + "signer": true + } + ], + "args": [ + { + "name": "pre_activation_swap_address", + "type": "pubkey" + } + ] + }, + { + "name": "swap", + "discriminator": [ + 248, + 198, + 158, + 145, + 225, + 117, + 135, + 200 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "optional": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "user_token_in", + "writable": true + }, + { + "name": "user_token_out", + "writable": true + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "oracle", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "host_fee_in", + "writable": true, + "optional": true + }, + { + "name": "user", + "signer": true + }, + { + "name": "token_x_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_y_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "amount_in", + "type": "u64" + }, + { + "name": "min_amount_out", + "type": "u64" + } + ] + }, + { + "name": "swap2", + "discriminator": [ + 65, + 75, + 63, + 76, + 235, + 91, + 91, + 136 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "optional": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "user_token_in", + "writable": true + }, + { + "name": "user_token_out", + "writable": true + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "oracle", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "host_fee_in", + "writable": true, + "optional": true + }, + { + "name": "user", + "signer": true + }, + { + "name": "token_x_program" + }, + { + "name": "token_y_program" + }, + { + "name": "memo_program", + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "amount_in", + "type": "u64" + }, + { + "name": "min_amount_out", + "type": "u64" + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "swap_exact_out", + "discriminator": [ + 250, + 73, + 101, + 33, + 38, + 207, + 75, + 184 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "optional": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "user_token_in", + "writable": true + }, + { + "name": "user_token_out", + "writable": true + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "oracle", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "host_fee_in", + "writable": true, + "optional": true + }, + { + "name": "user", + "signer": true + }, + { + "name": "token_x_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_y_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "max_in_amount", + "type": "u64" + }, + { + "name": "out_amount", + "type": "u64" + } + ] + }, + { + "name": "swap_exact_out2", + "discriminator": [ + 43, + 215, + 247, + 132, + 137, + 60, + 243, + 81 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "optional": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "user_token_in", + "writable": true + }, + { + "name": "user_token_out", + "writable": true + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "oracle", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "host_fee_in", + "writable": true, + "optional": true + }, + { + "name": "user", + "signer": true + }, + { + "name": "token_x_program" + }, + { + "name": "token_y_program" + }, + { + "name": "memo_program", + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "max_in_amount", + "type": "u64" + }, + { + "name": "out_amount", + "type": "u64" + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "swap_with_price_impact", + "discriminator": [ + 56, + 173, + 230, + 208, + 173, + 228, + 156, + 205 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "optional": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "user_token_in", + "writable": true + }, + { + "name": "user_token_out", + "writable": true + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "oracle", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "host_fee_in", + "writable": true, + "optional": true + }, + { + "name": "user", + "signer": true + }, + { + "name": "token_x_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "token_y_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "amount_in", + "type": "u64" + }, + { + "name": "active_id", + "type": { + "option": "i32" + } + }, + { + "name": "max_price_impact_bps", + "type": "u16" + } + ] + }, + { + "name": "swap_with_price_impact2", + "discriminator": [ + 74, + 98, + 192, + 214, + 177, + 51, + 75, + 51 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "bin_array_bitmap_extension" + ] + }, + { + "name": "bin_array_bitmap_extension", + "optional": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "user_token_in", + "writable": true + }, + { + "name": "user_token_out", + "writable": true + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "oracle", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "host_fee_in", + "writable": true, + "optional": true + }, + { + "name": "user", + "signer": true + }, + { + "name": "token_x_program" + }, + { + "name": "token_y_program" + }, + { + "name": "memo_program", + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "amount_in", + "type": "u64" + }, + { + "name": "active_id", + "type": { + "option": "i32" + } + }, + { + "name": "max_price_impact_bps", + "type": "u16" + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "update_base_fee_parameters", + "discriminator": [ + 75, + 168, + 223, + 161, + 16, + 195, + 3, + 47 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "fee_parameter", + "type": { + "defined": { + "name": "BaseFeeParameter" + } + } + } + ] + }, + { + "name": "update_dynamic_fee_parameters", + "discriminator": [ + 92, + 161, + 46, + 246, + 255, + 189, + 22, + 22 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "fee_parameter", + "type": { + "defined": { + "name": "DynamicFeeParameter" + } + } + } + ] + }, + { + "name": "update_fees_and_reward2", + "discriminator": [ + 32, + 142, + 184, + 154, + 103, + 65, + 184, + 88 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position" + ] + }, + { + "name": "owner", + "signer": true + } + ], + "args": [ + { + "name": "min_bin_id", + "type": "i32" + }, + { + "name": "max_bin_id", + "type": "i32" + } + ] + }, + { + "name": "update_fees_and_rewards", + "discriminator": [ + 154, + 230, + 250, + 13, + 236, + 209, + 75, + 223 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "lb_pair", + "writable": true, + "relations": [ + "position", + "bin_array_lower", + "bin_array_upper" + ] + }, + { + "name": "bin_array_lower", + "writable": true + }, + { + "name": "bin_array_upper", + "writable": true + }, + { + "name": "owner", + "signer": true + } + ], + "args": [] + }, + { + "name": "update_position_operator", + "discriminator": [ + 202, + 184, + 103, + 143, + 180, + 191, + 116, + 217 + ], + "accounts": [ + { + "name": "position", + "writable": true + }, + { + "name": "owner", + "signer": true, + "relations": [ + "position" + ] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "operator", + "type": "pubkey" + } + ] + }, + { + "name": "update_reward_duration", + "discriminator": [ + 138, + 174, + 196, + 169, + 213, + 235, + 254, + 107 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "bin_array" + ] + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + }, + { + "name": "bin_array", + "writable": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "new_duration", + "type": "u64" + } + ] + }, + { + "name": "update_reward_funder", + "discriminator": [ + 211, + 28, + 48, + 32, + 215, + 160, + 35, + 23 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "signer": true + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "new_funder", + "type": "pubkey" + } + ] + }, + { + "name": "withdraw_ineligible_reward", + "discriminator": [ + 148, + 206, + 42, + 195, + 247, + 49, + 103, + 8 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true, + "relations": [ + "bin_array" + ] + }, + { + "name": "reward_vault", + "writable": true + }, + { + "name": "reward_mint" + }, + { + "name": "funder_token_account", + "writable": true + }, + { + "name": "funder", + "signer": true + }, + { + "name": "bin_array", + "writable": true + }, + { + "name": "token_program" + }, + { + "name": "memo_program", + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "withdraw_protocol_fee", + "discriminator": [ + 158, + 201, + 158, + 189, + 33, + 93, + 162, + 103 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "reserve_x", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "reserve_y", + "writable": true, + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_x_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "token_y_mint", + "relations": [ + "lb_pair" + ] + }, + { + "name": "receiver_token_x", + "writable": true + }, + { + "name": "receiver_token_y", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "docs": [ + "operator" + ], + "signer": true + }, + { + "name": "token_x_program" + }, + { + "name": "token_y_program" + } + ], + "args": [ + { + "name": "max_amount_x", + "type": "u64" + }, + { + "name": "max_amount_y", + "type": "u64" + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + }, + { + "name": "zap_protocol_fee", + "discriminator": [ + 213, + 155, + 187, + 34, + 56, + 182, + 91, + 240 + ], + "accounts": [ + { + "name": "lb_pair", + "writable": true + }, + { + "name": "reserve", + "writable": true + }, + { + "name": "token_mint" + }, + { + "name": "receiver_token", + "writable": true + }, + { + "name": "operator" + }, + { + "name": "signer", + "docs": [ + "operator" + ], + "signer": true + }, + { + "name": "token_program" + }, + { + "name": "sysvar_instructions", + "address": "Sysvar1nstructions1111111111111111111111111" + } + ], + "args": [ + { + "name": "max_amount", + "type": "u64" + }, + { + "name": "remaining_accounts_info", + "type": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + ] + } + ], + "accounts": [ + { + "name": "BinArray", + "discriminator": [ + 92, + 142, + 92, + 220, + 5, + 148, + 70, + 181 + ] + }, + { + "name": "BinArrayBitmapExtension", + "discriminator": [ + 80, + 111, + 124, + 113, + 55, + 237, + 18, + 5 + ] + }, + { + "name": "ClaimFeeOperator", + "discriminator": [ + 166, + 48, + 134, + 86, + 34, + 200, + 188, + 150 + ] + }, + { + "name": "DummyZcAccount", + "discriminator": [ + 94, + 107, + 238, + 80, + 208, + 48, + 180, + 8 + ] + }, + { + "name": "LbPair", + "discriminator": [ + 33, + 11, + 49, + 98, + 181, + 101, + 177, + 13 + ] + }, + { + "name": "Operator", + "discriminator": [ + 219, + 31, + 188, + 145, + 69, + 139, + 204, + 117 + ] + }, + { + "name": "Oracle", + "discriminator": [ + 139, + 194, + 131, + 179, + 140, + 179, + 229, + 244 + ] + }, + { + "name": "Position", + "discriminator": [ + 170, + 188, + 143, + 228, + 122, + 64, + 247, + 208 + ] + }, + { + "name": "PositionV2", + "discriminator": [ + 117, + 176, + 212, + 199, + 245, + 180, + 133, + 182 + ] + }, + { + "name": "PresetParameter", + "discriminator": [ + 242, + 62, + 244, + 34, + 181, + 112, + 58, + 170 + ] + }, + { + "name": "PresetParameter2", + "discriminator": [ + 171, + 236, + 148, + 115, + 162, + 113, + 222, + 174 + ] + }, + { + "name": "TokenBadge", + "discriminator": [ + 116, + 219, + 204, + 229, + 249, + 116, + 255, + 150 + ] + } + ], + "events": [ + { + "name": "AddLiquidity", + "discriminator": [ + 31, + 94, + 125, + 90, + 227, + 52, + 61, + 186 + ] + }, + { + "name": "ClaimFee", + "discriminator": [ + 75, + 122, + 154, + 48, + 140, + 74, + 123, + 163 + ] + }, + { + "name": "ClaimFee2", + "discriminator": [ + 232, + 171, + 242, + 97, + 58, + 77, + 35, + 45 + ] + }, + { + "name": "ClaimReward", + "discriminator": [ + 148, + 116, + 134, + 204, + 22, + 171, + 85, + 95 + ] + }, + { + "name": "ClaimReward2", + "discriminator": [ + 27, + 143, + 244, + 33, + 80, + 43, + 110, + 146 + ] + }, + { + "name": "CompositionFee", + "discriminator": [ + 128, + 151, + 123, + 106, + 17, + 102, + 113, + 142 + ] + }, + { + "name": "DecreasePositionLength", + "discriminator": [ + 52, + 118, + 235, + 85, + 172, + 169, + 15, + 128 + ] + }, + { + "name": "DynamicFeeParameterUpdate", + "discriminator": [ + 88, + 88, + 178, + 135, + 194, + 146, + 91, + 243 + ] + }, + { + "name": "FeeParameterUpdate", + "discriminator": [ + 48, + 76, + 241, + 117, + 144, + 215, + 242, + 44 + ] + }, + { + "name": "FundReward", + "discriminator": [ + 246, + 228, + 58, + 130, + 145, + 170, + 79, + 204 + ] + }, + { + "name": "GoToABin", + "discriminator": [ + 59, + 138, + 76, + 68, + 138, + 131, + 176, + 67 + ] + }, + { + "name": "IncreaseObservation", + "discriminator": [ + 99, + 249, + 17, + 121, + 166, + 156, + 207, + 215 + ] + }, + { + "name": "IncreasePositionLength", + "discriminator": [ + 157, + 239, + 42, + 204, + 30, + 56, + 223, + 46 + ] + }, + { + "name": "InitializeReward", + "discriminator": [ + 211, + 153, + 88, + 62, + 149, + 60, + 177, + 70 + ] + }, + { + "name": "LbPairCreate", + "discriminator": [ + 185, + 74, + 252, + 125, + 27, + 215, + 188, + 111 + ] + }, + { + "name": "PositionClose", + "discriminator": [ + 255, + 196, + 16, + 107, + 28, + 202, + 53, + 128 + ] + }, + { + "name": "PositionCreate", + "discriminator": [ + 144, + 142, + 252, + 84, + 157, + 53, + 37, + 121 + ] + }, + { + "name": "Rebalancing", + "discriminator": [ + 0, + 109, + 117, + 179, + 61, + 91, + 199, + 200 + ] + }, + { + "name": "RemoveLiquidity", + "discriminator": [ + 116, + 244, + 97, + 232, + 103, + 31, + 152, + 58 + ] + }, + { + "name": "Swap", + "discriminator": [ + 81, + 108, + 227, + 190, + 205, + 208, + 10, + 196 + ] + }, + { + "name": "UpdatePositionLockReleasePoint", + "discriminator": [ + 133, + 214, + 66, + 224, + 64, + 12, + 7, + 191 + ] + }, + { + "name": "UpdatePositionOperator", + "discriminator": [ + 39, + 115, + 48, + 204, + 246, + 47, + 66, + 57 + ] + }, + { + "name": "UpdateRewardDuration", + "discriminator": [ + 223, + 245, + 224, + 153, + 49, + 29, + 163, + 172 + ] + }, + { + "name": "UpdateRewardFunder", + "discriminator": [ + 224, + 178, + 174, + 74, + 252, + 165, + 85, + 180 + ] + }, + { + "name": "WithdrawIneligibleReward", + "discriminator": [ + 231, + 189, + 65, + 149, + 102, + 215, + 154, + 244 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "InvalidStartBinIndex", + "msg": "Invalid start bin index" + }, + { + "code": 6001, + "name": "InvalidBinId", + "msg": "Invalid bin id" + }, + { + "code": 6002, + "name": "InvalidInput", + "msg": "Invalid input data" + }, + { + "code": 6003, + "name": "ExceededAmountSlippageTolerance", + "msg": "Exceeded amount slippage tolerance" + }, + { + "code": 6004, + "name": "ExceededBinSlippageTolerance", + "msg": "Exceeded bin slippage tolerance" + }, + { + "code": 6005, + "name": "CompositionFactorFlawed", + "msg": "Composition factor flawed" + }, + { + "code": 6006, + "name": "NonPresetBinStep", + "msg": "Non preset bin step" + }, + { + "code": 6007, + "name": "ZeroLiquidity", + "msg": "Zero liquidity" + }, + { + "code": 6008, + "name": "InvalidPosition", + "msg": "Invalid position" + }, + { + "code": 6009, + "name": "BinArrayNotFound", + "msg": "Bin array not found" + }, + { + "code": 6010, + "name": "InvalidTokenMint", + "msg": "Invalid token mint" + }, + { + "code": 6011, + "name": "InvalidAccountForSingleDeposit", + "msg": "Invalid account for single deposit" + }, + { + "code": 6012, + "name": "PairInsufficientLiquidity", + "msg": "Pair insufficient liquidity" + }, + { + "code": 6013, + "name": "InvalidFeeOwner", + "msg": "Invalid fee owner" + }, + { + "code": 6014, + "name": "InvalidFeeWithdrawAmount", + "msg": "Invalid fee withdraw amount" + }, + { + "code": 6015, + "name": "InvalidAdmin", + "msg": "Invalid admin" + }, + { + "code": 6016, + "name": "IdenticalFeeOwner", + "msg": "Identical fee owner" + }, + { + "code": 6017, + "name": "InvalidBps", + "msg": "Invalid basis point" + }, + { + "code": 6018, + "name": "MathOverflow", + "msg": "Math operation overflow" + }, + { + "code": 6019, + "name": "TypeCastFailed", + "msg": "Type cast error" + }, + { + "code": 6020, + "name": "InvalidRewardIndex", + "msg": "Invalid reward index" + }, + { + "code": 6021, + "name": "InvalidRewardDuration", + "msg": "Invalid reward duration" + }, + { + "code": 6022, + "name": "RewardInitialized", + "msg": "Reward already initialized" + }, + { + "code": 6023, + "name": "RewardUninitialized", + "msg": "Reward not initialized" + }, + { + "code": 6024, + "name": "IdenticalFunder", + "msg": "Identical funder" + }, + { + "code": 6025, + "name": "RewardCampaignInProgress", + "msg": "Reward campaign in progress" + }, + { + "code": 6026, + "name": "IdenticalRewardDuration", + "msg": "Reward duration is the same" + }, + { + "code": 6027, + "name": "InvalidBinArray", + "msg": "Invalid bin array" + }, + { + "code": 6028, + "name": "NonContinuousBinArrays", + "msg": "Bin arrays must be continuous" + }, + { + "code": 6029, + "name": "InvalidRewardVault", + "msg": "Invalid reward vault" + }, + { + "code": 6030, + "name": "NonEmptyPosition", + "msg": "Position is not empty" + }, + { + "code": 6031, + "name": "UnauthorizedAccess", + "msg": "Unauthorized access" + }, + { + "code": 6032, + "name": "InvalidFeeParameter", + "msg": "Invalid fee parameter" + }, + { + "code": 6033, + "name": "MissingOracle", + "msg": "Missing oracle account" + }, + { + "code": 6034, + "name": "InsufficientSample", + "msg": "Insufficient observation sample" + }, + { + "code": 6035, + "name": "InvalidLookupTimestamp", + "msg": "Invalid lookup timestamp" + }, + { + "code": 6036, + "name": "BitmapExtensionAccountIsNotProvided", + "msg": "Bitmap extension account is not provided" + }, + { + "code": 6037, + "name": "CannotFindNonZeroLiquidityBinArrayId", + "msg": "Cannot find non-zero liquidity binArrayId" + }, + { + "code": 6038, + "name": "BinIdOutOfBound", + "msg": "Bin id out of bound" + }, + { + "code": 6039, + "name": "InsufficientOutAmount", + "msg": "Insufficient amount in for minimum out" + }, + { + "code": 6040, + "name": "InvalidPositionWidth", + "msg": "Invalid position width" + }, + { + "code": 6041, + "name": "ExcessiveFeeUpdate", + "msg": "Excessive fee update" + }, + { + "code": 6042, + "name": "PoolDisabled", + "msg": "Pool disabled" + }, + { + "code": 6043, + "name": "InvalidPoolType", + "msg": "Invalid pool type" + }, + { + "code": 6044, + "name": "ExceedMaxWhitelist", + "msg": "Whitelist for wallet is full" + }, + { + "code": 6045, + "name": "InvalidIndex", + "msg": "Invalid index" + }, + { + "code": 6046, + "name": "RewardNotEnded", + "msg": "Reward not ended" + }, + { + "code": 6047, + "name": "MustWithdrawnIneligibleReward", + "msg": "Must withdraw ineligible reward" + }, + { + "code": 6048, + "name": "UnauthorizedAddress", + "msg": "Unauthorized address" + }, + { + "code": 6049, + "name": "OperatorsAreTheSame", + "msg": "Cannot update because operators are the same" + }, + { + "code": 6050, + "name": "WithdrawToWrongTokenAccount", + "msg": "Withdraw to wrong token account" + }, + { + "code": 6051, + "name": "WrongRentReceiver", + "msg": "Wrong rent receiver" + }, + { + "code": 6052, + "name": "AlreadyPassActivationPoint", + "msg": "Already activated" + }, + { + "code": 6053, + "name": "ExceedMaxSwappedAmount", + "msg": "Swapped amount is exceeded max swapped amount" + }, + { + "code": 6054, + "name": "InvalidStrategyParameters", + "msg": "Invalid strategy parameters" + }, + { + "code": 6055, + "name": "LiquidityLocked", + "msg": "Liquidity locked" + }, + { + "code": 6056, + "name": "BinRangeIsNotEmpty", + "msg": "Bin range is not empty" + }, + { + "code": 6057, + "name": "NotExactAmountOut", + "msg": "Amount out is not matched with exact amount out" + }, + { + "code": 6058, + "name": "InvalidActivationType", + "msg": "Invalid activation type" + }, + { + "code": 6059, + "name": "InvalidActivationDuration", + "msg": "Invalid activation duration" + }, + { + "code": 6060, + "name": "MissingTokenAmountAsTokenLaunchProof", + "msg": "Missing token amount as token launch owner proof" + }, + { + "code": 6061, + "name": "InvalidQuoteToken", + "msg": "Quote token must be SOL or USDC" + }, + { + "code": 6062, + "name": "InvalidBinStep", + "msg": "Invalid bin step" + }, + { + "code": 6063, + "name": "InvalidBaseFee", + "msg": "Invalid base fee" + }, + { + "code": 6064, + "name": "InvalidPreActivationDuration", + "msg": "Invalid pre-activation duration" + }, + { + "code": 6065, + "name": "AlreadyPassPreActivationSwapPoint", + "msg": "Already pass pre-activation swap point" + }, + { + "code": 6066, + "name": "InvalidStatus", + "msg": "Invalid status" + }, + { + "code": 6067, + "name": "ExceededMaxOracleLength", + "msg": "Exceed max oracle length" + }, + { + "code": 6068, + "name": "InvalidMinimumLiquidity", + "msg": "Invalid minimum liquidity" + }, + { + "code": 6069, + "name": "NotSupportMint", + "msg": "Not support token_2022 mint extension" + }, + { + "code": 6070, + "name": "UnsupportedMintExtension", + "msg": "Unsupported mint extension" + }, + { + "code": 6071, + "name": "UnsupportNativeMintToken2022", + "msg": "Unsupported native mint token2022" + }, + { + "code": 6072, + "name": "UnmatchTokenMint", + "msg": "Unmatch token mint" + }, + { + "code": 6073, + "name": "UnsupportedTokenMint", + "msg": "Unsupported token mint" + }, + { + "code": 6074, + "name": "InsufficientRemainingAccounts", + "msg": "Insufficient remaining accounts" + }, + { + "code": 6075, + "name": "InvalidRemainingAccountSlice", + "msg": "Invalid remaining account slice" + }, + { + "code": 6076, + "name": "DuplicatedRemainingAccountTypes", + "msg": "Duplicated remaining account types" + }, + { + "code": 6077, + "name": "MissingRemainingAccountForTransferHook", + "msg": "Missing remaining account for transfer hook" + }, + { + "code": 6078, + "name": "NoTransferHookProgram", + "msg": "Remaining account was passed for transfer hook but there's no hook program" + }, + { + "code": 6079, + "name": "ZeroFundedAmount", + "msg": "Zero funded amount" + }, + { + "code": 6080, + "name": "InvalidSide", + "msg": "Invalid side" + }, + { + "code": 6081, + "name": "InvalidResizeLength", + "msg": "Invalid resize length" + }, + { + "code": 6082, + "name": "NotSupportAtTheMoment", + "msg": "Not support at the moment" + }, + { + "code": 6083, + "name": "InvalidRebalanceParameters", + "msg": "Invalid rebalance parameters" + }, + { + "code": 6084, + "name": "InvalidRewardAccounts", + "msg": "Invalid reward accounts" + }, + { + "code": 6085, + "name": "UndeterminedError", + "msg": "Undetermined error" + }, + { + "code": 6086, + "name": "ReallocExceedMaxLengthPerInstruction", + "msg": "Realloc exceed max length per instruction" + }, + { + "code": 6087, + "name": "InvalidBaseFeeMantissa", + "msg": "Mantissa cannot more than two significant digits" + }, + { + "code": 6088, + "name": "InvalidPositionOwner", + "msg": "Invalid position owner" + }, + { + "code": 6089, + "name": "InvalidPoolAddress", + "msg": "Invalid pool address" + }, + { + "code": 6090, + "name": "InvalidTokenBadgeType", + "msg": "Invalid token badge type" + }, + { + "code": 6091, + "name": "InvalidTransferHookAuthority", + "msg": "Invalid transfer hook authority" + }, + { + "code": 6092, + "name": "AmountXIsNegative", + "msg": "Amount x is negative" + }, + { + "code": 6093, + "name": "AmountYIsNegative", + "msg": "Amount y is negative" + }, + { + "code": 6094, + "name": "InvalidPoolCreator", + "msg": "Invalid pool creator" + }, + { + "code": 6095, + "name": "InvalidFunctionType", + "msg": "Invalid function type" + }, + { + "code": 6096, + "name": "InvalidPermission", + "msg": "Invalid permission" + }, + { + "code": 6097, + "name": "IncorrectATA", + "msg": "Incorrect ATA" + }, + { + "code": 6098, + "name": "InvalidWithdrawProtocolFeeZapAccounts", + "msg": "Invalid withdraw protocol fee zap accounts" + }, + { + "code": 6099, + "name": "MintRestrictedFromZap", + "msg": "SOL,USDC protocol fee cannot be withdrawn via zap" + }, + { + "code": 6100, + "name": "CpiDisabled", + "msg": "CPI disabled" + }, + { + "code": 6101, + "name": "MissingZapOutInstruction", + "msg": "Missing zap out instruction" + }, + { + "code": 6102, + "name": "InvalidZapAccounts", + "msg": "Invalid zap accounts" + }, + { + "code": 6103, + "name": "InvalidZapOutParameters", + "msg": "Invalid zap out parameters" + } + ], + "types": [ + { + "name": "AccountsType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "TransferHookX" + }, + { + "name": "TransferHookY" + }, + { + "name": "TransferHookReward" + }, + { + "name": "TransferHookMultiReward", + "fields": [ + "u8" + ] + } + ] + } + }, + { + "name": "ActivationType", + "docs": [ + "Type of the activation" + ], + "repr": { + "kind": "rust" + }, + "type": { + "kind": "enum", + "variants": [ + { + "name": "Slot" + }, + { + "name": "Timestamp" + } + ] + } + }, + { + "name": "AddLiquidity", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "from", + "type": "pubkey" + }, + { + "name": "position", + "type": "pubkey" + }, + { + "name": "amounts", + "type": { + "array": [ + "u64", + 2 + ] + } + }, + { + "name": "active_bin_id", + "type": "i32" + } + ] + } + }, + { + "name": "AddLiquidityParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "min_delta_id", + "type": "i32" + }, + { + "name": "max_delta_id", + "type": "i32" + }, + { + "name": "x0", + "type": "u64" + }, + { + "name": "y0", + "type": "u64" + }, + { + "name": "delta_x", + "type": "u64" + }, + { + "name": "delta_y", + "type": "u64" + }, + { + "name": "bit_flag", + "type": "u8" + }, + { + "name": "favor_x_in_active_id", + "type": "bool" + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 16 + ] + } + } + ] + } + }, + { + "name": "AddLiquiditySingleSidePreciseParameter", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bins", + "type": { + "vec": { + "defined": { + "name": "CompressedBinDepositAmount" + } + } + } + }, + { + "name": "decompress_multiplier", + "type": "u64" + } + ] + } + }, + { + "name": "AddLiquiditySingleSidePreciseParameter2", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bins", + "type": { + "vec": { + "defined": { + "name": "CompressedBinDepositAmount" + } + } + } + }, + { + "name": "decompress_multiplier", + "type": "u64" + }, + { + "name": "max_amount", + "type": "u64" + } + ] + } + }, + { + "name": "BaseFeeParameter", + "type": { + "kind": "struct", + "fields": [ + { + "name": "protocol_share", + "docs": [ + "Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee" + ], + "type": "u16" + }, + { + "name": "base_factor", + "docs": [ + "Base factor for base fee rate" + ], + "type": "u16" + }, + { + "name": "base_fee_power_factor", + "docs": [ + "Base fee power factor" + ], + "type": "u8" + } + ] + } + }, + { + "name": "Bin", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount_x", + "docs": [ + "Amount of token X in the bin. This already excluded protocol fees." + ], + "type": "u64" + }, + { + "name": "amount_y", + "docs": [ + "Amount of token Y in the bin. This already excluded protocol fees." + ], + "type": "u64" + }, + { + "name": "price", + "docs": [ + "Bin price" + ], + "type": "u128" + }, + { + "name": "liquidity_supply", + "docs": [ + "Liquidities of the bin. This is the same as LP mint supply. q-number" + ], + "type": "u128" + }, + { + "name": "function_bytes", + "docs": [ + "function bytes, could be used for liquidity mining or other functions in future" + ], + "type": { + "array": [ + "u128", + 2 + ] + } + }, + { + "name": "fee_amount_x_per_token_stored", + "docs": [ + "Swap fee amount of token X per liquidity deposited." + ], + "type": "u128" + }, + { + "name": "fee_amount_y_per_token_stored", + "docs": [ + "Swap fee amount of token Y per liquidity deposited." + ], + "type": "u128" + }, + { + "name": "_padding_0", + "docs": [ + "_padding_0, previous amount_x_in, BE CAREFUL FOR TOMBSTONE WHEN REUSE !!" + ], + "type": "u128" + }, + { + "name": "_padding_1", + "docs": [ + "_padding_1, previous amount_y_in, BE CAREFUL FOR TOMBSTONE WHEN REUSE !!" + ], + "type": "u128" + } + ] + } + }, + { + "name": "BinArray", + "docs": [ + "An account to contain a range of bin. For example: Bin 100 <-> 200.", + "For example:", + "BinArray index: 0 contains bin 0 <-> 599", + "index: 2 contains bin 600 <-> 1199, ..." + ], + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "index", + "type": "i64" + }, + { + "name": "version", + "type": "u8" + }, + { + "name": "_padding_1", + "type": { + "array": [ + "u8", + 7 + ] + } + }, + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "bins", + "type": { + "array": [ + { + "defined": { + "name": "Bin" + } + }, + 70 + ] + } + } + ] + } + }, + { + "name": "BinArrayBitmapExtension", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "positive_bin_array_bitmap", + "docs": [ + "Packed initialized bin array state for start_bin_index is positive" + ], + "type": { + "array": [ + { + "array": [ + "u64", + 8 + ] + }, + 12 + ] + } + }, + { + "name": "negative_bin_array_bitmap", + "docs": [ + "Packed initialized bin array state for start_bin_index is negative" + ], + "type": { + "array": [ + { + "array": [ + "u64", + 8 + ] + }, + 12 + ] + } + } + ] + } + }, + { + "name": "BinLiquidityDistribution", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bin_id", + "docs": [ + "Define the bin ID wish to deposit to." + ], + "type": "i32" + }, + { + "name": "distribution_x", + "docs": [ + "DistributionX (or distributionY) is the percentages of amountX (or amountY) you want to add to each bin." + ], + "type": "u16" + }, + { + "name": "distribution_y", + "docs": [ + "DistributionX (or distributionY) is the percentages of amountX (or amountY) you want to add to each bin." + ], + "type": "u16" + } + ] + } + }, + { + "name": "BinLiquidityDistributionByWeight", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bin_id", + "docs": [ + "Define the bin ID wish to deposit to." + ], + "type": "i32" + }, + { + "name": "weight", + "docs": [ + "weight of liquidity distributed for this bin id" + ], + "type": "u16" + } + ] + } + }, + { + "name": "BinLiquidityReduction", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bin_id", + "type": "i32" + }, + { + "name": "bps_to_remove", + "type": "u16" + } + ] + } + }, + { + "name": "ClaimFee", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "position", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "fee_x", + "type": "u64" + }, + { + "name": "fee_y", + "type": "u64" + } + ] + } + }, + { + "name": "ClaimFee2", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "position", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "fee_x", + "type": "u64" + }, + { + "name": "fee_y", + "type": "u64" + }, + { + "name": "active_bin_id", + "type": "i32" + } + ] + } + }, + { + "name": "ClaimFeeOperator", + "docs": [ + "Parameter that set by the protocol" + ], + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "operator", + "docs": [ + "operator" + ], + "type": "pubkey" + }, + { + "name": "_padding", + "docs": [ + "Reserve" + ], + "type": { + "array": [ + "u8", + 128 + ] + } + } + ] + } + }, + { + "name": "ClaimReward", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "position", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "total_reward", + "type": "u64" + } + ] + } + }, + { + "name": "ClaimReward2", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "position", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "total_reward", + "type": "u64" + }, + { + "name": "active_bin_id", + "type": "i32" + } + ] + } + }, + { + "name": "CompositionFee", + "type": { + "kind": "struct", + "fields": [ + { + "name": "from", + "type": "pubkey" + }, + { + "name": "bin_id", + "type": "i16" + }, + { + "name": "token_x_fee_amount", + "type": "u64" + }, + { + "name": "token_y_fee_amount", + "type": "u64" + }, + { + "name": "protocol_token_x_fee_amount", + "type": "u64" + }, + { + "name": "protocol_token_y_fee_amount", + "type": "u64" + } + ] + } + }, + { + "name": "CompressedBinDepositAmount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bin_id", + "type": "i32" + }, + { + "name": "amount", + "type": "u32" + } + ] + } + }, + { + "name": "CustomizableParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "active_id", + "docs": [ + "Pool price" + ], + "type": "i32" + }, + { + "name": "bin_step", + "docs": [ + "Bin step" + ], + "type": "u16" + }, + { + "name": "base_factor", + "docs": [ + "Base factor" + ], + "type": "u16" + }, + { + "name": "activation_type", + "docs": [ + "Activation type. 0 = Slot, 1 = Time. Check ActivationType enum" + ], + "type": "u8" + }, + { + "name": "has_alpha_vault", + "docs": [ + "Whether the pool has an alpha vault" + ], + "type": "bool" + }, + { + "name": "activation_point", + "docs": [ + "Decide when does the pool start trade. None = Now" + ], + "type": { + "option": "u64" + } + }, + { + "name": "creator_pool_on_off_control", + "docs": [ + "Pool creator have permission to enable/disable pool with restricted program validation. Only applicable for customizable permissionless pool." + ], + "type": "bool" + }, + { + "name": "base_fee_power_factor", + "docs": [ + "Base fee power factor" + ], + "type": "u8" + }, + { + "name": "function_type", + "docs": [ + "function type" + ], + "type": "u8" + }, + { + "name": "padding", + "docs": [ + "Padding, for future use" + ], + "type": { + "array": [ + "u8", + 61 + ] + } + } + ] + } + }, + { + "name": "DecreasePositionLength", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "position", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "length_to_remove", + "type": "u16" + }, + { + "name": "side", + "type": "u8" + } + ] + } + }, + { + "name": "DummyIx", + "type": { + "kind": "struct", + "fields": [ + { + "name": "_pair_status", + "type": { + "defined": { + "name": "PairStatus" + } + } + }, + { + "name": "_pair_type", + "type": { + "defined": { + "name": "PairType" + } + } + }, + { + "name": "_activation_type", + "type": { + "defined": { + "name": "ActivationType" + } + } + }, + { + "name": "_token_program_flag", + "type": { + "defined": { + "name": "TokenProgramFlags" + } + } + }, + { + "name": "_resize_side", + "type": { + "defined": { + "name": "ResizeSide" + } + } + }, + { + "name": "_rounding", + "type": { + "defined": { + "name": "Rounding" + } + } + } + ] + } + }, + { + "name": "DummyZcAccount", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "position_bin_data", + "type": { + "defined": { + "name": "PositionBinData" + } + } + } + ] + } + }, + { + "name": "DynamicFeeParameter", + "type": { + "kind": "struct", + "fields": [ + { + "name": "filter_period", + "docs": [ + "Filter period determine high frequency trading time window." + ], + "type": "u16" + }, + { + "name": "decay_period", + "docs": [ + "Decay period determine when the volatile fee start decay / decrease." + ], + "type": "u16" + }, + { + "name": "reduction_factor", + "docs": [ + "Reduction factor controls the volatile fee rate decrement rate." + ], + "type": "u16" + }, + { + "name": "variable_fee_control", + "docs": [ + "Used to scale the variable fee component depending on the dynamic of the market" + ], + "type": "u32" + }, + { + "name": "max_volatility_accumulator", + "docs": [ + "Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate." + ], + "type": "u32" + } + ] + } + }, + { + "name": "DynamicFeeParameterUpdate", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "filter_period", + "docs": [ + "Filter period determine high frequency trading time window." + ], + "type": "u16" + }, + { + "name": "decay_period", + "docs": [ + "Decay period determine when the volatile fee start decay / decrease." + ], + "type": "u16" + }, + { + "name": "reduction_factor", + "docs": [ + "Reduction factor controls the volatile fee rate decrement rate." + ], + "type": "u16" + }, + { + "name": "variable_fee_control", + "docs": [ + "Used to scale the variable fee component depending on the dynamic of the market" + ], + "type": "u32" + }, + { + "name": "max_volatility_accumulator", + "docs": [ + "Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate." + ], + "type": "u32" + } + ] + } + }, + { + "name": "FeeInfo", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "fee_x_per_token_complete", + "type": "u128" + }, + { + "name": "fee_y_per_token_complete", + "type": "u128" + }, + { + "name": "fee_x_pending", + "type": "u64" + }, + { + "name": "fee_y_pending", + "type": "u64" + } + ] + } + }, + { + "name": "FeeParameterUpdate", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "protocol_share", + "type": "u16" + }, + { + "name": "base_factor", + "type": "u16" + } + ] + } + }, + { + "name": "FundReward", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "funder", + "type": "pubkey" + }, + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + }, + { + "name": "GoToABin", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "from_bin_id", + "type": "i32" + }, + { + "name": "to_bin_id", + "type": "i32" + } + ] + } + }, + { + "name": "IncreaseObservation", + "type": { + "kind": "struct", + "fields": [ + { + "name": "oracle", + "type": "pubkey" + }, + { + "name": "new_observation_length", + "type": "u64" + } + ] + } + }, + { + "name": "IncreasePositionLength", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "position", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "length_to_add", + "type": "u16" + }, + { + "name": "side", + "type": "u8" + } + ] + } + }, + { + "name": "InitPermissionPairIx", + "type": { + "kind": "struct", + "fields": [ + { + "name": "active_id", + "type": "i32" + }, + { + "name": "bin_step", + "type": "u16" + }, + { + "name": "base_factor", + "type": "u16" + }, + { + "name": "base_fee_power_factor", + "type": "u8" + }, + { + "name": "activation_type", + "type": "u8" + }, + { + "name": "protocol_share", + "type": "u16" + } + ] + } + }, + { + "name": "InitPresetParametersIx", + "type": { + "kind": "struct", + "fields": [ + { + "name": "index", + "type": "u16" + }, + { + "name": "bin_step", + "docs": [ + "Bin step. Represent the price increment / decrement." + ], + "type": "u16" + }, + { + "name": "base_factor", + "docs": [ + "Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor" + ], + "type": "u16" + }, + { + "name": "filter_period", + "docs": [ + "Filter period determine high frequency trading time window." + ], + "type": "u16" + }, + { + "name": "decay_period", + "docs": [ + "Decay period determine when the volatile fee start decay / decrease." + ], + "type": "u16" + }, + { + "name": "reduction_factor", + "docs": [ + "Reduction factor controls the volatile fee rate decrement rate." + ], + "type": "u16" + }, + { + "name": "variable_fee_control", + "docs": [ + "Used to scale the variable fee component depending on the dynamic of the market" + ], + "type": "u32" + }, + { + "name": "max_volatility_accumulator", + "docs": [ + "Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate." + ], + "type": "u32" + }, + { + "name": "protocol_share", + "docs": [ + "Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee" + ], + "type": "u16" + }, + { + "name": "base_fee_power_factor", + "docs": [ + "Base fee power factor" + ], + "type": "u8" + }, + { + "name": "function_type", + "docs": [ + "function type" + ], + "type": "u8" + } + ] + } + }, + { + "name": "InitializeLbPair2Params", + "type": { + "kind": "struct", + "fields": [ + { + "name": "active_id", + "docs": [ + "Pool price" + ], + "type": "i32" + }, + { + "name": "padding", + "docs": [ + "Padding, for future use" + ], + "type": { + "array": [ + "u8", + 96 + ] + } + } + ] + } + }, + { + "name": "InitializeReward", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "reward_mint", + "type": "pubkey" + }, + { + "name": "funder", + "type": "pubkey" + }, + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "reward_duration", + "type": "u64" + } + ] + } + }, + { + "name": "LbPair", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "parameters", + "type": { + "defined": { + "name": "StaticParameters" + } + } + }, + { + "name": "v_parameters", + "type": { + "defined": { + "name": "VariableParameters" + } + } + }, + { + "name": "bump_seed", + "type": { + "array": [ + "u8", + 1 + ] + } + }, + { + "name": "bin_step_seed", + "docs": [ + "Bin step signer seed" + ], + "type": { + "array": [ + "u8", + 2 + ] + } + }, + { + "name": "pair_type", + "docs": [ + "Type of the pair" + ], + "type": "u8" + }, + { + "name": "active_id", + "docs": [ + "Active bin id" + ], + "type": "i32" + }, + { + "name": "bin_step", + "docs": [ + "Bin step. Represent the price increment / decrement." + ], + "type": "u16" + }, + { + "name": "status", + "docs": [ + "Status of the pair. Check PairStatus enum." + ], + "type": "u8" + }, + { + "name": "require_base_factor_seed", + "docs": [ + "Require base factor seed" + ], + "type": "u8" + }, + { + "name": "base_factor_seed", + "docs": [ + "Base factor seed" + ], + "type": { + "array": [ + "u8", + 2 + ] + } + }, + { + "name": "activation_type", + "docs": [ + "Activation type" + ], + "type": "u8" + }, + { + "name": "creator_pool_on_off_control", + "docs": [ + "Allow pool creator to enable/disable pool with restricted validation. Only applicable for customizable permissionless pair type." + ], + "type": "u8" + }, + { + "name": "token_x_mint", + "docs": [ + "Token X mint" + ], + "type": "pubkey" + }, + { + "name": "token_y_mint", + "docs": [ + "Token Y mint" + ], + "type": "pubkey" + }, + { + "name": "reserve_x", + "docs": [ + "LB token X vault" + ], + "type": "pubkey" + }, + { + "name": "reserve_y", + "docs": [ + "LB token Y vault" + ], + "type": "pubkey" + }, + { + "name": "protocol_fee", + "docs": [ + "Uncollected protocol fee" + ], + "type": { + "defined": { + "name": "ProtocolFee" + } + } + }, + { + "name": "_padding_1", + "docs": [ + "_padding_1, previous Fee owner, BE CAREFUL FOR TOMBSTONE WHEN REUSE !!" + ], + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "reward_infos", + "docs": [ + "Farming reward information" + ], + "type": { + "array": [ + { + "defined": { + "name": "RewardInfo" + } + }, + 2 + ] + } + }, + { + "name": "oracle", + "docs": [ + "Oracle pubkey" + ], + "type": "pubkey" + }, + { + "name": "bin_array_bitmap", + "docs": [ + "Packed initialized bin array state" + ], + "type": { + "array": [ + "u64", + 16 + ] + } + }, + { + "name": "last_updated_at", + "docs": [ + "Last time the pool fee parameter was updated" + ], + "type": "i64" + }, + { + "name": "_padding_2", + "docs": [ + "_padding_2, previous whitelisted_wallet, BE CAREFUL FOR TOMBSTONE WHEN REUSE !!" + ], + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "pre_activation_swap_address", + "docs": [ + "Address allowed to swap when the current point is greater than or equal to the pre-activation point. The pre-activation point is calculated as `activation_point - pre_activation_duration`." + ], + "type": "pubkey" + }, + { + "name": "base_key", + "docs": [ + "Base keypair. Only required for permission pair" + ], + "type": "pubkey" + }, + { + "name": "activation_point", + "docs": [ + "Time point to enable the pair. Only applicable for permission pair." + ], + "type": "u64" + }, + { + "name": "pre_activation_duration", + "docs": [ + "Duration before activation activation_point. Used to calculate pre-activation time point for pre_activation_swap_address" + ], + "type": "u64" + }, + { + "name": "_padding_3", + "docs": [ + "_padding 3 is reclaimed free space from swap_cap_deactivate_point and swap_cap_amount before, BE CAREFUL FOR TOMBSTONE WHEN REUSE !!" + ], + "type": { + "array": [ + "u8", + 8 + ] + } + }, + { + "name": "_padding_4", + "docs": [ + "_padding_4, previous lock_duration, BE CAREFUL FOR TOMBSTONE WHEN REUSE !!" + ], + "type": "u64" + }, + { + "name": "creator", + "docs": [ + "Pool creator" + ], + "type": "pubkey" + }, + { + "name": "token_mint_x_program_flag", + "docs": [ + "token_mint_x_program_flag" + ], + "type": "u8" + }, + { + "name": "token_mint_y_program_flag", + "docs": [ + "token_mint_y_program_flag" + ], + "type": "u8" + }, + { + "name": "version", + "docs": [ + "version to know whether we have reset tombstone fields" + ], + "type": "u8" + }, + { + "name": "_reserved", + "docs": [ + "Reserved space for future use" + ], + "type": { + "array": [ + "u8", + 21 + ] + } + } + ] + } + }, + { + "name": "LbPairCreate", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "bin_step", + "type": "u16" + }, + { + "name": "token_x", + "type": "pubkey" + }, + { + "name": "token_y", + "type": "pubkey" + } + ] + } + }, + { + "name": "LiquidityOneSideParameter", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "docs": [ + "Amount of X token or Y token to deposit" + ], + "type": "u64" + }, + { + "name": "active_id", + "docs": [ + "Active bin that integrator observe off-chain" + ], + "type": "i32" + }, + { + "name": "max_active_bin_slippage", + "docs": [ + "max active bin slippage allowed" + ], + "type": "i32" + }, + { + "name": "bin_liquidity_dist", + "docs": [ + "Liquidity distribution to each bins" + ], + "type": { + "vec": { + "defined": { + "name": "BinLiquidityDistributionByWeight" + } + } + } + } + ] + } + }, + { + "name": "LiquidityParameter", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount_x", + "docs": [ + "Amount of X token to deposit" + ], + "type": "u64" + }, + { + "name": "amount_y", + "docs": [ + "Amount of Y token to deposit" + ], + "type": "u64" + }, + { + "name": "bin_liquidity_dist", + "docs": [ + "Liquidity distribution to each bins" + ], + "type": { + "vec": { + "defined": { + "name": "BinLiquidityDistribution" + } + } + } + } + ] + } + }, + { + "name": "LiquidityParameterByStrategy", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount_x", + "docs": [ + "Amount of X token to deposit" + ], + "type": "u64" + }, + { + "name": "amount_y", + "docs": [ + "Amount of Y token to deposit" + ], + "type": "u64" + }, + { + "name": "active_id", + "docs": [ + "Active bin that integrator observe off-chain" + ], + "type": "i32" + }, + { + "name": "max_active_bin_slippage", + "docs": [ + "max active bin slippage allowed" + ], + "type": "i32" + }, + { + "name": "strategy_parameters", + "docs": [ + "strategy parameters" + ], + "type": { + "defined": { + "name": "StrategyParameters" + } + } + } + ] + } + }, + { + "name": "LiquidityParameterByStrategyOneSide", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "docs": [ + "Amount of X token or Y token to deposit" + ], + "type": "u64" + }, + { + "name": "active_id", + "docs": [ + "Active bin that integrator observe off-chain" + ], + "type": "i32" + }, + { + "name": "max_active_bin_slippage", + "docs": [ + "max active bin slippage allowed" + ], + "type": "i32" + }, + { + "name": "strategy_parameters", + "docs": [ + "strategy parameters" + ], + "type": { + "defined": { + "name": "StrategyParameters" + } + } + } + ] + } + }, + { + "name": "LiquidityParameterByWeight", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount_x", + "docs": [ + "Amount of X token to deposit" + ], + "type": "u64" + }, + { + "name": "amount_y", + "docs": [ + "Amount of Y token to deposit" + ], + "type": "u64" + }, + { + "name": "active_id", + "docs": [ + "Active bin that integrator observe off-chain" + ], + "type": "i32" + }, + { + "name": "max_active_bin_slippage", + "docs": [ + "max active bin slippage allowed" + ], + "type": "i32" + }, + { + "name": "bin_liquidity_dist", + "docs": [ + "Liquidity distribution to each bins" + ], + "type": { + "vec": { + "defined": { + "name": "BinLiquidityDistributionByWeight" + } + } + } + } + ] + } + }, + { + "name": "Operator", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "signer", + "type": "pubkey" + }, + { + "name": "permission", + "type": "u128" + }, + { + "name": "padding", + "type": { + "array": [ + "u64", + 2 + ] + } + } + ] + } + }, + { + "name": "Oracle", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "idx", + "docs": [ + "Index of latest observation" + ], + "type": "u64" + }, + { + "name": "active_size", + "docs": [ + "Size of active sample. Active sample is initialized observation." + ], + "type": "u64" + }, + { + "name": "length", + "docs": [ + "Number of observations" + ], + "type": "u64" + } + ] + } + }, + { + "name": "PairStatus", + "docs": [ + "Pair status. 0 = Enabled, 1 = Disabled. Putting 0 as enabled for backward compatibility." + ], + "repr": { + "kind": "rust" + }, + "type": { + "kind": "enum", + "variants": [ + { + "name": "Enabled" + }, + { + "name": "Disabled" + } + ] + } + }, + { + "name": "PairType", + "docs": [ + "Type of the Pair. 0 = Permissionless, 1 = Permission, 2 = CustomizablePermissionless. Putting 0 as permissionless for backward compatibility." + ], + "repr": { + "kind": "rust" + }, + "type": { + "kind": "enum", + "variants": [ + { + "name": "Permissionless" + }, + { + "name": "Permission" + }, + { + "name": "CustomizablePermissionless" + }, + { + "name": "PermissionlessV2" + } + ] + } + }, + { + "name": "Position", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "docs": [ + "The LB pair of this position" + ], + "type": "pubkey" + }, + { + "name": "owner", + "docs": [ + "Owner of the position. Client rely on this to to fetch their positions." + ], + "type": "pubkey" + }, + { + "name": "liquidity_shares", + "docs": [ + "Liquidity shares of this position in bins (lower_bin_id <-> upper_bin_id). This is the same as LP concept." + ], + "type": { + "array": [ + "u64", + 70 + ] + } + }, + { + "name": "reward_infos", + "docs": [ + "Farming reward information" + ], + "type": { + "array": [ + { + "defined": { + "name": "UserRewardInfo" + } + }, + 70 + ] + } + }, + { + "name": "fee_infos", + "docs": [ + "Swap fee to claim information" + ], + "type": { + "array": [ + { + "defined": { + "name": "FeeInfo" + } + }, + 70 + ] + } + }, + { + "name": "lower_bin_id", + "docs": [ + "Lower bin ID" + ], + "type": "i32" + }, + { + "name": "upper_bin_id", + "docs": [ + "Upper bin ID" + ], + "type": "i32" + }, + { + "name": "last_updated_at", + "docs": [ + "Last updated timestamp" + ], + "type": "i64" + }, + { + "name": "total_claimed_fee_x_amount", + "docs": [ + "Total claimed token fee X" + ], + "type": "u64" + }, + { + "name": "total_claimed_fee_y_amount", + "docs": [ + "Total claimed token fee Y" + ], + "type": "u64" + }, + { + "name": "total_claimed_rewards", + "docs": [ + "Total claimed rewards" + ], + "type": { + "array": [ + "u64", + 2 + ] + } + }, + { + "name": "_reserved", + "docs": [ + "Reserved space for future use" + ], + "type": { + "array": [ + "u8", + 160 + ] + } + } + ] + } + }, + { + "name": "PositionBinData", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "liquidity_share", + "type": "u128" + }, + { + "name": "reward_info", + "type": { + "defined": { + "name": "UserRewardInfo" + } + } + }, + { + "name": "fee_info", + "type": { + "defined": { + "name": "FeeInfo" + } + } + } + ] + } + }, + { + "name": "PositionClose", + "type": { + "kind": "struct", + "fields": [ + { + "name": "position", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + } + ] + } + }, + { + "name": "PositionCreate", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "position", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + } + ] + } + }, + { + "name": "PositionV2", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "docs": [ + "The LB pair of this position" + ], + "type": "pubkey" + }, + { + "name": "owner", + "docs": [ + "Owner of the position. Client rely on this to to fetch their positions." + ], + "type": "pubkey" + }, + { + "name": "liquidity_shares", + "docs": [ + "Liquidity shares of this position in bins (lower_bin_id <-> upper_bin_id). This is the same as LP concept." + ], + "type": { + "array": [ + "u128", + 70 + ] + } + }, + { + "name": "reward_infos", + "docs": [ + "Farming reward information" + ], + "type": { + "array": [ + { + "defined": { + "name": "UserRewardInfo" + } + }, + 70 + ] + } + }, + { + "name": "fee_infos", + "docs": [ + "Swap fee to claim information" + ], + "type": { + "array": [ + { + "defined": { + "name": "FeeInfo" + } + }, + 70 + ] + } + }, + { + "name": "lower_bin_id", + "docs": [ + "Lower bin ID" + ], + "type": "i32" + }, + { + "name": "upper_bin_id", + "docs": [ + "Upper bin ID" + ], + "type": "i32" + }, + { + "name": "last_updated_at", + "docs": [ + "Last updated timestamp" + ], + "type": "i64" + }, + { + "name": "total_claimed_fee_x_amount", + "docs": [ + "Total claimed token fee X" + ], + "type": "u64" + }, + { + "name": "total_claimed_fee_y_amount", + "docs": [ + "Total claimed token fee Y" + ], + "type": "u64" + }, + { + "name": "total_claimed_rewards", + "docs": [ + "Total claimed rewards" + ], + "type": { + "array": [ + "u64", + 2 + ] + } + }, + { + "name": "operator", + "docs": [ + "Operator of position" + ], + "type": "pubkey" + }, + { + "name": "lock_release_point", + "docs": [ + "Time point which the locked liquidity can be withdraw" + ], + "type": "u64" + }, + { + "name": "_padding_0", + "docs": [ + "_padding_0, previous subjected_to_bootstrap_liquidity_locking, BE CAREFUL FOR TOMBSTONE WHEN REUSE !!" + ], + "type": "u8" + }, + { + "name": "fee_owner", + "docs": [ + "Address is able to claim fee in this position, only valid for bootstrap_liquidity_position" + ], + "type": "pubkey" + }, + { + "name": "version", + "docs": [ + "version to know whether we have reset tombstone fields" + ], + "type": "u8" + }, + { + "name": "_reserved", + "docs": [ + "Reserved space for future use" + ], + "type": { + "array": [ + "u8", + 86 + ] + } + } + ] + } + }, + { + "name": "PresetParameter", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bin_step", + "docs": [ + "Bin step. Represent the price increment / decrement." + ], + "type": "u16" + }, + { + "name": "base_factor", + "docs": [ + "Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor" + ], + "type": "u16" + }, + { + "name": "filter_period", + "docs": [ + "Filter period determine high frequency trading time window." + ], + "type": "u16" + }, + { + "name": "decay_period", + "docs": [ + "Decay period determine when the volatile fee start decay / decrease." + ], + "type": "u16" + }, + { + "name": "reduction_factor", + "docs": [ + "Reduction factor controls the volatile fee rate decrement rate." + ], + "type": "u16" + }, + { + "name": "variable_fee_control", + "docs": [ + "Used to scale the variable fee component depending on the dynamic of the market" + ], + "type": "u32" + }, + { + "name": "max_volatility_accumulator", + "docs": [ + "Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate." + ], + "type": "u32" + }, + { + "name": "min_bin_id", + "docs": [ + "Min bin id supported by the pool based on the configured bin step." + ], + "type": "i32" + }, + { + "name": "max_bin_id", + "docs": [ + "Max bin id supported by the pool based on the configured bin step." + ], + "type": "i32" + }, + { + "name": "protocol_share", + "docs": [ + "Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee" + ], + "type": "u16" + } + ] + } + }, + { + "name": "PresetParameter2", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "bin_step", + "docs": [ + "Bin step. Represent the price increment / decrement." + ], + "type": "u16" + }, + { + "name": "base_factor", + "docs": [ + "Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor" + ], + "type": "u16" + }, + { + "name": "filter_period", + "docs": [ + "Filter period determine high frequency trading time window." + ], + "type": "u16" + }, + { + "name": "decay_period", + "docs": [ + "Decay period determine when the volatile fee start decay / decrease." + ], + "type": "u16" + }, + { + "name": "variable_fee_control", + "docs": [ + "Used to scale the variable fee component depending on the dynamic of the market" + ], + "type": "u32" + }, + { + "name": "max_volatility_accumulator", + "docs": [ + "Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate." + ], + "type": "u32" + }, + { + "name": "reduction_factor", + "docs": [ + "Reduction factor controls the volatile fee rate decrement rate." + ], + "type": "u16" + }, + { + "name": "protocol_share", + "docs": [ + "Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee" + ], + "type": "u16" + }, + { + "name": "index", + "docs": [ + "index" + ], + "type": "u16" + }, + { + "name": "base_fee_power_factor", + "docs": [ + "Base fee power factor" + ], + "type": "u8" + }, + { + "name": "function_type", + "docs": [ + "function type, to check whether the pool should have LM farming or other functions in the future, refer FunctionType" + ], + "type": "u8" + }, + { + "name": "padding_1", + "docs": [ + "Padding 1 for future use" + ], + "type": { + "array": [ + "u64", + 20 + ] + } + } + ] + } + }, + { + "name": "ProtocolFee", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount_x", + "type": "u64" + }, + { + "name": "amount_y", + "type": "u64" + } + ] + } + }, + { + "name": "RebalanceLiquidityParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "active_id", + "docs": [ + "active id" + ], + "type": "i32" + }, + { + "name": "max_active_bin_slippage", + "docs": [ + "max active bin slippage allowed" + ], + "type": "u16" + }, + { + "name": "should_claim_fee", + "docs": [ + "a flag to indicate that whether fee should be harvested" + ], + "type": "bool" + }, + { + "name": "should_claim_reward", + "docs": [ + "a flag to indicate that whether rewards should be harvested" + ], + "type": "bool" + }, + { + "name": "min_withdraw_x_amount", + "docs": [ + "threshold for withdraw token x" + ], + "type": "u64" + }, + { + "name": "max_deposit_x_amount", + "docs": [ + "threshold for deposit token x" + ], + "type": "u64" + }, + { + "name": "min_withdraw_y_amount", + "docs": [ + "threshold for withdraw token y" + ], + "type": "u64" + }, + { + "name": "max_deposit_y_amount", + "docs": [ + "threshold for deposit token y" + ], + "type": "u64" + }, + { + "name": "shrink_mode", + "docs": [ + "shrink mode" + ], + "type": "u8" + }, + { + "name": "padding", + "docs": [ + "padding 32 bytes for future usage" + ], + "type": { + "array": [ + "u8", + 31 + ] + } + }, + { + "name": "removes", + "docs": [ + "removes" + ], + "type": { + "vec": { + "defined": { + "name": "RemoveLiquidityParams" + } + } + } + }, + { + "name": "adds", + "docs": [ + "adds" + ], + "type": { + "vec": { + "defined": { + "name": "AddLiquidityParams" + } + } + } + } + ] + } + }, + { + "name": "Rebalancing", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "position", + "type": "pubkey" + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "active_bin_id", + "type": "i32" + }, + { + "name": "x_withdrawn_amount", + "type": "u64" + }, + { + "name": "x_added_amount", + "type": "u64" + }, + { + "name": "y_withdrawn_amount", + "type": "u64" + }, + { + "name": "y_added_amount", + "type": "u64" + }, + { + "name": "x_fee_amount", + "type": "u64" + }, + { + "name": "y_fee_amount", + "type": "u64" + }, + { + "name": "old_min_id", + "type": "i32" + }, + { + "name": "old_max_id", + "type": "i32" + }, + { + "name": "new_min_id", + "type": "i32" + }, + { + "name": "new_max_id", + "type": "i32" + }, + { + "name": "rewards", + "type": { + "array": [ + "u64", + 2 + ] + } + } + ] + } + }, + { + "name": "RemainingAccountsInfo", + "type": { + "kind": "struct", + "fields": [ + { + "name": "slices", + "type": { + "vec": { + "defined": { + "name": "RemainingAccountsSlice" + } + } + } + } + ] + } + }, + { + "name": "RemainingAccountsSlice", + "type": { + "kind": "struct", + "fields": [ + { + "name": "accounts_type", + "type": { + "defined": { + "name": "AccountsType" + } + } + }, + { + "name": "length", + "type": "u8" + } + ] + } + }, + { + "name": "RemoveLiquidity", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "from", + "type": "pubkey" + }, + { + "name": "position", + "type": "pubkey" + }, + { + "name": "amounts", + "type": { + "array": [ + "u64", + 2 + ] + } + }, + { + "name": "active_bin_id", + "type": "i32" + } + ] + } + }, + { + "name": "RemoveLiquidityParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "min_bin_id", + "type": { + "option": "i32" + } + }, + { + "name": "max_bin_id", + "type": { + "option": "i32" + } + }, + { + "name": "bps", + "type": "u16" + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 16 + ] + } + } + ] + } + }, + { + "name": "ResizeSide", + "docs": [ + "Side of resize, 0 for lower and 1 for upper" + ], + "repr": { + "kind": "rust" + }, + "type": { + "kind": "enum", + "variants": [ + { + "name": "Lower" + }, + { + "name": "Upper" + } + ] + } + }, + { + "name": "RewardInfo", + "docs": [ + "Stores the state relevant for tracking liquidity mining rewards" + ], + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "docs": [ + "Reward token mint." + ], + "type": "pubkey" + }, + { + "name": "vault", + "docs": [ + "Reward vault token account." + ], + "type": "pubkey" + }, + { + "name": "funder", + "docs": [ + "Authority account that allows to fund rewards" + ], + "type": "pubkey" + }, + { + "name": "reward_duration", + "docs": [ + "LM reward duration in seconds." + ], + "type": "u64" + }, + { + "name": "reward_duration_end", + "docs": [ + "LM reward duration end time." + ], + "type": "u64" + }, + { + "name": "reward_rate", + "docs": [ + "LM reward rate" + ], + "type": "u128" + }, + { + "name": "last_update_time", + "docs": [ + "The last time reward states were updated." + ], + "type": "u64" + }, + { + "name": "cumulative_seconds_with_empty_liquidity_reward", + "docs": [ + "Accumulated seconds where when farm distribute rewards, but the bin is empty. The reward will be accumulated for next reward time window." + ], + "type": "u64" + } + ] + } + }, + { + "name": "Rounding", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Up" + }, + { + "name": "Down" + } + ] + } + }, + { + "name": "StaticParameters", + "docs": [ + "Parameter that set by the protocol" + ], + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "base_factor", + "docs": [ + "Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor" + ], + "type": "u16" + }, + { + "name": "filter_period", + "docs": [ + "Filter period determine high frequency trading time window." + ], + "type": "u16" + }, + { + "name": "decay_period", + "docs": [ + "Decay period determine when the volatile fee start decay / decrease." + ], + "type": "u16" + }, + { + "name": "reduction_factor", + "docs": [ + "Reduction factor controls the volatile fee rate decrement rate." + ], + "type": "u16" + }, + { + "name": "variable_fee_control", + "docs": [ + "Used to scale the variable fee component depending on the dynamic of the market" + ], + "type": "u32" + }, + { + "name": "max_volatility_accumulator", + "docs": [ + "Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate." + ], + "type": "u32" + }, + { + "name": "min_bin_id", + "docs": [ + "Min bin id supported by the pool based on the configured bin step." + ], + "type": "i32" + }, + { + "name": "max_bin_id", + "docs": [ + "Max bin id supported by the pool based on the configured bin step." + ], + "type": "i32" + }, + { + "name": "protocol_share", + "docs": [ + "Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee" + ], + "type": "u16" + }, + { + "name": "base_fee_power_factor", + "docs": [ + "Base fee power factor" + ], + "type": "u8" + }, + { + "name": "function_type", + "docs": [ + "function type" + ], + "type": "u8" + }, + { + "name": "_padding", + "docs": [ + "Padding for bytemuck safe alignment" + ], + "type": { + "array": [ + "u8", + 4 + ] + } + } + ] + } + }, + { + "name": "StrategyParameters", + "type": { + "kind": "struct", + "fields": [ + { + "name": "min_bin_id", + "docs": [ + "min bin id" + ], + "type": "i32" + }, + { + "name": "max_bin_id", + "docs": [ + "max bin id" + ], + "type": "i32" + }, + { + "name": "strategy_type", + "docs": [ + "strategy type" + ], + "type": { + "defined": { + "name": "StrategyType" + } + } + }, + { + "name": "parameteres", + "docs": [ + "parameters" + ], + "type": { + "array": [ + "u8", + 64 + ] + } + } + ] + } + }, + { + "name": "StrategyType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "SpotOneSide" + }, + { + "name": "CurveOneSide" + }, + { + "name": "BidAskOneSide" + }, + { + "name": "SpotBalanced" + }, + { + "name": "CurveBalanced" + }, + { + "name": "BidAskBalanced" + }, + { + "name": "SpotImBalanced" + }, + { + "name": "CurveImBalanced" + }, + { + "name": "BidAskImBalanced" + } + ] + } + }, + { + "name": "Swap", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "from", + "type": "pubkey" + }, + { + "name": "start_bin_id", + "type": "i32" + }, + { + "name": "end_bin_id", + "type": "i32" + }, + { + "name": "amount_in", + "type": "u64" + }, + { + "name": "amount_out", + "type": "u64" + }, + { + "name": "swap_for_y", + "type": "bool" + }, + { + "name": "fee", + "type": "u64" + }, + { + "name": "protocol_fee", + "type": "u64" + }, + { + "name": "fee_bps", + "type": "u128" + }, + { + "name": "host_fee", + "type": "u64" + } + ] + } + }, + { + "name": "TokenBadge", + "docs": [ + "Parameter that set by the protocol" + ], + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "token_mint", + "docs": [ + "token mint" + ], + "type": "pubkey" + }, + { + "name": "_padding", + "docs": [ + "Reserve" + ], + "type": { + "array": [ + "u8", + 128 + ] + } + } + ] + } + }, + { + "name": "TokenProgramFlags", + "repr": { + "kind": "rust" + }, + "type": { + "kind": "enum", + "variants": [ + { + "name": "TokenProgram" + }, + { + "name": "TokenProgram2022" + } + ] + } + }, + { + "name": "UpdatePositionLockReleasePoint", + "type": { + "kind": "struct", + "fields": [ + { + "name": "position", + "type": "pubkey" + }, + { + "name": "current_point", + "type": "u64" + }, + { + "name": "new_lock_release_point", + "type": "u64" + }, + { + "name": "old_lock_release_point", + "type": "u64" + }, + { + "name": "sender", + "type": "pubkey" + } + ] + } + }, + { + "name": "UpdatePositionOperator", + "type": { + "kind": "struct", + "fields": [ + { + "name": "position", + "type": "pubkey" + }, + { + "name": "old_operator", + "type": "pubkey" + }, + { + "name": "new_operator", + "type": "pubkey" + } + ] + } + }, + { + "name": "UpdateRewardDuration", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "old_reward_duration", + "type": "u64" + }, + { + "name": "new_reward_duration", + "type": "u64" + } + ] + } + }, + { + "name": "UpdateRewardFunder", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "reward_index", + "type": "u64" + }, + { + "name": "old_funder", + "type": "pubkey" + }, + { + "name": "new_funder", + "type": "pubkey" + } + ] + } + }, + { + "name": "UserRewardInfo", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "reward_per_token_completes", + "type": { + "array": [ + "u128", + 2 + ] + } + }, + { + "name": "reward_pendings", + "type": { + "array": [ + "u64", + 2 + ] + } + } + ] + } + }, + { + "name": "VariableParameters", + "docs": [ + "Parameters that changes based on dynamic of the market" + ], + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "volatility_accumulator", + "docs": [ + "Volatility accumulator measure the number of bin crossed since reference bin ID. Normally (without filter period taken into consideration), reference bin ID is the active bin of last swap.", + "It affects the variable fee rate" + ], + "type": "u32" + }, + { + "name": "volatility_reference", + "docs": [ + "Volatility reference is decayed volatility accumulator. It is always <= volatility_accumulator" + ], + "type": "u32" + }, + { + "name": "index_reference", + "docs": [ + "Active bin id of last swap." + ], + "type": "i32" + }, + { + "name": "_padding", + "docs": [ + "Padding for bytemuck safe alignment" + ], + "type": { + "array": [ + "u8", + 4 + ] + } + }, + { + "name": "last_update_timestamp", + "docs": [ + "Last timestamp the variable parameters was updated" + ], + "type": "i64" + }, + { + "name": "_padding_1", + "docs": [ + "Padding for bytemuck safe alignment" + ], + "type": { + "array": [ + "u8", + 8 + ] + } + } + ] + } + }, + { + "name": "WithdrawIneligibleReward", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lb_pair", + "type": "pubkey" + }, + { + "name": "reward_mint", + "type": "pubkey" + }, + { + "name": "amount", + "type": "u64" + } + ] + } + } + ], + "constants": [ + { + "name": "BASIS_POINT_MAX", + "type": "i32", + "value": "10000" + }, + { + "name": "BIN_ARRAY", + "type": "bytes", + "value": "[98, 105, 110, 95, 97, 114, 114, 97, 121]" + }, + { + "name": "BIN_ARRAY_BITMAP_SEED", + "type": "bytes", + "value": "[98, 105, 116, 109, 97, 112]" + }, + { + "name": "BIN_ARRAY_BITMAP_SIZE", + "type": "i32", + "value": "512" + }, + { + "name": "CLAIM_PROTOCOL_FEE_OPERATOR", + "type": "bytes", + "value": "[99, 102, 95, 111, 112, 101, 114, 97, 116, 111, 114]" + }, + { + "name": "DEFAULT_BIN_PER_POSITION", + "type": "u64", + "value": "70" + }, + { + "name": "EXTENSION_BINARRAY_BITMAP_SIZE", + "type": "u64", + "value": "12" + }, + { + "name": "FEE_PRECISION", + "type": "u64", + "value": "1000000000" + }, + { + "name": "HOST_FEE_BPS", + "docs": [ + "Host fee. 20%" + ], + "type": "u16", + "value": "2000" + }, + { + "name": "ILM_PROTOCOL_SHARE", + "type": "u16", + "value": "2000" + }, + { + "name": "MAX_BASE_FEE", + "docs": [ + "Maximum base fee, base_fee / 10^9 = fee_in_percentage" + ], + "type": "u128", + "value": "100000000" + }, + { + "name": "MAX_BIN_ID_PER_BIN_STEP", + "docs": [ + "Maximum bin ID per bin step. Computed based on 1 bps. Used for bin id bound estimation." + ], + "type": "i32", + "value": "351639" + }, + { + "name": "MAX_BIN_PER_ARRAY", + "type": "u64", + "value": "70" + }, + { + "name": "MAX_BIN_STEP", + "docs": [ + "Maximum bin step" + ], + "type": "u16", + "value": "400" + }, + { + "name": "MAX_FEE_RATE", + "docs": [ + "Maximum fee rate. 10%" + ], + "type": "u64", + "value": "100000000" + }, + { + "name": "MAX_PROTOCOL_SHARE", + "docs": [ + "Maximum protocol share of the fee. 25%" + ], + "type": "u16", + "value": "2500" + }, + { + "name": "MAX_RESIZE_LENGTH", + "type": "u64", + "value": "91" + }, + { + "name": "MAX_REWARD_BIN_SPLIT", + "type": "u64", + "value": "15" + }, + { + "name": "MAX_REWARD_DURATION", + "type": "u64", + "value": "31536000" + }, + { + "name": "MINIMUM_LIQUIDITY", + "type": "u128", + "value": "1000000" + }, + { + "name": "MIN_BASE_FEE", + "docs": [ + "Minimum base fee" + ], + "type": "u128", + "value": "100000" + }, + { + "name": "MIN_REWARD_DURATION", + "type": "u64", + "value": "1" + }, + { + "name": "NUM_REWARDS", + "type": "u64", + "value": "2" + }, + { + "name": "OPERATOR_PREFIX", + "type": "bytes", + "value": "[111, 112, 101, 114, 97, 116, 111, 114]" + }, + { + "name": "ORACLE", + "type": "bytes", + "value": "[111, 114, 97, 99, 108, 101]" + }, + { + "name": "POSITION", + "type": "bytes", + "value": "[112, 111, 115, 105, 116, 105, 111, 110]" + }, + { + "name": "POSITION_MAX_LENGTH", + "type": "u64", + "value": "1400" + }, + { + "name": "PRESET_PARAMETER", + "type": "bytes", + "value": "[112, 114, 101, 115, 101, 116, 95, 112, 97, 114, 97, 109, 101, 116, 101, 114]" + }, + { + "name": "PRESET_PARAMETER2", + "type": "bytes", + "value": "[112, 114, 101, 115, 101, 116, 95, 112, 97, 114, 97, 109, 101, 116, 101, 114, 50]" + }, + { + "name": "PROTOCOL_SHARE", + "type": "u16", + "value": "500" + } + ] +} \ No newline at end of file diff --git a/hyperstack-idl/tests/fixtures/ore.json b/hyperstack-idl/tests/fixtures/ore.json new file mode 100644 index 00000000..e4485200 --- /dev/null +++ b/hyperstack-idl/tests/fixtures/ore.json @@ -0,0 +1,1678 @@ +{ + "version": "3.7.22", + "name": "ore", + "instructions": [ + { + "name": "automate", + "discriminant": { + "type": "u8", + "value": 0 + }, + "docs": [ + "Configures or closes a miner automation account.", + "Automation PDA seeds: [\"automation\", signer].", + "Miner PDA seeds: [\"miner\", signer]." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "automation", + "isMut": true, + "isSigner": false + }, + { + "name": "executor", + "isMut": false, + "isSigner": false + }, + { + "name": "miner", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "deposit", + "type": "u64" + }, + { + "name": "fee", + "type": "u64" + }, + { + "name": "mask", + "type": "u64" + }, + { + "name": "strategy", + "type": "u8" + }, + { + "name": "reload", + "type": "u64" + } + ] + }, + { + "name": "checkpoint", + "discriminant": { + "type": "u8", + "value": 2 + }, + "docs": [ + "Settles miner rewards for a completed round.", + "Treasury PDA seeds: [\"treasury\"]." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "board", + "isMut": false, + "isSigner": false + }, + { + "name": "miner", + "isMut": true, + "isSigner": false + }, + { + "name": "round", + "isMut": true, + "isSigner": false + }, + { + "name": "treasury", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "claimSol", + "discriminant": { + "type": "u8", + "value": 3 + }, + "docs": [ + "Claims SOL rewards from the miner account." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "miner", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "claimOre", + "discriminant": { + "type": "u8", + "value": 4 + }, + "docs": [ + "Claims ORE token rewards from the treasury vault." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "miner", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "address": "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp" + }, + { + "name": "recipient", + "isMut": true, + "isSigner": false + }, + { + "name": "treasury", + "isMut": true, + "isSigner": false + }, + { + "name": "treasuryTokens", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "associatedTokenProgram", + "isMut": false, + "isSigner": false, + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + } + ], + "args": [] + }, + { + "name": "close", + "discriminant": { + "type": "u8", + "value": 5 + }, + "docs": [ + "Closes an expired round account and returns rent to the payer.", + "Round PDA seeds: [\"round\", round_id].", + "Treasury PDA seeds: [\"treasury\"]." + ], + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "board", + "isMut": true, + "isSigner": false + }, + { + "name": "rentPayer", + "isMut": true, + "isSigner": false + }, + { + "name": "round", + "isMut": true, + "isSigner": false + }, + { + "name": "treasury", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "deploy", + "discriminant": { + "type": "u8", + "value": 6 + }, + "docs": [ + "Deploys SOL to selected squares for the current round.", + "Automation PDA seeds: [\"automation\", authority].", + "Config PDA seeds: [\"config\"].", + "Miner PDA seeds: [\"miner\", authority].", + "Round PDA seeds: [\"round\", board.round_id]." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "authority", + "isMut": true, + "isSigner": false + }, + { + "name": "automation", + "isMut": true, + "isSigner": false + }, + { + "name": "board", + "isMut": true, + "isSigner": false + }, + { + "name": "config", + "isMut": true, + "isSigner": false + }, + { + "name": "miner", + "isMut": true, + "isSigner": false + }, + { + "name": "round", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + }, + { + "name": "oreProgram", + "isMut": false, + "isSigner": false, + "address": "oreV3EG1i9BEgiAJ8b177Z2S2rMarzak4NMv1kULvWv" + }, + { + "name": "entropyVar", + "isMut": true, + "isSigner": false + }, + { + "name": "entropyProgram", + "isMut": false, + "isSigner": false, + "address": "3jSkUuYBoJzQPMEzTvkDFXCZUBksPamrVhrnHR9igu2X" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "squares", + "type": "u32" + } + ] + }, + { + "name": "log", + "discriminant": { + "type": "u8", + "value": 8 + }, + "docs": [ + "Emits an arbitrary log message from the board PDA.", + "Bytes following the discriminator are logged verbatim." + ], + "accounts": [ + { + "name": "board", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "reset", + "discriminant": { + "type": "u8", + "value": 9 + }, + "docs": [ + "Finalizes the current round, mints rewards, and opens the next round.", + "Board PDA seeds: [\"board\"].", + "Treasury PDA seeds: [\"treasury\"].", + "Round PDA seeds: [\"round\", board.round_id] and [\"round\", board.round_id + 1]." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "board", + "isMut": true, + "isSigner": false + }, + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "feeCollector", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false, + "address": "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp" + }, + { + "name": "round", + "isMut": true, + "isSigner": false + }, + { + "name": "roundNext", + "isMut": true, + "isSigner": false + }, + { + "name": "topMiner", + "isMut": false, + "isSigner": false + }, + { + "name": "treasury", + "isMut": true, + "isSigner": false + }, + { + "name": "treasuryTokens", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "oreProgram", + "isMut": false, + "isSigner": false, + "address": "oreV3EG1i9BEgiAJ8b177Z2S2rMarzak4NMv1kULvWv" + }, + { + "name": "slotHashesSysvar", + "isMut": false, + "isSigner": false, + "address": "SysvarS1otHashes111111111111111111111111111" + }, + { + "name": "entropyVar", + "isMut": false, + "isSigner": false + }, + { + "name": "entropyProgram", + "isMut": false, + "isSigner": false, + "address": "3jSkUuYBoJzQPMEzTvkDFXCZUBksPamrVhrnHR9igu2X" + }, + { + "name": "mintAuthority", + "isMut": true, + "isSigner": false + }, + { + "name": "oreMintProgram", + "isMut": false, + "isSigner": false, + "address": "mintzxW6Kckmeyh1h6Zfdj9QcYgCzhPSGiC8ChZ6fCx" + } + ], + "args": [] + }, + { + "name": "deposit", + "discriminant": { + "type": "u8", + "value": 10 + }, + "docs": [ + "Deposits ORE into a staking account.", + "Stake PDA seeds: [\"stake\", signer]." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "address": "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp" + }, + { + "name": "sender", + "isMut": true, + "isSigner": false + }, + { + "name": "stake", + "isMut": true, + "isSigner": false + }, + { + "name": "stakeTokens", + "isMut": true, + "isSigner": false + }, + { + "name": "treasury", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "associatedTokenProgram", + "isMut": false, + "isSigner": false, + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "compound_fee", + "type": "u64" + } + ] + }, + { + "name": "withdraw", + "discriminant": { + "type": "u8", + "value": 11 + }, + "docs": [ + "Withdraws ORE from a staking account.", + "Stake PDA seeds: [\"stake\", signer]." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "address": "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp" + }, + { + "name": "recipient", + "isMut": true, + "isSigner": false + }, + { + "name": "stake", + "isMut": true, + "isSigner": false + }, + { + "name": "stakeTokens", + "isMut": true, + "isSigner": false + }, + { + "name": "treasury", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "associatedTokenProgram", + "isMut": false, + "isSigner": false, + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "claimYield", + "discriminant": { + "type": "u8", + "value": 12 + }, + "docs": [ + "Claims accrued staking rewards.", + "Stake PDA seeds: [\"stake\", signer].", + "Treasury PDA seeds: [\"treasury\"]." + ], + "accounts": [ + { + "name": "signer", + "isMut": true, + "isSigner": true + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "address": "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp" + }, + { + "name": "recipient", + "isMut": true, + "isSigner": false + }, + { + "name": "stake", + "isMut": true, + "isSigner": false + }, + { + "name": "treasury", + "isMut": true, + "isSigner": false + }, + { + "name": "treasuryTokens", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "associatedTokenProgram", + "isMut": false, + "isSigner": false, + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "buyback", + "discriminant": { + "type": "u8", + "value": 13 + }, + "docs": [ + "Buys back ORE from the market." + ], + "accounts": [], + "args": [] + }, + { + "name": "bury", + "discriminant": { + "type": "u8", + "value": 24 + }, + "docs": [ + "Swaps vaulted SOL for ORE via a CPI and burns the proceeds.", + "Treasury PDA seeds: [\"treasury\"].", + "Additional swap accounts are passed through as remaining accounts." + ], + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "board", + "isMut": true, + "isSigner": false + }, + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "address": "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp" + }, + { + "name": "treasury", + "isMut": true, + "isSigner": false + }, + { + "name": "treasuryOre", + "isMut": true, + "isSigner": false + }, + { + "name": "treasurySol", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "oreProgram", + "isMut": false, + "isSigner": false, + "address": "oreV3EG1i9BEgiAJ8b177Z2S2rMarzak4NMv1kULvWv" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "wrap", + "discriminant": { + "type": "u8", + "value": 14 + }, + "docs": [ + "Wraps SOL held by the treasury into WSOL for swapping.", + "Treasury PDA seeds: [\"treasury\"]." + ], + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "treasury", + "isMut": true, + "isSigner": false + }, + { + "name": "treasurySol", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, + { + "name": "setAdmin", + "discriminant": { + "type": "u8", + "value": 15 + }, + "docs": [ + "Updates the program admin address." + ], + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "config", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "admin", + "type": "publicKey" + } + ] + }, + { + "name": "newVar", + "discriminant": { + "type": "u8", + "value": 19 + }, + "docs": [ + "Creates a new entropy var account through the entropy program." + ], + "accounts": [ + { + "name": "signer", + "isMut": false, + "isSigner": true + }, + { + "name": "board", + "isMut": true, + "isSigner": false + }, + { + "name": "config", + "isMut": true, + "isSigner": false + }, + { + "name": "provider", + "isMut": false, + "isSigner": false + }, + { + "name": "var", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "address": "11111111111111111111111111111111" + }, + { + "name": "entropyProgram", + "isMut": false, + "isSigner": false, + "address": "3jSkUuYBoJzQPMEzTvkDFXCZUBksPamrVhrnHR9igu2X" + } + ], + "args": [ + { + "name": "id", + "type": "u64" + }, + { + "name": "commit", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "samples", + "type": "u64" + } + ] + }, + { + "name": "reloadSol", + "discriminant": { + "type": "u8", + "value": 21 + }, + "docs": [ + "Reloads SOL into the automation account." + ], + "accounts": [], + "args": [] + }, + { + "name": "compoundYield", + "discriminant": { + "type": "u8", + "value": 22 + }, + "docs": [ + "Compounds staking yield." + ], + "accounts": [], + "args": [] + }, + { + "name": "liq", + "discriminant": { + "type": "u8", + "value": 25 + }, + "docs": [ + "Liquidation instruction." + ], + "accounts": [], + "args": [] + } + ], + "accounts": [ + { + "name": "Automation", + "discriminator": [ + 100, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "docs": [ + "Automation parameters for automated mining deployments." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "balance", + "type": "u64" + }, + { + "name": "executor", + "type": "publicKey" + }, + { + "name": "fee", + "type": "u64" + }, + { + "name": "strategy", + "type": "u64" + }, + { + "name": "mask", + "type": "u64" + }, + { + "name": "reload", + "type": "u64" + } + ] + } + }, + { + "name": "Board", + "discriminator": [ + 105, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "docs": [ + "Global round tracking for the mining game." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "round_id", + "type": "u64" + }, + { + "name": "start_slot", + "type": "u64" + }, + { + "name": "end_slot", + "type": "u64" + }, + { + "name": "epoch_id", + "type": "u64" + } + ] + } + }, + { + "name": "Config", + "discriminator": [ + 101, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "docs": [ + "Program configuration state." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "admin", + "type": "publicKey" + }, + { + "name": "buffer_a", + "type": "publicKey" + }, + { + "name": "buffer_b", + "type": "publicKey" + }, + { + "name": "buffer_c", + "type": "publicKey" + }, + { + "name": "buffer_d", + "type": "publicKey" + }, + { + "name": "buffer_e", + "type": "u64" + } + ] + } + }, + { + "name": "Miner", + "discriminator": [ + 103, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "docs": [ + "Tracks a miner's deployed SOL and reward balances." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "deployed", + "type": { + "array": [ + "u64", + 25 + ] + } + }, + { + "name": "cumulative", + "type": { + "array": [ + "u64", + 25 + ] + } + }, + { + "name": "checkpoint_fee", + "type": "u64" + }, + { + "name": "checkpoint_id", + "type": "u64" + }, + { + "name": "last_claim_ore_at", + "type": "i64" + }, + { + "name": "last_claim_sol_at", + "type": "i64" + }, + { + "name": "rewards_factor", + "type": { + "defined": "Numeric" + } + }, + { + "name": "rewards_sol", + "type": "u64" + }, + { + "name": "rewards_ore", + "type": "u64" + }, + { + "name": "refined_ore", + "type": "u64" + }, + { + "name": "round_id", + "type": "u64" + }, + { + "name": "lifetime_rewards_sol", + "type": "u64" + }, + { + "name": "lifetime_rewards_ore", + "type": "u64" + }, + { + "name": "lifetime_deployed", + "type": "u64" + } + ] + } + }, + { + "name": "Round", + "discriminator": [ + 109, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "docs": [ + "State for a single mining round." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "id", + "type": "u64" + }, + { + "name": "deployed", + "type": { + "array": [ + "u64", + 25 + ] + } + }, + { + "name": "slot_hash", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "count", + "type": { + "array": [ + "u64", + 25 + ] + } + }, + { + "name": "expires_at", + "type": "u64" + }, + { + "name": "motherlode", + "type": "u64" + }, + { + "name": "rent_payer", + "type": "publicKey" + }, + { + "name": "top_miner", + "type": "publicKey" + }, + { + "name": "top_miner_reward", + "type": "u64" + }, + { + "name": "total_deployed", + "type": "u64" + }, + { + "name": "total_miners", + "type": "u64" + }, + { + "name": "total_vaulted", + "type": "u64" + }, + { + "name": "total_winnings", + "type": "u64" + } + ] + } + }, + { + "name": "Stake", + "discriminator": [ + 108, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "docs": [ + "State for a staking participant." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "authority", + "type": "publicKey" + }, + { + "name": "balance", + "type": "u64" + }, + { + "name": "buffer_a", + "type": "u64" + }, + { + "name": "buffer_b", + "type": "u64" + }, + { + "name": "buffer_c", + "type": "u64" + }, + { + "name": "buffer_d", + "type": "u64" + }, + { + "name": "compound_fee_reserve", + "type": "u64" + }, + { + "name": "last_claim_at", + "type": "i64" + }, + { + "name": "last_deposit_at", + "type": "i64" + }, + { + "name": "last_withdraw_at", + "type": "i64" + }, + { + "name": "rewards_factor", + "type": { + "defined": "Numeric" + } + }, + { + "name": "rewards", + "type": "u64" + }, + { + "name": "lifetime_rewards", + "type": "u64" + }, + { + "name": "buffer_f", + "type": "u64" + } + ] + } + }, + { + "name": "Treasury", + "discriminator": [ + 104, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "docs": [ + "Singleton treasury account tracking protocol balances." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "balance", + "type": "u64" + }, + { + "name": "buffer_a", + "type": "u64" + }, + { + "name": "motherlode", + "type": "u64" + }, + { + "name": "miner_rewards_factor", + "type": { + "defined": "Numeric" + } + }, + { + "name": "stake_rewards_factor", + "type": { + "defined": "Numeric" + } + }, + { + "name": "buffer_b", + "type": "u64" + }, + { + "name": "total_refined", + "type": "u64" + }, + { + "name": "total_staked", + "type": "u64" + }, + { + "name": "total_unclaimed", + "type": "u64" + } + ] + } + } + ], + "types": [ + { + "name": "Numeric", + "docs": [ + "Fixed-point helper backed by I80F48 from the steel crate." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "bits", + "type": { + "array": [ + "u8", + 16 + ] + } + } + ] + } + } + ], + "events": [ + { + "name": "ResetEvent", + "discriminator": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "fields": [ + { + "name": "disc", + "type": "u64", + "index": false + }, + { + "name": "round_id", + "type": "u64", + "index": false + }, + { + "name": "start_slot", + "type": "u64", + "index": false + }, + { + "name": "end_slot", + "type": "u64", + "index": false + }, + { + "name": "winning_square", + "type": "u64", + "index": false + }, + { + "name": "top_miner", + "type": "publicKey", + "index": false + }, + { + "name": "num_winners", + "type": "u64", + "index": false + }, + { + "name": "motherlode", + "type": "u64", + "index": false + }, + { + "name": "total_deployed", + "type": "u64", + "index": false + }, + { + "name": "total_vaulted", + "type": "u64", + "index": false + }, + { + "name": "total_winnings", + "type": "u64", + "index": false + }, + { + "name": "total_minted", + "type": "u64", + "index": false + }, + { + "name": "ts", + "type": "i64", + "index": false + }, + { + "name": "rng", + "type": "u64", + "index": false + }, + { + "name": "deployed_winning_square", + "type": "u64", + "index": false + } + ] + }, + { + "name": "BuryEvent", + "discriminator": [ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "fields": [ + { + "name": "disc", + "type": "u64", + "index": false + }, + { + "name": "ore_buried", + "type": "u64", + "index": false + }, + { + "name": "ore_shared", + "type": "u64", + "index": false + }, + { + "name": "sol_amount", + "type": "u64", + "index": false + }, + { + "name": "new_circulating_supply", + "type": "u64", + "index": false + }, + { + "name": "ts", + "type": "i64", + "index": false + } + ] + }, + { + "name": "DeployEvent", + "discriminator": [ + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "fields": [ + { + "name": "disc", + "type": "u64", + "index": false + }, + { + "name": "authority", + "type": "publicKey", + "index": false + }, + { + "name": "amount", + "type": "u64", + "index": false + }, + { + "name": "mask", + "type": "u64", + "index": false + }, + { + "name": "round_id", + "type": "u64", + "index": false + }, + { + "name": "signer", + "type": "publicKey", + "index": false + }, + { + "name": "strategy", + "type": "u64", + "index": false + }, + { + "name": "total_squares", + "type": "u64", + "index": false + }, + { + "name": "ts", + "type": "i64", + "index": false + } + ] + }, + { + "name": "LiqEvent", + "discriminator": [ + 3, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "fields": [ + { + "name": "disc", + "type": "u64", + "index": false + }, + { + "name": "sol_amount", + "type": "u64", + "index": false + }, + { + "name": "recipient", + "type": "publicKey", + "index": false + }, + { + "name": "ts", + "type": "i64", + "index": false + } + ] + } + ], + "errors": [ + { + "code": 0, + "name": "AmountTooSmall", + "msg": "Amount too small" + }, + { + "code": 1, + "name": "NotAuthorized", + "msg": "Not authorized" + } + ], + "metadata": { + "address": "oreV3EG1i9BEgiAJ8b177Z2S2rMarzak4NMv1kULvWv", + "name": "ore", + "version": "0.1.0", + "spec": "0.1.0", + "origin": "steel" + } +} diff --git a/hyperstack-idl/tests/fixtures/pump.json b/hyperstack-idl/tests/fixtures/pump.json new file mode 100644 index 00000000..8a441abf --- /dev/null +++ b/hyperstack-idl/tests/fixtures/pump.json @@ -0,0 +1,1391 @@ +{ + "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "metadata": { + "name": "pump", + "version": "0.1.0", + "spec": "0.1.0", + "description": "Pump.fun Anchor IDL & CPI Library", + "repository": "https://github.com/s6nqou/pump-anchor" + }, + "instructions": [ + { + "name": "buy", + "docs": [ + "Buys tokens from a bonding curve." + ], + "discriminator": [ + 102, + 6, + 61, + 18, + 1, + 218, + 235, + 234 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 215, + 101, + 161, + 147, + 217, + 203, + 225, + 70, + 206, + 235, + 121, + 172, + 28, + 180, + 133, + 237, + 95, + 91, + 55, + 145, + 58, + 140, + 245, + 133, + 126, + 255, + 0, + 169 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "associated_user", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "rent", + "address": "SysvarRent111111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "max_sol_cost", + "type": "u64" + } + ] + }, + { + "name": "create", + "docs": [ + "Creates a new coin and bonding curve." + ], + "discriminator": [ + 24, + 30, + 200, + 40, + 5, + 28, + 7, + 119 + ], + "accounts": [ + { + "name": "mint", + "writable": true + }, + { + "name": "mint_authority" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 215, + 101, + 161, + 147, + 217, + 203, + 225, + 70, + 206, + 235, + 121, + 172, + 28, + 180, + 133, + 237, + 95, + 91, + 55, + 145, + 58, + 140, + 245, + 133, + 126, + 255, + 0, + 169 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "mpl_token_metadata", + "address": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" + }, + { + "name": "metadata", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "rent", + "address": "SysvarRent111111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "uri", + "type": "string" + } + ] + }, + { + "name": "initialize", + "docs": [ + "Creates the global state." + ], + "discriminator": [ + 175, + 175, + 109, + 31, + 13, + 152, + 155, + 237 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [] + }, + { + "name": "sell", + "docs": [ + "Sells tokens into a bonding curve." + ], + "discriminator": [ + 51, + 230, + 133, + 164, + 1, + 127, + 131, + 173 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "fee_recipient", + "writable": true + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 215, + 101, + 161, + 147, + 217, + 203, + 225, + 70, + 206, + 235, + 121, + 172, + 28, + 180, + 133, + 237, + 95, + 91, + 55, + 145, + 58, + 140, + 245, + 133, + 126, + 255, + 0, + 169 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "associated_user", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "min_sol_output", + "type": "u64" + } + ] + }, + { + "name": "set_params", + "docs": [ + "Sets the global state parameters." + ], + "discriminator": [ + 27, + 234, + 178, + 52, + 147, + 2, + 187, + 141 + ], + "accounts": [ + { + "name": "global", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "fee_recipient", + "type": "pubkey" + }, + { + "name": "initial_virtual_token_reserves", + "type": "u64" + }, + { + "name": "initial_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "initial_real_token_reserves", + "type": "u64" + }, + { + "name": "token_total_suply", + "type": "u64" + }, + { + "name": "fee_basis_points", + "type": "u64" + } + ] + }, + { + "name": "withdraw", + "docs": [ + "Allows the admin to withdraw liquidity for a migration once the bonding curve completes" + ], + "discriminator": [ + 183, + 18, + 70, + 156, + 148, + 109, + 161, + 34 + ], + "accounts": [ + { + "name": "global", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 103, + 108, + 111, + 98, + 97, + 108 + ] + } + ] + } + }, + { + "name": "mint" + }, + { + "name": "bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 111, + 110, + 100, + 105, + 110, + 103, + 45, + 99, + 117, + 114, + 118, + 101 + ] + }, + { + "kind": "account", + "path": "mint" + } + ] + } + }, + { + "name": "associated_bonding_curve", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "bonding_curve" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 215, + 101, + 161, + 147, + 217, + 203, + 225, + 70, + 206, + 235, + 121, + 172, + 28, + 180, + 133, + 237, + 95, + 91, + 55, + 145, + 58, + 140, + 245, + 133, + 126, + 255, + 0, + 169 + ] + }, + { + "kind": "account", + "path": "mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "associated_user", + "writable": true + }, + { + "name": "user", + "writable": true, + "signer": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "rent", + "address": "SysvarRent111111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "BondingCurve", + "discriminator": [ + 23, + 183, + 248, + 55, + 96, + 216, + 172, + 96 + ] + }, + { + "name": "Global", + "discriminator": [ + 167, + 232, + 232, + 177, + 200, + 108, + 114, + 127 + ] + } + ], + "events": [ + { + "name": "CompleteEvent", + "discriminator": [ + 95, + 114, + 97, + 156, + 212, + 46, + 152, + 8 + ] + }, + { + "name": "CreateEvent", + "discriminator": [ + 27, + 114, + 169, + 77, + 222, + 235, + 99, + 118 + ] + }, + { + "name": "SetParamsEvent", + "discriminator": [ + 223, + 195, + 159, + 246, + 62, + 48, + 143, + 131 + ] + }, + { + "name": "TradeEvent", + "discriminator": [ + 189, + 219, + 127, + 211, + 78, + 230, + 97, + 238 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "NotAuthorized", + "msg": "The given account is not authorized to execute this instruction." + }, + { + "code": 6001, + "name": "AlreadyInitialized", + "msg": "The program is already initialized." + }, + { + "code": 6002, + "name": "TooMuchSolRequired", + "msg": "slippage: Too much SOL required to buy the given amount of tokens." + }, + { + "code": 6003, + "name": "TooLittleSolReceived", + "msg": "slippage: Too little SOL received to sell the given amount of tokens." + }, + { + "code": 6004, + "name": "MintDoesNotMatchBondingCurve", + "msg": "The mint does not match the bonding curve." + }, + { + "code": 6005, + "name": "BondingCurveComplete", + "msg": "The bonding curve has completed and liquidity migrated to raydium." + }, + { + "code": 6006, + "name": "BondingCurveNotComplete", + "msg": "The bonding curve has not completed." + }, + { + "code": 6007, + "name": "NotInitialized", + "msg": "The program is not initialized." + } + ], + "types": [ + { + "name": "BondingCurve", + "type": { + "kind": "struct", + "fields": [ + { + "name": "virtual_token_reserves", + "type": "u64" + }, + { + "name": "virtual_sol_reserves", + "type": "u64" + }, + { + "name": "real_token_reserves", + "type": "u64" + }, + { + "name": "real_sol_reserves", + "type": "u64" + }, + { + "name": "token_total_suply", + "type": "u64" + }, + { + "name": "complete", + "type": "bool" + } + ] + } + }, + { + "name": "CompleteEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "user", + "type": "pubkey" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + } + ] + } + }, + { + "name": "CreateEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "bonding_curve", + "type": "pubkey" + }, + { + "name": "user", + "type": "pubkey" + } + ] + } + }, + { + "name": "Global", + "type": { + "kind": "struct", + "fields": [ + { + "name": "initialized", + "type": "bool" + }, + { + "name": "authority", + "type": "pubkey" + }, + { + "name": "fee_recipient", + "type": "pubkey" + }, + { + "name": "initial_virtual_token_reserves", + "type": "u64" + }, + { + "name": "initial_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "initial_real_token_reserves", + "type": "u64" + }, + { + "name": "token_total_suply", + "type": "u64" + }, + { + "name": "fee_basis_points", + "type": "u64" + } + ] + } + }, + { + "name": "SetParamsEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "fee_recipient", + "type": "pubkey" + }, + { + "name": "initial_virtual_token_reserves", + "type": "u64" + }, + { + "name": "initial_virtual_sol_reserves", + "type": "u64" + }, + { + "name": "initial_real_token_reserves", + "type": "u64" + }, + { + "name": "token_total_suply", + "type": "u64" + }, + { + "name": "fee_basis_points", + "type": "u64" + } + ] + } + }, + { + "name": "TradeEvent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "mint", + "type": "pubkey" + }, + { + "name": "sol_amount", + "type": "u64" + }, + { + "name": "token_amount", + "type": "u64" + }, + { + "name": "is_buy", + "type": "bool" + }, + { + "name": "user", + "type": "pubkey" + }, + { + "name": "timestamp", + "type": "i64" + }, + { + "name": "virtual_sol_reserves", + "type": "u64" + }, + { + "name": "virtual_token_reserves", + "type": "u64" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/hyperstack-idl/tests/parse_fixtures.rs b/hyperstack-idl/tests/parse_fixtures.rs new file mode 100644 index 00000000..e75552a1 --- /dev/null +++ b/hyperstack-idl/tests/parse_fixtures.rs @@ -0,0 +1,51 @@ +use hyperstack_idl::parse::parse_idl_file; +use std::path::PathBuf; + +fn fixture_path(name: &str) -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures") + .join(name) +} + +#[test] +fn test_parse_ore_legacy() { + let idl = parse_idl_file(&fixture_path("ore.json")).expect("should parse ore.json"); + assert_eq!( + idl.instructions.len(), + 19, + "ore should have 19 instructions" + ); + assert!(idl.name.is_some(), "ore should have a name"); +} + +#[test] +fn test_parse_entropy_legacy() { + let idl = parse_idl_file(&fixture_path("entropy.json")).expect("should parse entropy.json"); + assert_eq!( + idl.instructions.len(), + 5, + "entropy should have 5 instructions" + ); +} + +#[test] +fn test_parse_pump_modern() { + let idl = parse_idl_file(&fixture_path("pump.json")).expect("should parse pump.json"); + assert_eq!(idl.instructions.len(), 6, "pump should have 6 instructions"); +} + +#[test] +fn test_parse_meteora_dlmm_modern() { + let idl = + parse_idl_file(&fixture_path("meteora_dlmm.json")).expect("should parse meteora_dlmm.json"); + assert_eq!( + idl.instructions.len(), + 74, + "meteora_dlmm should have 74 instructions" + ); + assert_eq!( + idl.constants.len(), + 30, + "meteora_dlmm should have 30 constants" + ); +} diff --git a/hyperstack-macros/Cargo.toml b/hyperstack-macros/Cargo.toml index e97c2746..cefc58a7 100644 --- a/hyperstack-macros/Cargo.toml +++ b/hyperstack-macros/Cargo.toml @@ -23,3 +23,4 @@ serde_json = "1.0" sha2 = "0.10" hex = "0.4" bs58 = "0.5" +hyperstack-idl = { path = "../hyperstack-idl" } diff --git a/hyperstack-macros/src/ast/types.rs b/hyperstack-macros/src/ast/types.rs index 8fe03be0..1b58ed69 100644 --- a/hyperstack-macros/src/ast/types.rs +++ b/hyperstack-macros/src/ast/types.rs @@ -11,6 +11,8 @@ use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; +pub use hyperstack_idl::snapshot::*; + // ============================================================================ // Core AST Types // ============================================================================ @@ -58,187 +60,12 @@ pub enum PopulationStrategy { UniqueCount, } -// ============================================================================ -// IDL Snapshot Types - Embedded IDL for AST-only compilation -// ============================================================================ - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlSnapshot { - pub name: String, - /// Program ID this IDL belongs to - #[serde(default, skip_serializing_if = "Option::is_none")] - pub program_id: Option, - pub version: String, - pub accounts: Vec, - pub instructions: Vec, - #[serde(default)] - pub types: Vec, - #[serde(default)] - pub events: Vec, - #[serde(default)] - pub errors: Vec, - /// Discriminant size in bytes (1 for Steel, 8 for Anchor) - /// Defaults to 8 (Anchor) for backwards compatibility - #[serde(default = "default_discriminant_size")] - pub discriminant_size: usize, -} +/// Default discriminant size (8 bytes for Anchor). +/// Used by InstructionDef serde default. fn default_discriminant_size() -> usize { 8 } - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlAccountSnapshot { - pub name: String, - pub discriminator: Vec, - #[serde(default)] - pub docs: Vec, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub serialization: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlInstructionSnapshot { - pub name: String, - pub discriminator: Vec, - #[serde(default)] - pub docs: Vec, - pub accounts: Vec, - pub args: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlInstructionAccountSnapshot { - pub name: String, - #[serde(default)] - pub writable: bool, - #[serde(default)] - pub signer: bool, - #[serde(default)] - pub optional: bool, - #[serde(default)] - pub address: Option, - #[serde(default)] - pub docs: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlFieldSnapshot { - pub name: String, - #[serde(rename = "type")] - pub type_: IdlTypeSnapshot, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(untagged)] -pub enum IdlTypeSnapshot { - Simple(String), - Array(IdlArrayTypeSnapshot), - Option(IdlOptionTypeSnapshot), - Vec(IdlVecTypeSnapshot), - HashMap(IdlHashMapTypeSnapshot), - Defined(IdlDefinedTypeSnapshot), -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlHashMapTypeSnapshot { - #[serde(rename = "hashMap")] - pub hash_map: (Box, Box), -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlArrayTypeSnapshot { - pub array: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(untagged)] -pub enum IdlArrayElementSnapshot { - Type(IdlTypeSnapshot), - TypeName(String), - Size(u32), -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlOptionTypeSnapshot { - pub option: Box, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlVecTypeSnapshot { - pub vec: Box, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlDefinedTypeSnapshot { - pub defined: IdlDefinedInnerSnapshot, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(untagged)] -pub enum IdlDefinedInnerSnapshot { - Named { name: String }, - Simple(String), -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum IdlSerializationSnapshot { - Borsh, - Bytemuck, - #[serde(alias = "bytemuckunsafe")] - BytemuckUnsafe, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlTypeDefSnapshot { - pub name: String, - #[serde(default)] - pub docs: Vec, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub serialization: Option, - #[serde(rename = "type")] - pub type_def: IdlTypeDefKindSnapshot, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(untagged)] -pub enum IdlTypeDefKindSnapshot { - Struct { - kind: String, - fields: Vec, - }, - TupleStruct { - kind: String, - fields: Vec, - }, - Enum { - kind: String, - variants: Vec, - }, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlEnumVariantSnapshot { - pub name: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlEventSnapshot { - pub name: String, - pub discriminator: Vec, - #[serde(default)] - pub docs: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdlErrorSnapshot { - pub code: u32, - pub name: String, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub msg: Option, -} - // ============================================================================ // Computed Field Expression AST // ============================================================================ diff --git a/hyperstack-macros/src/ast/writer.rs b/hyperstack-macros/src/ast/writer.rs index e8916387..38b4ed39 100644 --- a/hyperstack-macros/src/ast/writer.rs +++ b/hyperstack-macros/src/ast/writer.rs @@ -905,7 +905,7 @@ pub fn extract_instructions_from_idl( .iter() .map(|arg| InstructionArgDef { name: arg.name.clone(), - arg_type: arg.type_.to_rust_type_string(), + arg_type: crate::parse::idl::to_rust_type_string(&arg.type_), docs: vec![], }) .collect(); diff --git a/hyperstack-macros/src/idl_codegen.rs b/hyperstack-macros/src/idl_codegen.rs index 0d5d2971..cc6abba7 100644 --- a/hyperstack-macros/src/idl_codegen.rs +++ b/hyperstack-macros/src/idl_codegen.rs @@ -48,9 +48,9 @@ fn resolve_type_string( in_accounts_module: bool, ) -> String { let base = if bytemuck { - idl_type.to_rust_type_string_bytemuck() + crate::parse::idl::to_rust_type_string_bytemuck(idl_type) } else { - idl_type.to_rust_type_string() + crate::parse::idl::to_rust_type_string(idl_type) }; match idl_type { IdlType::Defined(def) => { diff --git a/hyperstack-macros/src/parse/idl.rs b/hyperstack-macros/src/parse/idl.rs index 7e1e1601..c7c0f3c1 100644 --- a/hyperstack-macros/src/parse/idl.rs +++ b/hyperstack-macros/src/parse/idl.rs @@ -1,475 +1,92 @@ //! IDL file parsing. //! -//! Parses Anchor IDL JSON files into Rust structures for code generation. +//! Re-exports IDL types from the `hyperstack-idl` library and provides +//! macros-specific code generation helpers. #![allow(dead_code)] +#![allow(unused_imports)] -use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; -use std::fs; -use std::path::Path; +// Re-export all IDL types from the hyperstack-idl library +pub use hyperstack_idl::discriminator::*; +pub use hyperstack_idl::parse::*; +pub use hyperstack_idl::types::*; -fn anchor_discriminator(preimage: &str) -> Vec { - let hash = Sha256::digest(preimage.as_bytes()); - hash[..8].to_vec() -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlSpec { - #[serde(default)] - pub version: Option, - #[serde(default)] - pub name: Option, - #[serde(default)] - pub address: Option, - pub instructions: Vec, - pub accounts: Vec, - #[serde(default)] - pub types: Vec, - #[serde(default)] - pub events: Vec, - #[serde(default)] - pub errors: Vec, - pub metadata: Option, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlMetadata { - #[serde(default)] - pub name: Option, - #[serde(default)] - pub version: Option, - #[serde(default)] - pub address: Option, - #[serde(default)] - pub spec: Option, - #[serde(default)] - pub description: Option, - #[serde(default)] - pub origin: Option, -} - -/// Steel-style discriminant format: {"type": "u8", "value": N} -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct SteelDiscriminant { - #[serde(rename = "type")] - pub type_: String, - pub value: u64, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlInstruction { - pub name: String, - /// Anchor-style discriminator: 8-byte array - #[serde(default)] - pub discriminator: Vec, - /// Steel-style discriminant: {"type": "u8", "value": N} - #[serde(default)] - pub discriminant: Option, - #[serde(default)] - pub docs: Vec, - pub accounts: Vec, - pub args: Vec, -} - -impl IdlInstruction { - pub fn get_discriminator(&self) -> Vec { - if !self.discriminator.is_empty() { - return self.discriminator.clone(); +/// Convert an IDL type to a Rust type string for Borsh-serialized accounts. +pub fn to_rust_type_string(idl_type: &IdlType) -> String { + match idl_type { + IdlType::Simple(s) => map_simple_type(s), + IdlType::Array(arr) => { + if arr.array.len() == 2 { + match (&arr.array[0], &arr.array[1]) { + (IdlTypeArrayElement::Type(t), IdlTypeArrayElement::Size(size)) => { + format!("[{}; {}]", map_simple_type(t), size) + } + (IdlTypeArrayElement::Nested(nested), IdlTypeArrayElement::Size(size)) => { + let inner = to_rust_type_string(nested); + format!("[{}; {}]", inner, size) + } + _ => "Vec".to_string(), + } + } else { + "Vec".to_string() + } } - - if let Some(disc) = &self.discriminant { - let value = disc.value as u8; - return vec![value, 0, 0, 0, 0, 0, 0, 0]; + IdlType::Defined(def) => match &def.defined { + IdlTypeDefinedInner::Named { name } => name.clone(), + IdlTypeDefinedInner::Simple(s) => s.clone(), + }, + IdlType::Option(opt) => { + let inner_type = to_rust_type_string(&opt.option); + format!("Option<{}>", inner_type) } - - // Legacy IDLs omit discriminators — derive from Anchor convention: - // sha256("global:")[0..8] - anchor_discriminator(&format!("global:{}", self.name)) - } -} - -/// PDA definition in Anchor IDL format -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlPda { - pub seeds: Vec, - #[serde(default)] - pub program: Option, -} - -/// PDA seed in Anchor IDL format -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(tag = "kind", rename_all = "lowercase")] -pub enum IdlPdaSeed { - /// Constant byte array seed - Const { value: Vec }, - /// Reference to another account in the instruction - Account { - path: String, - #[serde(default)] - account: Option, - }, - /// Reference to an instruction argument - Arg { - path: String, - #[serde(rename = "type", default)] - arg_type: Option, - }, -} - -/// Program reference for cross-program PDAs -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum IdlPdaProgram { - /// Reference to another account that holds the program ID - Account { kind: String, path: String }, - /// Literal program ID - Literal { kind: String, value: String }, - /// Constant program ID as bytes - Const { kind: String, value: Vec }, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlAccountArg { - pub name: String, - #[serde(rename = "isMut", alias = "writable", default)] - pub is_mut: bool, - #[serde(rename = "isSigner", alias = "signer", default)] - pub is_signer: bool, - #[serde(default)] - pub address: Option, - #[serde(default)] - pub optional: bool, - #[serde(default)] - pub docs: Vec, - #[serde(default)] - pub pda: Option, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlAccount { - pub name: String, - #[serde(default)] - pub discriminator: Vec, - #[serde(default)] - pub docs: Vec, - /// Steel format embedded type definition - #[serde(rename = "type", default)] - pub type_def: Option, -} - -impl IdlAccount { - pub fn get_discriminator(&self) -> Vec { - if !self.discriminator.is_empty() { - return self.discriminator.clone(); + IdlType::Vec(vec) => { + let inner_type = to_rust_type_string(&vec.vec); + format!("Vec<{}>", inner_type) } - - // Legacy IDLs omit discriminators — derive from Anchor convention: - // sha256("account:")[0..8] - anchor_discriminator(&format!("account:{}", self.name)) - } -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlTypeDefStruct { - pub kind: String, - pub fields: Vec, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlField { - pub name: String, - #[serde(rename = "type")] - pub type_: IdlType, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum IdlType { - Simple(String), - Array(IdlTypeArray), - Option(IdlTypeOption), - Vec(IdlTypeVec), - HashMap(IdlTypeHashMap), - Defined(IdlTypeDefined), -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlTypeOption { - pub option: Box, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlTypeVec { - pub vec: Box, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlTypeHashMap { - #[serde(alias = "bTreeMap")] - #[serde(rename = "hashMap")] - pub hash_map: (Box, Box), -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlTypeArray { - pub array: Vec, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum IdlTypeArrayElement { - Nested(IdlType), - Type(String), - Size(u32), -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlTypeDefined { - pub defined: IdlTypeDefinedInner, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum IdlTypeDefinedInner { - Named { name: String }, - Simple(String), -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlRepr { - pub kind: String, -} - -/// Account serialization format as specified in the IDL. -/// Defaults to Borsh when not specified. -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)] -#[serde(rename_all = "lowercase")] -pub enum IdlSerialization { - #[default] - Borsh, - Bytemuck, - #[serde(alias = "bytemuckunsafe")] - BytemuckUnsafe, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlTypeDef { - pub name: String, - #[serde(default)] - pub docs: Vec, - /// Serialization format: "borsh" (default), "bytemuck", or "bytemuckunsafe" - #[serde(default)] - pub serialization: Option, - /// Repr annotation for zero-copy types (e.g., {"kind": "c"}) - #[serde(default)] - pub repr: Option, - #[serde(rename = "type")] - pub type_def: IdlTypeDefKind, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum IdlTypeDefKind { - Struct { - kind: String, - fields: Vec, - }, - TupleStruct { - kind: String, - fields: Vec, - }, - Enum { - kind: String, - variants: Vec, - }, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlEnumVariant { - pub name: String, -} - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlEvent { - pub name: String, - #[serde(default)] - pub discriminator: Vec, - #[serde(default)] - pub docs: Vec, -} - -impl IdlEvent { - pub fn get_discriminator(&self) -> Vec { - if !self.discriminator.is_empty() { - return self.discriminator.clone(); + IdlType::HashMap(hm) => { + let key_type = to_rust_type_string(&hm.hash_map.0); + let val_type = to_rust_type_string(&hm.hash_map.1); + format!("std::collections::HashMap<{}, {}>", key_type, val_type) } - anchor_discriminator(&format!("event:{}", self.name)) } } -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct IdlError { - pub code: u32, - pub name: String, - #[serde(default)] - pub msg: Option, -} - -pub fn parse_idl_file>(path: P) -> Result { - let content = fs::read_to_string(&path) - .map_err(|e| format!("Failed to read IDL file {:?}: {}", path.as_ref(), e))?; - - parse_idl_content(&content) -} - -pub fn parse_idl_content(content: &str) -> Result { - serde_json::from_str(content).map_err(|e| format!("Failed to parse IDL JSON: {}", e)) -} - -impl IdlSpec { - pub fn get_name(&self) -> &str { - self.name - .as_deref() - .or_else(|| self.metadata.as_ref().and_then(|m| m.name.as_deref())) - .unwrap_or("unknown") - } - - pub fn get_version(&self) -> &str { - self.version - .as_deref() - .or_else(|| self.metadata.as_ref().and_then(|m| m.version.as_deref())) - .unwrap_or("0.1.0") - } - - /// Check if a field is an account (vs an arg/data field) for a given instruction - /// Returns Some("accounts") if it's an account, Some("data") if it's an arg, None if not found - pub fn get_instruction_field_prefix( - &self, - instruction_name: &str, - field_name: &str, - ) -> Option<&'static str> { - // Normalize instruction name for comparison. - // IDL may use camelCase (e.g., "swapBaseIn") or snake_case (e.g., "create_v2") - // while code uses PascalCase (e.g., "SwapBaseIn", "CreateV2"). - // Try both case-insensitive match and snake_case conversion. - let normalized_name = to_snake_case(instruction_name); - - for instruction in &self.instructions { - if instruction.name == normalized_name - || instruction.name.eq_ignore_ascii_case(instruction_name) - { - // Check if it's an account - for account in &instruction.accounts { - if account.name == field_name { - return Some("accounts"); + /// Convert an IDL type to a Rust type string for bytemuck (zero-copy) accounts. + pub fn to_rust_type_string_bytemuck(idl_type: &IdlType) -> String { + match idl_type { + IdlType::Simple(s) => map_simple_type_bytemuck(s), + IdlType::Array(arr) => { + if arr.array.len() == 2 { + match (&arr.array[0], &arr.array[1]) { + (IdlTypeArrayElement::Type(t), IdlTypeArrayElement::Size(size)) => { + format!("[{}; {}]", map_simple_type_bytemuck(t), size) } - } - // Check if it's an arg (instruction data) - for arg in &instruction.args { - if arg.name == field_name { - return Some("data"); + (IdlTypeArrayElement::Nested(nested), IdlTypeArrayElement::Size(size)) => { + let inner = to_rust_type_string_bytemuck(nested); + format!("[{}; {}]", inner, size) } + _ => "Vec".to_string(), } - // Field not found in this instruction - return None; + } else { + "Vec".to_string() } } - // Instruction not found - None - } - - /// Get the discriminator bytes for an instruction by name - pub fn get_instruction_discriminator(&self, instruction_name: &str) -> Option> { - let normalized_name = to_snake_case(instruction_name); - for instruction in &self.instructions { - if instruction.name == normalized_name { - let disc = instruction.get_discriminator(); - if !disc.is_empty() { - return Some(disc); - } - } + IdlType::Defined(def) => match &def.defined { + IdlTypeDefinedInner::Named { name } => name.clone(), + IdlTypeDefinedInner::Simple(s) => s.clone(), + }, + IdlType::Option(opt) => { + let inner_type = to_rust_type_string_bytemuck(&opt.option); + format!("Option<{}>", inner_type) } - None - } -} - -impl IdlType { - pub fn to_rust_type_string(&self) -> String { - match self { - IdlType::Simple(s) => map_simple_type(s), - IdlType::Array(arr) => { - if arr.array.len() == 2 { - match (&arr.array[0], &arr.array[1]) { - (IdlTypeArrayElement::Type(t), IdlTypeArrayElement::Size(size)) => { - format!("[{}; {}]", map_simple_type(t), size) - } - (IdlTypeArrayElement::Nested(nested), IdlTypeArrayElement::Size(size)) => { - let inner = nested.to_rust_type_string(); - format!("[{}; {}]", inner, size) - } - _ => "Vec".to_string(), - } - } else { - "Vec".to_string() - } - } - IdlType::Defined(def) => match &def.defined { - IdlTypeDefinedInner::Named { name } => name.clone(), - IdlTypeDefinedInner::Simple(s) => s.clone(), - }, - IdlType::Option(opt) => { - let inner_type = opt.option.to_rust_type_string(); - format!("Option<{}>", inner_type) - } - IdlType::Vec(vec) => { - let inner_type = vec.vec.to_rust_type_string(); - format!("Vec<{}>", inner_type) - } - IdlType::HashMap(hm) => { - let key_type = hm.hash_map.0.to_rust_type_string(); - let val_type = hm.hash_map.1.to_rust_type_string(); - format!("std::collections::HashMap<{}, {}>", key_type, val_type) - } + IdlType::Vec(vec) => { + let inner_type = to_rust_type_string_bytemuck(&vec.vec); + format!("Vec<{}>", inner_type) } - } - - pub fn to_rust_type_string_bytemuck(&self) -> String { - match self { - IdlType::Simple(s) => map_simple_type_bytemuck(s), - IdlType::Array(arr) => { - if arr.array.len() == 2 { - match (&arr.array[0], &arr.array[1]) { - (IdlTypeArrayElement::Type(t), IdlTypeArrayElement::Size(size)) => { - format!("[{}; {}]", map_simple_type_bytemuck(t), size) - } - (IdlTypeArrayElement::Nested(nested), IdlTypeArrayElement::Size(size)) => { - let inner = nested.to_rust_type_string_bytemuck(); - format!("[{}; {}]", inner, size) - } - _ => "Vec".to_string(), - } - } else { - "Vec".to_string() - } - } - IdlType::Defined(def) => match &def.defined { - IdlTypeDefinedInner::Named { name } => name.clone(), - IdlTypeDefinedInner::Simple(s) => s.clone(), - }, - IdlType::Option(opt) => { - let inner_type = opt.option.to_rust_type_string_bytemuck(); - format!("Option<{}>", inner_type) - } - IdlType::Vec(vec) => { - let inner_type = vec.vec.to_rust_type_string_bytemuck(); - format!("Vec<{}>", inner_type) - } - IdlType::HashMap(hm) => { - let key_type = hm.hash_map.0.to_rust_type_string_bytemuck(); - let val_type = hm.hash_map.1.to_rust_type_string_bytemuck(); - format!("std::collections::HashMap<{}, {}>", key_type, val_type) - } + IdlType::HashMap(hm) => { + let key_type = to_rust_type_string_bytemuck(&hm.hash_map.0); + let val_type = to_rust_type_string_bytemuck(&hm.hash_map.1); + format!("std::collections::HashMap<{}, {}>", key_type, val_type) } } } @@ -515,250 +132,3 @@ fn map_simple_type_bytemuck(idl_type: &str) -> String { _ => idl_type.to_string(), } } - -pub fn to_snake_case(s: &str) -> String { - let mut result = String::new(); - - for c in s.chars() { - if c.is_uppercase() { - if !result.is_empty() { - result.push('_'); - } - result.push(c.to_lowercase().next().unwrap()); - } else { - result.push(c); - } - } - - result -} - -pub fn to_pascal_case(s: &str) -> String { - s.split('_') - .map(|word| { - let mut chars = word.chars(); - match chars.next() { - None => String::new(), - Some(f) => f.to_uppercase().collect::() + chars.as_str(), - } - }) - .collect() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_anchor_discriminator_known_values() { - let disc = anchor_discriminator("global:initialize"); - assert_eq!(disc.len(), 8); - assert_eq!(disc, &Sha256::digest(b"global:initialize")[..8]); - } - - #[test] - fn test_anchor_account_discriminator() { - let disc = anchor_discriminator("account:LendingMarket"); - assert_eq!(disc.len(), 8); - assert_eq!(disc, &Sha256::digest(b"account:LendingMarket")[..8]); - } - - #[test] - fn test_legacy_idl_parses_without_discriminator() { - let json = r#"{ - "address": "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8", - "version": "0.3.0", - "name": "raydium_amm", - "instructions": [ - { - "name": "initialize", - "accounts": [ - { "name": "tokenProgram", "isMut": false, "isSigner": false } - ], - "args": [ - { "name": "nonce", "type": "u8" } - ] - } - ], - "accounts": [ - { - "name": "TargetOrders", - "type": { - "kind": "struct", - "fields": [ - { "name": "owner", "type": { "array": ["u64", 4] } } - ] - } - } - ], - "types": [], - "errors": [] - }"#; - let idl = parse_idl_content(json).expect("legacy IDL should parse"); - - assert_eq!(idl.instructions.len(), 1); - assert_eq!(idl.accounts.len(), 1); - assert!(idl.accounts[0].discriminator.is_empty()); - assert!(idl.instructions[0].discriminator.is_empty()); - assert!(idl.instructions[0].discriminant.is_none()); - } - - #[test] - fn test_legacy_instruction_computes_discriminator() { - let json = r#"{ - "name": "raydium_amm", - "instructions": [ - { - "name": "initialize", - "accounts": [], - "args": [] - } - ], - "accounts": [], - "types": [], - "errors": [] - }"#; - let idl = parse_idl_content(json).unwrap(); - let disc = idl.instructions[0].get_discriminator(); - - assert_eq!(disc.len(), 8); - let expected = anchor_discriminator("global:initialize"); - assert_eq!(disc, expected); - } - - #[test] - fn test_legacy_account_computes_discriminator() { - let json = r#"{ - "name": "test", - "instructions": [], - "accounts": [ - { - "name": "LendingMarket", - "type": { "kind": "struct", "fields": [] } - } - ], - "types": [], - "errors": [] - }"#; - let idl = parse_idl_content(json).unwrap(); - let disc = idl.accounts[0].get_discriminator(); - - assert_eq!(disc.len(), 8); - let expected = anchor_discriminator("account:LendingMarket"); - assert_eq!(disc, expected); - } - - #[test] - fn test_explicit_discriminator_not_overridden() { - let json = r#"{ - "name": "test", - "instructions": [ - { - "name": "transfer", - "discriminator": [1, 2, 3, 4, 5, 6, 7, 8], - "accounts": [], - "args": [] - } - ], - "accounts": [ - { - "name": "TokenAccount", - "discriminator": [10, 20, 30, 40, 50, 60, 70, 80] - } - ], - "types": [], - "errors": [] - }"#; - let idl = parse_idl_content(json).unwrap(); - - assert_eq!( - idl.instructions[0].get_discriminator(), - vec![1, 2, 3, 4, 5, 6, 7, 8] - ); - assert_eq!( - idl.accounts[0].get_discriminator(), - vec![10, 20, 30, 40, 50, 60, 70, 80] - ); - } - - #[test] - fn test_steel_discriminant_still_works() { - let json = r#"{ - "name": "test", - "instructions": [ - { - "name": "CreateMetadataAccount", - "accounts": [], - "args": [], - "discriminant": { "type": "u8", "value": 0 } - }, - { - "name": "UpdateMetadataAccount", - "accounts": [], - "args": [], - "discriminant": { "type": "u8", "value": 1 } - } - ], - "accounts": [], - "types": [], - "errors": [] - }"#; - let idl = parse_idl_content(json).unwrap(); - - assert_eq!( - idl.instructions[0].get_discriminator(), - vec![0, 0, 0, 0, 0, 0, 0, 0] - ); - assert_eq!( - idl.instructions[1].get_discriminator(), - vec![1, 0, 0, 0, 0, 0, 0, 0] - ); - } - - #[test] - fn test_legacy_event_computes_discriminator() { - let json = r#"{ - "name": "test", - "instructions": [], - "accounts": [], - "types": [], - "events": [ - { "name": "TransferEvent" } - ], - "errors": [] - }"#; - let idl = parse_idl_content(json).unwrap(); - let disc = idl.events[0].get_discriminator(); - - assert_eq!(disc.len(), 8); - let expected = anchor_discriminator("event:TransferEvent"); - assert_eq!(disc, expected); - } - - #[test] - fn test_is_mut_is_signer_aliases() { - let json = r#"{ - "name": "test", - "instructions": [ - { - "name": "do_thing", - "accounts": [ - { "name": "payer", "isMut": true, "isSigner": true }, - { "name": "dest", "writable": true, "signer": false } - ], - "args": [] - } - ], - "accounts": [], - "types": [], - "errors": [] - }"#; - let idl = parse_idl_content(json).unwrap(); - let accounts = &idl.instructions[0].accounts; - - assert!(accounts[0].is_mut); - assert!(accounts[0].is_signer); - assert!(accounts[1].is_mut); - assert!(!accounts[1].is_signer); - } -} diff --git a/hyperstack-macros/src/parse/pda_validation.rs b/hyperstack-macros/src/parse/pda_validation.rs index cec64e23..3907128f 100644 --- a/hyperstack-macros/src/parse/pda_validation.rs +++ b/hyperstack-macros/src/parse/pda_validation.rs @@ -100,7 +100,7 @@ impl<'a> PdaValidationContext<'a> { )) } Some(idl_arg) => { - let actual_type = idl_arg.type_.to_rust_type_string(); + let actual_type = crate::parse::idl::to_rust_type_string(&idl_arg.type_); if arg_type != &actual_type { Err(Error::new( seed.span, @@ -156,6 +156,7 @@ mod tests { version: Some("0.1.0".to_string()), metadata: None, accounts: vec![], + constants: vec![], instructions: vec![IdlInstruction { name: "create".to_string(), discriminator: vec![0; 8], diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml index bdd92d44..fc13868d 100644 --- a/interpreter/Cargo.toml +++ b/interpreter/Cargo.toml @@ -28,6 +28,7 @@ tracing = "0.1" reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } futures = "0.3" hyperstack-macros = { version = "0.5.3", path = "../hyperstack-macros" } +hyperstack-idl = { path = "../hyperstack-idl" } # OpenTelemetry for distributed tracing and metrics (optional, behind 'otel' feature) opentelemetry = { version = "0.22", features = ["otel_unstable"], optional = true } diff --git a/interpreter/src/ast.rs b/interpreter/src/ast.rs index 4432658b..4e777b9a 100644 --- a/interpreter/src/ast.rs +++ b/interpreter/src/ast.rs @@ -3,323 +3,64 @@ use serde_json::Value; use std::collections::BTreeMap; use std::marker::PhantomData; -// ============================================================================ -// IDL Snapshot Types - Embedded IDL for AST-only compilation -// ============================================================================ - -/// Snapshot of an Anchor IDL for embedding in the AST -/// Contains all information needed to generate parsers and SDK types -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlSnapshot { - /// Program name (e.g., "pump") - pub name: String, - /// Program ID this IDL belongs to - #[serde(default, skip_serializing_if = "Option::is_none")] - pub program_id: Option, - /// Program version - pub version: String, - /// Account type definitions - pub accounts: Vec, - /// Instruction definitions - pub instructions: Vec, - /// Type definitions (structs, enums) - #[serde(default)] - pub types: Vec, - /// Event definitions - #[serde(default)] - pub events: Vec, - /// Error definitions - #[serde(default)] - pub errors: Vec, - /// Discriminant size in bytes (1 for Steel, 8 for Anchor) - /// Defaults to 8 (Anchor) for backwards compatibility - #[serde(default = "default_discriminant_size")] - pub discriminant_size: usize, -} - -fn default_discriminant_size() -> usize { - 8 -} - -/// Account definition from IDL -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlAccountSnapshot { - /// Account name (e.g., "BondingCurve") - pub name: String, - /// 8-byte discriminator - pub discriminator: Vec, - /// Documentation - #[serde(default)] - pub docs: Vec, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub serialization: Option, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum IdlSerializationSnapshot { - Borsh, - Bytemuck, - #[serde(alias = "bytemuckunsafe")] - BytemuckUnsafe, -} - -/// Instruction definition from IDL -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlInstructionSnapshot { - /// Instruction name (e.g., "buy", "sell", "create") - pub name: String, - /// 8-byte discriminator - pub discriminator: Vec, - /// Documentation - #[serde(default)] - pub docs: Vec, - /// Account arguments - pub accounts: Vec, - /// Data arguments - pub args: Vec, -} - -/// Account argument in an instruction -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlInstructionAccountSnapshot { - /// Account name (e.g., "mint", "user") - pub name: String, - /// Whether this account is writable - #[serde(default)] - pub writable: bool, - /// Whether this account is a signer - #[serde(default)] - pub signer: bool, - /// Optional - if the account is optional - #[serde(default)] - pub optional: bool, - /// Fixed address constraint (if any) - #[serde(default)] - pub address: Option, - /// Documentation - #[serde(default)] - pub docs: Vec, -} - -/// Field definition (used in instructions, accounts, types) -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlFieldSnapshot { - /// Field name - pub name: String, - /// Field type - #[serde(rename = "type")] - pub type_: IdlTypeSnapshot, -} - -/// Type representation from IDL -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(untagged)] -pub enum IdlTypeSnapshot { - /// Simple types: "u64", "bool", "string", "pubkey", etc. - Simple(String), - /// Array type: { "array": ["u8", 32] } - Array(IdlArrayTypeSnapshot), - /// Option type: { "option": "u64" } - Option(IdlOptionTypeSnapshot), - /// Vec type: { "vec": "u8" } - Vec(IdlVecTypeSnapshot), - /// HashMap type: { "hashMap": ["string", "u64"] } - HashMap(IdlHashMapTypeSnapshot), - /// Defined/custom type: { "defined": { "name": "MyStruct" } } - Defined(IdlDefinedTypeSnapshot), -} - -/// Array type representation -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlArrayTypeSnapshot { - /// [element_type, size] - pub array: Vec, -} - -/// Array element (can be type or size) -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(untagged)] -pub enum IdlArrayElementSnapshot { - /// Nested type - Type(IdlTypeSnapshot), - /// Type name as string - TypeName(String), - /// Array size - Size(u32), -} - -/// Option type representation -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlOptionTypeSnapshot { - pub option: Box, -} - -/// Vec type representation -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlVecTypeSnapshot { - pub vec: Box, -} - -/// HashMap type representation: { "hashMap": [key_type, value_type] } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlHashMapTypeSnapshot { - #[serde(rename = "hashMap")] - pub hash_map: Vec, -} - -/// Defined/custom type reference -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlDefinedTypeSnapshot { - pub defined: IdlDefinedInnerSnapshot, -} - -/// Inner defined type (can be named or simple string) -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(untagged)] -pub enum IdlDefinedInnerSnapshot { - /// Named: { "name": "MyStruct" } - Named { name: String }, - /// Simple string reference - Simple(String), -} - -/// Type definition (struct or enum) -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlTypeDefSnapshot { - /// Type name - pub name: String, - /// Documentation - #[serde(default)] - pub docs: Vec, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub serialization: Option, - /// Type definition (struct or enum) - #[serde(rename = "type")] - pub type_def: IdlTypeDefKindSnapshot, -} - -/// Type definition kind (struct or enum) -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(untagged)] -pub enum IdlTypeDefKindSnapshot { - /// Struct: { "kind": "struct", "fields": [...] } - Struct { - kind: String, - fields: Vec, - }, - /// Tuple struct: { "kind": "struct", "fields": ["type1", "type2"] } - TupleStruct { - kind: String, - fields: Vec, - }, - /// Enum: { "kind": "enum", "variants": [...] } - Enum { - kind: String, - variants: Vec, - }, -} - -/// Enum variant -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlEnumVariantSnapshot { - pub name: String, -} - -/// Event definition -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdlEventSnapshot { - /// Event name - pub name: String, - /// 8-byte discriminator - pub discriminator: Vec, - /// Documentation - #[serde(default)] - pub docs: Vec, -} - -/// Error definition -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdlErrorSnapshot { - /// Error code - pub code: u32, - /// Error name - pub name: String, - /// Error message (optional - some IDLs omit this) - #[serde(default, skip_serializing_if = "Option::is_none")] - pub msg: Option, -} - -impl IdlTypeSnapshot { - /// Convert IDL type to Rust type string - pub fn to_rust_type_string(&self) -> String { - match self { - IdlTypeSnapshot::Simple(s) => Self::map_simple_type(s), - IdlTypeSnapshot::Array(arr) => { - if arr.array.len() == 2 { - match (&arr.array[0], &arr.array[1]) { - ( - IdlArrayElementSnapshot::TypeName(t), - IdlArrayElementSnapshot::Size(size), - ) => { - format!("[{}; {}]", Self::map_simple_type(t), size) - } - ( - IdlArrayElementSnapshot::Type(nested), - IdlArrayElementSnapshot::Size(size), - ) => { - format!("[{}; {}]", nested.to_rust_type_string(), size) - } - _ => "Vec".to_string(), +pub use hyperstack_idl::snapshot::*; + +pub fn idl_type_snapshot_to_rust_string(ty: &IdlTypeSnapshot) -> String { + match ty { + IdlTypeSnapshot::Simple(s) => map_simple_idl_type(s), + IdlTypeSnapshot::Array(arr) => { + if arr.array.len() == 2 { + match (&arr.array[0], &arr.array[1]) { + (IdlArrayElementSnapshot::TypeName(t), IdlArrayElementSnapshot::Size(size)) => { + format!("[{}; {}]", map_simple_idl_type(t), size) } - } else { - "Vec".to_string() + ( + IdlArrayElementSnapshot::Type(nested), + IdlArrayElementSnapshot::Size(size), + ) => { + format!("[{}; {}]", idl_type_snapshot_to_rust_string(nested), size) + } + _ => "Vec".to_string(), } + } else { + "Vec".to_string() } - IdlTypeSnapshot::Option(opt) => { - format!("Option<{}>", opt.option.to_rust_type_string()) - } - IdlTypeSnapshot::Vec(vec) => { - format!("Vec<{}>", vec.vec.to_rust_type_string()) - } - IdlTypeSnapshot::HashMap(map) => { - let key_type = map - .hash_map - .first() - .map(|t| t.to_rust_type_string()) - .unwrap_or_else(|| "String".to_string()); - let val_type = map - .hash_map - .get(1) - .map(|t| t.to_rust_type_string()) - .unwrap_or_else(|| "serde_json::Value".to_string()); - format!("std::collections::HashMap<{}, {}>", key_type, val_type) - } - IdlTypeSnapshot::Defined(def) => match &def.defined { - IdlDefinedInnerSnapshot::Named { name } => name.clone(), - IdlDefinedInnerSnapshot::Simple(s) => s.clone(), - }, } + IdlTypeSnapshot::Option(opt) => { + format!("Option<{}>", idl_type_snapshot_to_rust_string(&opt.option)) + } + IdlTypeSnapshot::Vec(vec) => { + format!("Vec<{}>", idl_type_snapshot_to_rust_string(&vec.vec)) + } + IdlTypeSnapshot::HashMap(map) => { + let key_type = idl_type_snapshot_to_rust_string(&map.hash_map.0); + let val_type = idl_type_snapshot_to_rust_string(&map.hash_map.1); + format!("std::collections::HashMap<{}, {}>", key_type, val_type) + } + IdlTypeSnapshot::Defined(def) => match &def.defined { + IdlDefinedInnerSnapshot::Named { name } => name.clone(), + IdlDefinedInnerSnapshot::Simple(s) => s.clone(), + }, } +} - fn map_simple_type(idl_type: &str) -> String { - match idl_type { - "u8" => "u8".to_string(), - "u16" => "u16".to_string(), - "u32" => "u32".to_string(), - "u64" => "u64".to_string(), - "u128" => "u128".to_string(), - "i8" => "i8".to_string(), - "i16" => "i16".to_string(), - "i32" => "i32".to_string(), - "i64" => "i64".to_string(), - "i128" => "i128".to_string(), - "bool" => "bool".to_string(), - "string" => "String".to_string(), - "publicKey" | "pubkey" => "solana_pubkey::Pubkey".to_string(), - "bytes" => "Vec".to_string(), - _ => idl_type.to_string(), - } +fn map_simple_idl_type(idl_type: &str) -> String { + match idl_type { + "u8" => "u8".to_string(), + "u16" => "u16".to_string(), + "u32" => "u32".to_string(), + "u64" => "u64".to_string(), + "u128" => "u128".to_string(), + "i8" => "i8".to_string(), + "i16" => "i16".to_string(), + "i32" => "i32".to_string(), + "i64" => "i64".to_string(), + "i128" => "i128".to_string(), + "bool" => "bool".to_string(), + "string" => "String".to_string(), + "publicKey" | "pubkey" => "solana_pubkey::Pubkey".to_string(), + "bytes" => "Vec".to_string(), + _ => idl_type.to_string(), } } @@ -996,6 +737,10 @@ fn default_emit() -> bool { true } +fn default_instruction_discriminant_size() -> usize { + 8 +} + fn is_true(value: &bool) -> bool { *value } @@ -1507,7 +1252,7 @@ pub struct InstructionDef { pub discriminator: Vec, /// Size of discriminator in bytes (for buffer allocation) - #[serde(default = "default_discriminant_size")] + #[serde(default = "default_instruction_discriminant_size")] pub discriminator_size: usize, /// Accounts required by this instruction, in order diff --git a/release-please-config.json b/release-please-config.json index 5780a65b..217ca5f0 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -78,8 +78,16 @@ "packages/hyperstack": { "release-type": "node", "component": "hyperstack-npm" + }, + "hyperstack-idl": { + "release-type": "rust", + "component": "hyperstack-idl" } }, + + + + "plugins": [ { "type": "cargo-workspace", diff --git a/stacks/ore/.hyperstack/OreStream.stack.json b/stacks/ore/.hyperstack/OreStream.stack.json index c367ff11..b54e064a 100644 --- a/stacks/ore/.hyperstack/OreStream.stack.json +++ b/stacks/ore/.hyperstack/OreStream.stack.json @@ -3789,14 +3789,14 @@ "pda_field": { "segments": [ "accounts", - "treasury" + "entropyVar" ], "offsets": null }, "seed_field": { "segments": [ "accounts", - "roundNext" + "round" ], "offsets": null }, @@ -3808,14 +3808,14 @@ "pda_field": { "segments": [ "accounts", - "entropyVar" + "treasury" ], "offsets": null }, "seed_field": { "segments": [ "accounts", - "round" + "roundNext" ], "offsets": null }, @@ -4556,7 +4556,7 @@ "result_type": "Option < f64 >" } ], - "content_hash": "fee0e768222fd6bab78adf26826ac1111fe5fd95dead39939b03b22e9e184eea", + "content_hash": "bb98d040a8cb94fca1a7641d664ed8245ec8c7cf266ace6c894cbfbf97dc0dea", "views": [ { "id": "OreRound/latest", @@ -5146,14 +5146,14 @@ "pda_field": { "segments": [ "accounts", - "treasury" + "entropyVar" ], "offsets": null }, "seed_field": { "segments": [ "accounts", - "roundNext" + "round" ], "offsets": null }, @@ -5165,14 +5165,14 @@ "pda_field": { "segments": [ "accounts", - "entropyVar" + "treasury" ], "offsets": null }, "seed_field": { "segments": [ "accounts", - "round" + "roundNext" ], "offsets": null }, @@ -5280,7 +5280,7 @@ "result_type": "Option < f64 >" } ], - "content_hash": "c409570174a695cd8b4b2e0b8ac65181ffe477b99943e419af878d34f61cddce", + "content_hash": "66301f1e812d89849a1f719de939d80b2973335b736c6c08c1d5b7773fa023ef", "views": [] }, { @@ -6706,14 +6706,14 @@ "pda_field": { "segments": [ "accounts", - "treasury" + "entropyVar" ], "offsets": null }, "seed_field": { "segments": [ "accounts", - "roundNext" + "round" ], "offsets": null }, @@ -6725,14 +6725,14 @@ "pda_field": { "segments": [ "accounts", - "entropyVar" + "treasury" ], "offsets": null }, "seed_field": { "segments": [ "accounts", - "round" + "roundNext" ], "offsets": null }, @@ -6746,7 +6746,7 @@ "resolver_specs": [], "computed_fields": [], "computed_field_specs": [], - "content_hash": "fa965125488f32c6c794a56c517d6455d2b04d90f29250d419b73efd957ea3d3", + "content_hash": "236fccaddbe4354f0fd201accb7bc597b90894a1b517d987980178b7be75a6be", "views": [] } ], @@ -8854,5 +8854,5 @@ ] } ], - "content_hash": "23b682bb628f14e33ffc389f62dc06cac3119d72ee29b59da9535e96d8acea8d" + "content_hash": "d54be9ac027a6a9581305854d45da529430879dd2e0dec2786f6da636a4cadda" } \ No newline at end of file