Skip to content

Commit

Permalink
initialize repo
Browse files Browse the repository at this point in the history
  • Loading branch information
raycursive committed Dec 3, 2024
0 parents commit 0630e5f
Show file tree
Hide file tree
Showing 14 changed files with 10,994 additions and 0 deletions.
24 changes: 24 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

.DS_Store
.vscode/
16 changes: 16 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "atlas-idl-schema"
version = "0.1.1"
edition = "2021"

[dependencies]
anyhow = "1.0.93"
bincode = "1.3.3"
borsh = { version = "0.10.4" }
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.133"
solana-program = "2.1.4"

[dev-dependencies]
solana-client = "2"
solana-sdk = "2"
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Atlas IDL Schema
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod on_chain_idl;
pub mod parse_idl;
pub mod schema;
pub mod value;
153 changes: 153 additions & 0 deletions src/on_chain_idl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use borsh::{BorshDeserialize, BorshSerialize};

use crate::{
schema::{SchemaNode, SchemaType},
value::{TypedValue, ValueNode},
};

#[derive(Debug, Clone, BorshDeserialize, BorshSerialize, PartialEq, Eq)]
pub struct InstructionDecoder {
pub accounts: Vec<String>,
pub instruction_args_parser: SchemaNode,
}

#[derive(Debug, Clone, BorshDeserialize, BorshSerialize, PartialEq, Eq)]
pub struct OnChainIdl {
pub program_name: String,
pub account_disc_len: u8,
pub instruction_disc_len: u8,
pub accounts: Vec<(u64, SchemaNode)>,
pub instruction_params: Vec<(u64, InstructionDecoder)>,
}

impl OnChainIdl {
pub fn get_parsed_instruction(
&self,
instruction_data: Vec<u8>,
account_keys: &[String],
show_hidden: bool,
) -> anyhow::Result<ParsedInstructionResult> {
let data = instruction_data;
if data.len() < self.instruction_disc_len as usize {
return Err(anyhow::anyhow!("Instruction data is too short"));
}

let mut padded_data = [0u8; 8];
let slice_len = std::cmp::min(self.instruction_disc_len as usize, 8);
padded_data[..slice_len].copy_from_slice(&data[..slice_len]);
let discriminant = u64::from_le_bytes(padded_data);

let instruction_decoder = self
.instruction_params
.iter()
.find(|(disc, _)| *disc == discriminant)
.map(|(_, decoder)| decoder)
.ok_or(anyhow::anyhow!("Instruction discriminant not found"))?;

let mut account_names = vec![];
for (i, _) in account_keys.iter().enumerate() {
let name = instruction_decoder
.accounts
.get(i)
.cloned()
.unwrap_or(format!("Account {}", i + 1));
account_names.push(name);
}

let schema = instruction_decoder.instruction_args_parser.clone();

let args: ValueNode = schema
.deserialize_bytes(
&mut &data[self.instruction_disc_len as usize..],
show_hidden,
)?
.ok_or(anyhow::anyhow!(
"is_hidden shouldn't be true in instructions"
))?;

Ok(ParsedInstructionResult::new(
schema,
account_names,
args.value,
))
}

pub fn get_parsed_account(
&self,
account_data: Vec<u8>,
show_hidden: bool,
) -> anyhow::Result<ParsedAccountResult> {
if account_data.len() < self.account_disc_len as usize {
return Err(anyhow::anyhow!("Account data is too short"));
}

let discriminant = self.get_account_discriminator(&account_data);

let account_schema = self
.accounts
.iter()
.find(|(disc, _)| *disc == discriminant)
.map(|(_, schema)| schema)
.ok_or(anyhow::anyhow!("Account discriminant not found"))?
.clone();

let value: ValueNode = account_schema
.deserialize_bytes(
&mut &account_data[self.account_disc_len as usize..],
show_hidden,
)?
.ok_or(anyhow::anyhow!("Account type shouldn't be hidden"))?;

Ok(ParsedAccountResult::new(account_schema, value.value))
}

pub fn get_account_discriminator(&self, account_data: &[u8]) -> u64 {
let mut padded_data = [0u8; 8];
let slice_len = std::cmp::min(self.account_disc_len as usize, 8);
padded_data[..slice_len].copy_from_slice(&account_data[..slice_len]);
u64::from_le_bytes(padded_data)
}

pub fn get_instruction_discriminator(&self, data: &[u8]) -> u64 {
let mut padded_data = [0u8; 8];
let slice_len = std::cmp::min(self.instruction_disc_len as usize, 8);
padded_data[..slice_len].copy_from_slice(&data[..slice_len]);
u64::from_le_bytes(padded_data)
}
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct ParsedInstructionResult {
pub name: String,
pub schema: SchemaType,
pub accounts: Vec<String>,
pub value: TypedValue,
}

impl ParsedInstructionResult {
pub fn new(schema: SchemaNode, accounts: Vec<String>, value: TypedValue) -> Self {
Self {
name: schema.name,
schema: schema.typ,
accounts,
value,
}
}
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct ParsedAccountResult {
pub name: String,
pub schema: SchemaType,
pub value: TypedValue,
}

impl ParsedAccountResult {
pub fn new(schema: SchemaNode, value: TypedValue) -> Self {
Self {
name: schema.name,
schema: schema.typ,
value,
}
}
}
Loading

0 comments on commit 0630e5f

Please sign in to comment.