use std::collections::BTreeMap;
use thiserror::Error;
#[derive(Debug, Clone, PartialEq)]
pub enum FieldType {
Bool,
U8,
U16,
U32,
U64,
U128,
I8,
I16,
I32,
I64,
I128,
F32,
F64,
String,
Bytes,
Pubkey,
Option(Box<FieldType>),
Vec(Box<FieldType>),
Array {
ty: Box<FieldType>,
len: usize,
},
Struct(Vec<NamedField>),
Enum(Vec<EnumVariant>),
Defined(String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct NamedField {
pub name: String,
pub ty: FieldType,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EnumVariant {
pub name: String,
pub fields: Option<Vec<NamedField>>,
}
pub type DefinedTypes = BTreeMap<String, FieldType>;
#[derive(Debug, Clone, PartialEq)]
pub struct NamedAccount {
pub name: String,
pub writable: bool,
pub signer: bool,
pub optional: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct InstructionSchema {
pub name: String,
pub discriminator: Vec<u8>,
pub accounts: Vec<NamedAccount>,
pub args: Vec<NamedField>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ProgramSchema {
pub program_id: String,
pub instructions: BTreeMap<Vec<u8>, InstructionSchema>,
pub disc_lens: Vec<usize>,
pub defined_types: DefinedTypes,
}
impl ProgramSchema {
pub fn build(
program_id: String,
instructions: BTreeMap<Vec<u8>, InstructionSchema>,
defined_types: DefinedTypes,
) -> Self {
let mut lens: Vec<usize> = instructions.keys().map(|k| k.len()).collect();
lens.sort_unstable();
lens.dedup();
lens.reverse();
Self {
program_id,
instructions,
disc_lens: lens,
defined_types,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DecodedInstruction {
pub name: String,
pub args: serde_json::Value,
pub named_accounts: std::collections::BTreeMap<String, String>,
pub extra_accounts: Vec<String>,
}
#[derive(Debug, Error)]
pub enum DecodeError {
#[error("buffer underflow reading {what}: need {need}, have {have}")]
Underflow {
what: &'static str,
need: usize,
have: usize,
},
#[error("trailing {0} byte(s) after decode")]
TrailingBytes(usize),
#[error("invalid bool byte: {0} (expected 0 or 1)")]
InvalidBool(u8),
#[error("invalid option tag: {0} (expected 0 or 1)")]
InvalidOptionTag(u8),
#[error("unknown enum variant index: {0}")]
UnknownEnumVariant(u8),
#[error("invalid UTF-8 in string")]
InvalidUtf8,
#[error("unresolved defined type: {0}")]
UnresolvedType(String),
#[error("instruction data is empty (no discriminator)")]
EmptyInstructionData,
#[error("unknown discriminator: 0x{}", hex_lower(.0))]
UnknownDiscriminator(Vec<u8>),
#[error("instruction '{instruction}' expects at least {expected} accounts, got {got}")]
AccountCountTooFew {
instruction: String,
expected: usize,
got: usize,
},
#[error("invalid base58 account pubkey at index {index}: {reason}")]
InvalidAccountPubkey { index: usize, reason: String },
}
fn hex_lower(bytes: &[u8]) -> String {
let mut s = String::with_capacity(bytes.len() * 2);
for b in bytes {
use std::fmt::Write;
let _ = write!(&mut s, "{:02x}", b);
}
s
}