diff --git a/README.MD b/README.MD
index 5ef3074..5770348 100644
--- a/README.MD
+++ b/README.MD
@@ -1,28 +1,43 @@
-
-
-
+
+
+
-# SODA: Ship a Solana program inferred from Solana data.
+# Ship a Solana program inferred from Solana data
-A tool for Solana development and audit that allows you to automate tasks and build Solana programs and clients from: IDLs, on-chain data, existing repos, input and output examples of each instruction, and naming conventions, among other possible resources.
-From an IDL to whatever you want.
+ **Note: Soda is in active development, so all is subject to change. This code is unaudited. Use at your own risk.**
+
+---
### Current Features
-
-- **Generate Solana Program and client from IDL.**
-- **Write, read and edit IDL in a desktop UI.**
-- **Template based: create your custom building blocks.**
-- **Lang and chain agnostic.**
+- Generate Solana Programs and clients' source code from an IDL
+- Write, read and edit IDL in a desktop UI
+- Template based:
+ - Create your custom building blocks
+ - Handlebars with Rhai scripting available
+ - Common helpers added by default
+ - Custom helpers support
+ - Binary Files support
+ - Filesystem based and dynamic paths
+ - Lang agnostic
+ - Interconvertible portable template file format and template folder, both supported by CLI and Desktop App
+- Available Templates:
+ - Anchor Program With a NextJs-Tailwind Client (Used as default in Desktop App)
+ - Seahorse Program (experimental)
+ - React Native Client (experimental)
## Install Soda Desktop
+
To install the Desktop App you need to [download the lastest release](https://github.com/Web3-Builders-Alliance/soda/releases)
+(Optionally you can download templates.zip file if you want to use other templates than the default one, or just clone this repo an use it from the templates folder)
+
+**Provisional Steps:** We are still working in add code signing in our app so at this point a security warning probably will show up. Until we finish the code signing process you will need to check how to open the app in your OS.
+
## Install Soda CLI
You can install the CLI using cargo or npm.
-
-- Using cargo:
+- Using cargo (recommended):
```cargo install soda-cli```
- Using npm:
@@ -31,28 +46,16 @@ You can install the CLI using cargo or npm.
#### CLI Usage
```soda-cli create-project ```
-
-creates a new project from an IDL file.
-If no template path is provided, the project will be try to use a template.soda file in the current directory.
+Creates a new project from an IDL file.
+If no template path is provided, the project will try to use a template.soda file in the current directory.
If no IDL path is provided, the CLI will try to open an idl.json file in the current directory.
```soda-cli pack-template ```
-
-creates a template file from a template folder.
+Creates a template file from a template folder.
If no output path is provided, the template will be created in the current directory with the name template.soda
if no template path is provided, the CLI will try to read from a template folder in the current directory.
```soda-cli unpack-template ```
-
-creates a template folder from a template file.
+Creates a template folder from a template file.
If no output path is provided, the template folder will be created in the current directory with the name template.
-if no template path is provided, the CLI will try to read from a template.soda file in the current directory.
-
-## Note
-
-Soda is in active development, so all is subject to change.
-This code is unaudited.
-Use at your own risk.
-
-## Powered by
-![The Solana Foundation](https://github.com/Web3-Builders-Alliance/soda/blob/main/ui/public/solana.svg?raw=true)
+If no template path is provided, the CLI will try to read from a template.soda file in the current directory.
diff --git a/assets/soda.png b/assets/soda.png
new file mode 100644
index 0000000..0bd5b3b
Binary files /dev/null and b/assets/soda.png differ
diff --git a/cli/Cargo.lock b/cli/Cargo.lock
index dfed4b5..3af2fdf 100644
--- a/cli/Cargo.lock
+++ b/cli/Cargo.lock
@@ -512,7 +512,7 @@ dependencies = [
[[package]]
name = "soda-cli"
-version = "0.0.5"
+version = "0.1.1"
dependencies = [
"clap",
"serde_json",
@@ -521,9 +521,9 @@ dependencies = [
[[package]]
name = "soda_sol"
-version = "0.0.5"
+version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cdb481f532f7205f3024ba8069fea66414462799295b1b7957e76587512f13c"
+checksum = "e6b50644fc479a9454dbee60e41e5f08bd7c209f307370795d21ac472bf839b4"
dependencies = [
"bincode",
"handlebars",
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index c163ea4..27a3151 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -1,13 +1,14 @@
[package]
name = "soda-cli"
-version = "0.0.5"
+version = "0.1.1"
edition = "2021"
license = "Apache-2.0"
description = "Generates Solana Projects from an IDL"
+repository = "https://github.com/Web3-Builders-Alliance/soda"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde_json = "1.0.93"
clap = { version = "4.1.8", features = ["derive"] }
-soda_sol = "0.0.5"
\ No newline at end of file
+soda_sol = "0.0.6"
diff --git a/cli/package.json b/cli/package.json
index 433df5f..9405ac5 100644
--- a/cli/package.json
+++ b/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@use_soda/soda-cli",
- "version": "0.0.5",
+ "version": "0.1.1",
"description": "Generates Solana Projects from an IDL",
"main": "start.js",
"directories": {
diff --git a/cli/pre-install.js b/cli/pre-install.js
index bd60c8b..2e6d886 100644
--- a/cli/pre-install.js
+++ b/cli/pre-install.js
@@ -29,8 +29,8 @@ if (fs.existsSync(cargoDir)) {
const features = process.env.npm_config_features ? `--features ${process.env.npm_config_features.replace(",", " ")}` : "";
-console.log(`Installing and compiling soda-cli 0.0.5 ${features} ...`);
-exec(`cargo install soda-cli --vers 0.0.5 ${features}`, (error, stdout, stderr) => {
+console.log(`Installing and compiling soda-cli 0.1.1 ${features} ...`);
+exec(`cargo install soda-cli --vers 0.1.1 ${features}`, (error, stdout, stderr) => {
console.log(stdout);
if (error || stderr) {
console.log(error || stderr);
diff --git a/cli/src/main.rs b/cli/src/main.rs
index b138889..1f444be 100644
--- a/cli/src/main.rs
+++ b/cli/src/main.rs
@@ -30,8 +30,8 @@ fn main() -> Result<(), Box> {
} else {
TEMPLATE_FILE_NAME
};
- let template = get_template_from_fs(template_path).unwrap();
- save_template(template, file_path).unwrap();
+ let template = get_template_from_fs(template_path)?;
+ save_template(template, file_path)?;
println!("Template File Generated!");
}
command if command == "unpack-template" => {
@@ -45,8 +45,8 @@ fn main() -> Result<(), Box> {
} else {
TEMPLATE_DEFAULT_PATH
};
- let template = load_template(template_path).unwrap();
- write_project_to_fs(template.files, &format!("{}/files", base_path)).unwrap();
+ let template = load_template(template_path)?;
+ write_project_to_fs(template.files, &format!("{}/files", base_path))?;
let mut helpers = vec![];
for helper in template.helpers {
helpers.push(TemplateFile {
@@ -54,11 +54,10 @@ fn main() -> Result<(), Box> {
content: Content::String(helper.script),
});
}
- write_project_to_fs(helpers, base_path).unwrap();
- let metadata = serde_json::to_string(&template.metadata).unwrap();
- let mut metadata_file =
- File::create(format!("{}/metadata.json", base_path)).unwrap();
- metadata_file.write_all(metadata.as_bytes()).unwrap();
+ write_project_to_fs(helpers, base_path)?;
+ let metadata = serde_json::to_string(&template.metadata)?;
+ let mut metadata_file = File::create(format!("{}/metadata.json", base_path))?;
+ metadata_file.write_all(metadata.as_bytes())?;
println!("Template Unpacked!");
}
command if command == "create-project" => {
@@ -72,10 +71,10 @@ fn main() -> Result<(), Box> {
} else {
TEMPLATE_FILE_NAME
};
- let json_file_path = canonicalize(idl_path).unwrap();
- let file = File::open(json_file_path).unwrap();
+ let json_file_path = canonicalize(idl_path)?;
+ let file = File::open(json_file_path)?;
let idl: IDL = serde_json::from_reader(file).expect("error while reading json");
- generate_from_idl(".", idl, template_path).unwrap();
+ generate_from_idl(".", idl, template_path)?;
println!("Project Generated!");
}
_ => {
diff --git a/crate/Cargo.lock b/crate/Cargo.lock
index 52ccaf6..5dbca98 100644
--- a/crate/Cargo.lock
+++ b/crate/Cargo.lock
@@ -364,7 +364,7 @@ dependencies = [
[[package]]
name = "soda_sol"
-version = "0.0.5"
+version = "0.0.6"
dependencies = [
"bincode",
"handlebars",
diff --git a/crate/Cargo.toml b/crate/Cargo.toml
index dbffde5..c3a636c 100644
--- a/crate/Cargo.toml
+++ b/crate/Cargo.toml
@@ -1,10 +1,11 @@
[package]
name = "soda_sol"
-version = "0.0.5"
+version = "0.0.6"
edition = "2021"
license = "Apache-2.0"
description = "Generates Solana Projects from an IDL"
repository = "https://github.com/Web3-Builders-Alliance/soda"
+documentation = "https://docs.rs/soda_sol/latest/soda_sol/"
authors = ["Juan Patricio Marchetto"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/crate/src/error.rs b/crate/src/error.rs
new file mode 100644
index 0000000..e5525fe
--- /dev/null
+++ b/crate/src/error.rs
@@ -0,0 +1,90 @@
+use std::sync::{MutexGuard, PoisonError};
+
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+ #[error("soda_sol crate error: {message}")]
+ CustomError { message: String },
+}
+#[derive(serde::Serialize, serde::Deserialize)]
+struct ErrorWrapper {
+ error: String,
+}
+impl serde::Serialize for Error {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: serde::ser::Serializer,
+ {
+ let error_message = match self {
+ Error::CustomError { message } => message,
+ };
+ let wrapper = ErrorWrapper {
+ error: error_message.to_string(),
+ };
+ wrapper.serialize(serializer)
+ }
+}
+
+impl<'de> serde::Deserialize<'de> for Error {
+ fn deserialize(deserializer: D) -> Result
+ where
+ D: serde::de::Deserializer<'de>,
+ {
+ let wrapper = ErrorWrapper::deserialize(deserializer)?;
+ Ok(Error::CustomError {
+ message: wrapper.error,
+ })
+ }
+}
+
+impl From for Error {
+ fn from(err: bincode::Error) -> Error {
+ Error::CustomError {
+ message: err.to_string(),
+ }
+ }
+}
+
+impl From for Error {
+ fn from(err: std::io::Error) -> Error {
+ Error::CustomError {
+ message: err.to_string(),
+ }
+ }
+}
+
+impl From> for Error {
+ fn from(err: Result<(), std::io::Error>) -> Error {
+ match err {
+ Ok(_) => Error::CustomError {
+ message: "no error".to_string(),
+ },
+ Err(err) => Error::CustomError {
+ message: err.to_string(),
+ },
+ }
+ }
+}
+
+impl From for Error {
+ fn from(err: serde_json::Error) -> Error {
+ Error::CustomError {
+ message: err.to_string(),
+ }
+ }
+}
+
+impl From for Error {
+ fn from(err: handlebars::RenderError) -> Error {
+ Error::CustomError {
+ message: err.to_string(),
+ }
+ }
+}
+
+impl From>> for Error {
+ fn from(err: PoisonError>) -> Error {
+ Error::CustomError {
+ message: err.to_string(),
+ }
+ }
+}
diff --git a/crate/src/generate_from_idl.rs b/crate/src/generate_from_idl.rs
new file mode 100644
index 0000000..a41a11c
--- /dev/null
+++ b/crate/src/generate_from_idl.rs
@@ -0,0 +1,12 @@
+use crate::*;
+use std::path::PathBuf;
+
+pub fn generate_from_idl(base_path: &str, idl: IDL, template_path: &str) -> Result<(), Error> {
+ let template = if PathBuf::from(template_path).is_file() {
+ load_template(template_path)?
+ } else {
+ get_template_from_fs(template_path)?
+ };
+ let dinamyc_files = generate_project(template, &idl)?;
+ write_project_to_fs(dinamyc_files, base_path)
+}
diff --git a/crate/src/generate_project.rs b/crate/src/generate_project.rs
new file mode 100644
index 0000000..2b0df30
--- /dev/null
+++ b/crate/src/generate_project.rs
@@ -0,0 +1,75 @@
+use std::path::PathBuf;
+
+use crate::*;
+use helpers::{apply_user_helpers, create_handlebars_registry};
+
+pub fn generate_project(template: Template, idl: &IDL) -> Result, Error> {
+ let Template { files, helpers, .. } = template;
+ let mut handlebars = create_handlebars_registry();
+ apply_user_helpers(helpers, &mut handlebars)?;
+ let mut data: Data = idl.clone().into();
+ let mut dinamic_files = vec![];
+ for TemplateFile { path, content } in files {
+ if path.contains("{{#each") {
+ let breaks: Vec<(usize, &str)> = path.match_indices("{{#each").collect();
+ if breaks.len() % 2 == 0 {
+ let resultant: Vec<(usize, &(usize, &str))> = breaks.iter().enumerate().collect();
+ for (index, (break_index, _)) in resultant {
+ if index % 2 == 0 && breaks.len() > index + 1 {
+ let (close_index, _) = breaks[index + 1];
+ let (rest, last_part) = path.split_at(close_index + 9);
+ let (prev_part, exp) = rest.split_at(*break_index);
+ let expresion_whithout_last: String =
+ exp.get(0..exp.len() - 9).unwrap_or("").to_string();
+ let expresion = format!("{},{}", expresion_whithout_last, "{{/each}}");
+ let new_paths = handlebars.render_template(&expresion, idl)?;
+ let mut new_paths_with_template: Vec<(String, Content, Vec)> =
+ (new_paths)
+ .split(',')
+ .map(|middle_part| {
+ (
+ format!("{}{}{}", prev_part, middle_part, last_part),
+ content.clone(),
+ [middle_part.to_string()].to_vec(),
+ )
+ })
+ .collect();
+ new_paths_with_template.pop();
+ dinamic_files.append(&mut new_paths_with_template);
+ }
+ }
+ } else {
+ println!(
+ "WARN: skipping {} open and clossing #each doesn't match",
+ path
+ )
+ }
+ } else {
+ dinamic_files.push((path.clone(), content.clone(), [].to_vec()));
+ }
+ }
+ let mut project: Vec = vec![];
+ for (path, template, path_replacements) in dinamic_files {
+ data.path_replacements = path_replacements;
+ let file_path = if PathBuf::from(&path)
+ .extension()
+ .is_some_and(|ext| ext == "hbs")
+ {
+ handlebars.render_template(path.get(0..path.len() - 4).unwrap_or(""), &data)?
+ } else {
+ handlebars.render_template(&path, &data)?
+ };
+ let content: Content = match template {
+ Content::String(content) => {
+ structs::Content::String(handlebars.render_template(&content, &data)?)
+ }
+ Content::Vec(content) => structs::Content::Vec(content),
+ };
+
+ project.push(TemplateFile {
+ path: format!("/{}{}", &data.name, file_path),
+ content,
+ })
+ }
+ Ok(project)
+}
diff --git a/crate/src/get_template_from_fs.rs b/crate/src/get_template_from_fs.rs
new file mode 100644
index 0000000..9f782ae
--- /dev/null
+++ b/crate/src/get_template_from_fs.rs
@@ -0,0 +1,80 @@
+use crate::{Content, Error, Template, TemplateFile, TemplateHelper, TemplateMetadata};
+use std::{
+ fs::{read, read_to_string},
+ path::PathBuf,
+};
+use walkdir::WalkDir;
+
+pub fn get_template_from_fs(template_path: &str) -> Result {
+ let mut files = vec![];
+ for entry in WalkDir::new(format!("{}/files/", template_path)) {
+ match entry {
+ Ok(val) => {
+ let path = &format!("{}", val.path().display());
+ let is_file = PathBuf::from(path).is_file();
+ if is_file {
+ let content: Content = if PathBuf::from(path)
+ .extension()
+ .is_some_and(|ext| ext == "hbs")
+ {
+ Content::String(read_to_string(path.clone())?)
+ } else {
+ Content::Vec(read(path.clone())?)
+ };
+
+ files.push(TemplateFile {
+ path: path
+ .get(template_path.len() + 6..path.len())
+ .unwrap_or("")
+ .to_string(),
+ content,
+ });
+ }
+ }
+ Err(err) => {
+ return Err(Error::CustomError {
+ message: err.to_string(),
+ })
+ }
+ }
+ }
+ let mut helpers = vec![];
+ for entry in WalkDir::new(format!("{}/helpers/", template_path)) {
+ match entry {
+ Ok(val) => {
+ let path = format!("{}", val.path().to_string_lossy());
+ let is_file = PathBuf::from(&path).is_file();
+ if is_file {
+ let script = read_to_string(val.path())?;
+ let helper_name = path
+ .get(0..path.len() - 5)
+ .unwrap_or("")
+ .split('/')
+ .last()
+ .unwrap_or("")
+ .to_string();
+ helpers.push(TemplateHelper {
+ helper_name,
+ script,
+ });
+ }
+ }
+ Err(err) => {
+ return Err(Error::CustomError {
+ message: err.to_string(),
+ })
+ }
+ }
+ }
+ let metadata_path = format!("{}/metadata.json", template_path);
+ let metadata = if PathBuf::from(&metadata_path).is_file() {
+ serde_json::from_str(&read_to_string(metadata_path)?)?
+ } else {
+ TemplateMetadata::default()
+ };
+ Ok(Template {
+ files,
+ helpers,
+ metadata,
+ })
+}
diff --git a/crate/src/helpers.rs b/crate/src/helpers.rs
index 5d89d01..14734fe 100644
--- a/crate/src/helpers.rs
+++ b/crate/src/helpers.rs
@@ -1,6 +1,6 @@
-use crate::structs;
+use crate::{structs, Error};
use handlebars::{handlebars_helper, Handlebars};
-use structs::{InstructionType, InstructionTypeVec, TemplateHelper, VecEnum, IDL};
+use structs::{InstructionType, TemplateHelper, IDL};
pub(crate) fn create_handlebars_registry() -> Handlebars<'static> {
handlebars_helper!(snakecase: |name: String| name.chars().fold(
@@ -14,40 +14,21 @@ pub(crate) fn create_handlebars_registry() -> Handlebars<'static> {
);
handlebars_helper!(pascalcase: |name: String|{
- let mut passcalcaseChars: Vec = name.chars().collect();
- if passcalcaseChars.is_empty() {
+ let mut passcalcase_chars: Vec = name.chars().collect();
+ if passcalcase_chars.is_empty() {
"".to_string()
} else {
- let first: Vec = passcalcaseChars[0].to_uppercase().to_string().chars().collect();
- passcalcaseChars[0] = *first.first().unwrap();
- let passcalcase: String = passcalcaseChars.into_iter().collect();
+ let first: Vec = passcalcase_chars[0].to_uppercase().to_string().chars().collect();
+ passcalcase_chars[0] = *first.first().unwrap_or(&' ');
+ let passcalcase: String = passcalcase_chars.into_iter().collect();
passcalcase
}
}
);
- handlebars_helper!(type_from_account_field: |account_field_type: InstructionType|
- match account_field_type {
- InstructionType::String(name)=>name,
- InstructionType::vec(content)=>{
- format!("Vec{}{}{}", "<", match content {
- InstructionTypeVec::String(name)=>name,
- InstructionTypeVec::defined(content)=>content.defined,
- InstructionTypeVec::vec(content)=>{
- match content.vec {
- VecEnum::String(name)=>name,
- VecEnum::defined(content)=>{content.defined},
- }
- },
- },">")
- },
- InstructionType::defined(content)=>content.defined,
- InstructionType::option(content)=> content.option,
- }.replace("publicKey", "Pubkey")
- .replace("string", "String")
- );
+ handlebars_helper!(type_from_account_field: |account_field_type: InstructionType| type_from_account_field_impl(account_field_type));
- handlebars_helper!(debug_idl: |idl: IDL|serde_json::to_string(&idl).unwrap());
+ handlebars_helper!(debug_idl: |idl: IDL|serde_json::to_string(&idl).unwrap_or("".to_string()));
let mut handlebars = Handlebars::new();
@@ -58,14 +39,61 @@ pub(crate) fn create_handlebars_registry() -> Handlebars<'static> {
handlebars
}
-pub fn apply_user_helpers(helpers: Vec, handlebars: &mut handlebars::Handlebars) {
+pub fn apply_user_helpers(
+ helpers: Vec,
+ handlebars: &mut handlebars::Handlebars,
+) -> Result<(), Error> {
for TemplateHelper {
helper_name,
script,
} in helpers
{
- handlebars
- .register_script_helper(&helper_name, &script)
- .unwrap();
+ match handlebars.register_script_helper(&helper_name, &script) {
+ Ok(_) => {}
+ Err(err) => {
+ return Err(Error::CustomError {
+ message: format!("Error registering helper: {}", err),
+ })
+ }
+ }
}
+ Ok(())
+}
+
+fn type_from_account_field_impl(account_field_type: InstructionType) -> String {
+ match account_field_type {
+ InstructionType::String => "String".to_string(),
+ InstructionType::U8 => "u8".to_string(),
+ InstructionType::U16 => "u16".to_string(),
+ InstructionType::U32 => "u32".to_string(),
+ InstructionType::U64 => "u64".to_string(),
+ InstructionType::U128 => "u128".to_string(),
+ InstructionType::Bool => "bool".to_string(),
+ InstructionType::Vec(content) => format!("Vec<{}>", type_from_account_field_impl(*content)),
+ InstructionType::Option(option) => format!("Option<{}>", type_from_account_field_impl(*option)),
+ InstructionType::Defined(defined) => defined,
+ InstructionType::Array(content, number) => format!("[{}; {}]", type_from_account_field_impl(*content), number),
+ InstructionType::Bytes => "Vec".to_string(),
+ InstructionType::I128 => "i128".to_string(),
+ InstructionType::I16 => "i16".to_string(),
+ InstructionType::I32 => "i32".to_string(),
+ InstructionType::I64 => "i64".to_string(),
+ InstructionType::I8 => "i8".to_string(),
+ InstructionType::Tuple(content) => {
+ let mut tuple = "(".to_string();
+ for (i, inner_content) in content.iter().enumerate() {
+ tuple.push_str(&type_from_account_field_impl(inner_content.clone()));
+ if i < content.iter().len() - 1 {
+ tuple.push_str(", ");
+ }
+ }
+ tuple.push_str(")");
+ tuple
+ }
+ InstructionType::PublicKey => "Pubkey".to_string(),
+ InstructionType::HashMap(content_a, content_b) => format!("HashMap<{}, {}>", type_from_account_field_impl(*content_a), type_from_account_field_impl(*content_b)),
+ InstructionType::BTreeMap(content_a, content_b) => format!("BTreeMap<{}, {}>", type_from_account_field_impl(*content_a), type_from_account_field_impl(*content_b)),
+ InstructionType::HashSet(content) => format!("HashSet<{}>", type_from_account_field_impl(*content)),
+ InstructionType::BTreeSet(content) => format!("BTreeSet<{}>", type_from_account_field_impl(*content)),
+ }
}
diff --git a/crate/src/lib.rs b/crate/src/lib.rs
index 27831c2..dac04a6 100644
--- a/crate/src/lib.rs
+++ b/crate/src/lib.rs
@@ -1,232 +1,18 @@
-#![allow(non_snake_case, non_camel_case_types)]
-
-use std::{
- fmt::Error,
- fs::{create_dir_all, read, read_to_string, File},
- io::Write,
- path::PathBuf,
-};
-use walkdir::WalkDir;
mod helpers;
+pub mod error;
+pub mod generate_from_idl;
+pub mod generate_project;
+pub mod get_template_from_fs;
+pub mod load_template;
+pub mod save_template;
pub mod structs;
-use bincode::{deserialize, serialize};
-use helpers::{apply_user_helpers, create_handlebars_registry};
+pub mod write_project_to_fs;
+
+pub use error::Error;
+pub use generate_from_idl::generate_from_idl;
+pub use generate_project::generate_project;
+pub use get_template_from_fs::get_template_from_fs;
+pub use load_template::load_template;
+pub use save_template::save_template;
pub use structs::{Content, Data, Template, TemplateFile, TemplateHelper, TemplateMetadata, IDL};
-
-pub fn generate_from_idl(base_path: &str, idl: IDL, template_path: &str) -> Result<(), Error> {
- if PathBuf::from(template_path).is_file() {
- match load_template(template_path) {
- Ok(template) => {
- let dinamyc_files = generate_project(template, &idl);
- match write_project_to_fs(dinamyc_files, base_path) {
- Ok(_) => Ok(()),
- Err(_err) => Err(Error),
- }
- }
- Err(_err) => Err(Error),
- }
- } else {
- match get_template_from_fs(template_path) {
- Ok(template) => {
- let dinamyc_files = generate_project(template, &idl);
- match write_project_to_fs(dinamyc_files, base_path) {
- Ok(_) => Ok(()),
- Err(_err) => Err(Error),
- }
- }
- Err(_err) => Err(Error),
- }
- }
-}
-
-pub fn write_project_to_fs(dinamyc_files: Vec, base_path: &str) -> Result<(), Error> {
- for TemplateFile { path, content } in dinamyc_files {
- let path_with_base = format!("{}/{}", base_path, path);
- let prefix = std::path::Path::new(&path_with_base).parent().unwrap();
- match create_dir_all(prefix) {
- Ok(_) => match File::create(path_with_base) {
- Ok(mut file) => match content {
- Content::String(content) => match file.write_all(content.as_bytes()) {
- Ok(_) => {}
- Err(_err) => return Err(Error),
- },
- Content::Vec(content) => match file.write_all(&content) {
- Ok(_) => {}
- Err(_err) => return Err(Error),
- },
- },
- Err(_err) => return Err(Error),
- },
- Err(_err) => return Err(Error),
- }
- }
- Ok(())
-}
-
-pub fn get_template_from_fs(template_path: &str) -> Result {
- let mut files = vec![];
- for entry in WalkDir::new(format!("{}/files/", template_path)) {
- match entry {
- Ok(val) => {
- let path = &format!("{}", val.path().display());
- let is_file = PathBuf::from(path).is_file();
- if is_file {
- let content: Content = if PathBuf::from(path)
- .extension()
- .is_some_and(|ext| ext == "hbs")
- {
- structs::Content::String(read_to_string(path.clone()).unwrap())
- } else {
- structs::Content::Vec(read(path.clone()).unwrap())
- };
-
- files.push(TemplateFile {
- path: path
- .get(template_path.len() + 6..path.len())
- .unwrap()
- .to_string(),
- content,
- });
- }
- }
- Err(_err) => return Err(Error),
- }
- }
- let mut helpers = vec![];
- for entry in WalkDir::new(format!("{}/helpers/", template_path)) {
- match entry {
- Ok(val) => {
- let path = format!("{}", val.path().to_string_lossy());
- let is_file = PathBuf::from(&path).is_file();
- if is_file {
- let script = read_to_string(val.path()).unwrap();
- let helper_name = path
- .get(0..path.len() - 5)
- .unwrap()
- .split('/')
- .last()
- .unwrap()
- .to_string();
- helpers.push(TemplateHelper {
- helper_name,
- script,
- });
- }
- }
- Err(_err) => return Err(Error),
- }
- }
- let metadata = if PathBuf::from(format!("{}/metadata.json", template_path)).is_file() {
- match read_to_string(format!("{}/metadata.json", template_path)) {
- Ok(val) => match serde_json::from_str(&val) {
- Ok(decoded) => decoded,
- Err(_err) => TemplateMetadata::default(),
- },
- Err(_err) => TemplateMetadata::default(),
- }
- } else {
- TemplateMetadata::default()
- };
- Ok(Template {
- files,
- helpers,
- metadata,
- })
-}
-
-pub fn generate_project(template: Template, idl: &IDL) -> Vec {
- let Template { files, helpers, .. } = template;
- let mut handlebars = create_handlebars_registry();
- apply_user_helpers(helpers, &mut handlebars);
- let mut data: Data = idl.clone().into();
- let mut dinamic_files = vec![];
- for TemplateFile { path, content } in files {
- if path.contains("{{#each") {
- let breaks: Vec<(usize, &str)> = path.match_indices("{{#each").collect();
- if breaks.len() % 2 == 0 {
- let resultant: Vec<(usize, &(usize, &str))> = breaks.iter().enumerate().collect();
- for (index, (break_index, _)) in resultant {
- if index % 2 == 0 && breaks.len() > index + 1 {
- let (close_index, _) = breaks[index + 1];
- let (rest, last_part) = path.split_at(close_index + 9);
- let (prev_part, exp) = rest.split_at(*break_index);
- let expresion_whithout_last: String =
- exp.get(0..exp.len() - 9).unwrap().to_string();
- let expresion = format!("{},{}", expresion_whithout_last, "{{/each}}");
- let new_paths = handlebars.render_template(&expresion, idl);
- let new_paths_unwrapped = new_paths.unwrap();
- let mut new_paths_with_template: Vec<(String, Content, Vec)> =
- (new_paths_unwrapped)
- .split(',')
- .map(|middle_part| {
- (
- format!("{}{}{}", prev_part, middle_part, last_part),
- content.clone(),
- [middle_part.to_string()].to_vec(),
- )
- })
- .collect();
- new_paths_with_template.pop();
- dinamic_files.append(&mut new_paths_with_template);
- }
- }
- } else {
- println!(
- "WARN: skipping {} open and clossing #each doesn't match",
- path
- )
- }
- } else {
- dinamic_files.push((path.clone(), content.clone(), [].to_vec()));
- }
- }
- let mut project: Vec = vec![];
- for (path, template, path_replacements) in dinamic_files {
- data.path_replacements = path_replacements;
- let file_path = if PathBuf::from(&path)
- .extension()
- .is_some_and(|ext| ext == "hbs")
- {
- handlebars
- .render_template(path.get(0..path.len() - 4).unwrap(), &data)
- .unwrap()
- } else {
- handlebars.render_template(&path, &data).unwrap()
- };
- let content: Content = match template {
- Content::String(content) => {
- structs::Content::String(handlebars.render_template(&content, &data).unwrap())
- }
- Content::Vec(content) => structs::Content::Vec(content),
- };
-
- project.push(TemplateFile {
- path: format!("/{}{}", &data.name, file_path),
- content,
- })
- }
- project
-}
-
-pub fn save_template(template: Template, path: &str) -> Result<(), Error> {
- match std::fs::File::create(path) {
- Ok(mut file) => match serialize(&template) {
- Ok(encoded) => match file.write_all(&encoded) {
- Err(_err) => Err(Error),
- Ok(_) => Ok(()),
- },
- Err(_err) => Err(Error),
- },
- Err(_err) => Err(Error),
- }
-}
-
-pub fn load_template(path: &str) -> Result {
- match &read(path) {
- Ok(val) => match deserialize(val) {
- Ok(decoded) => Ok(decoded),
- Err(_err) => Err(Error),
- },
- Err(_err) => Err(Error),
- }
-}
+pub use write_project_to_fs::write_project_to_fs;
diff --git a/crate/src/load_template.rs b/crate/src/load_template.rs
new file mode 100644
index 0000000..9f65920
--- /dev/null
+++ b/crate/src/load_template.rs
@@ -0,0 +1,9 @@
+use crate::{Error, Template};
+use bincode::deserialize;
+use std::fs::read;
+
+pub fn load_template(path: &str) -> Result {
+ let bytes = read(path)?;
+ let template: Template = deserialize(&bytes)?;
+ Ok(template)
+}
diff --git a/crate/src/save_template.rs b/crate/src/save_template.rs
new file mode 100644
index 0000000..8bdc360
--- /dev/null
+++ b/crate/src/save_template.rs
@@ -0,0 +1,8 @@
+use crate::{Error, Template};
+use bincode::serialize;
+use std::io::Write;
+
+pub fn save_template(template: Template, path: &str) -> Result<(), Error> {
+ let mut file = std::fs::File::create(path)?;
+ Ok(file.write_all(&serialize(&template)?)?)
+}
diff --git a/crate/src/structs.rs b/crate/src/structs.rs
index 9926543..adaac50 100644
--- a/crate/src/structs.rs
+++ b/crate/src/structs.rs
@@ -1,20 +1,22 @@
+#![allow(non_snake_case, non_camel_case_types)]
+
use serde_derive::{self, Deserialize, Serialize};
use std::convert::From;
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct IDL {
- pub(crate) version: String,
- pub(crate) name: String,
- pub(crate) instructions: Vec,
+ pub version: String,
+ pub name: String,
+ pub instructions: Vec,
#[serde(default)]
- pub(crate) accounts: Vec,
+ pub accounts: Vec,
#[serde(default)]
- pub(crate) types: Vec,
+ pub types: Vec,
#[serde(default)]
- pub(crate) events: Vec,
+ pub events: Vec,
#[serde(default)]
- pub(crate) errors: Vec,
+ pub errors: Vec,
#[serde(default)]
- pub(crate) metadata: Metadata,
+ pub metadata: Metadata,
}
#[derive(Deserialize, Serialize, Debug)]
@@ -134,6 +136,7 @@ pub struct Accounts {
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Types {
+ #[serde(default)]
pub(crate) name: String,
#[serde(default)]
#[serde(rename = "type")]
@@ -142,6 +145,7 @@ pub struct Types {
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Event {
+ #[serde(default)]
pub(crate) name: String,
#[serde(default)]
pub(crate) fields: Vec,
@@ -192,27 +196,36 @@ impl Default for Type {
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Kind {
pub(crate) kind: String,
- #[serde(default)]
- pub(crate) variants: Vec,
+ pub(crate) variants: Option>,
+ pub(crate) fields: Option>,
}
impl Default for Kind {
fn default() -> Self {
Kind {
kind: "struct".to_string(),
- variants: [].to_vec(),
+ variants: None,
+ fields: Some([].to_vec()),
}
}
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Field {
+ #[serde(default)]
pub(crate) name: String,
#[serde(rename = "type")]
pub(crate) type_: InstructionType,
pub(crate) index: bool,
}
+#[derive(Deserialize, Serialize, Debug, Clone)]
+pub struct KindField {
+ pub(crate) name: String,
+ #[serde(rename = "type")]
+ pub(crate) type_: InstructionType,
+}
+
#[derive(Deserialize, Serialize, Debug, Clone, Default)]
pub struct PDA {
pub(crate) seeds: Vec,
@@ -222,7 +235,7 @@ pub struct PDA {
pub struct Seed {
pub(crate) kind: String,
#[serde(rename = "type")]
- pub(crate) type_: String,
+ pub(crate) type_: VecEnum,
#[serde(default)]
pub(crate) value: String,
#[serde(default)]
@@ -230,17 +243,36 @@ pub struct Seed {
}
#[derive(Deserialize, Serialize, Debug, Clone)]
-#[serde(untagged)]
+#[serde(rename_all = "camelCase")]
pub enum InstructionType {
- String(String),
- vec(InstructionTypeVec),
- defined(Defined),
- option(OptionType),
+ Array(Box, usize),
+ Bool,
+ Bytes,
+ Defined(String),
+ I128,
+ I16,
+ I32,
+ I64,
+ I8,
+ Option(Box),
+ Tuple(Vec),
+ PublicKey,
+ String,
+ U128,
+ U16,
+ U32,
+ U64,
+ U8,
+ Vec(Box),
+ HashMap(Box, Box),
+ BTreeMap(Box, Box),
+ HashSet(Box),
+ BTreeSet(Box),
}
impl Default for InstructionType {
fn default() -> Self {
- InstructionType::String("".to_string())
+ InstructionType::String
}
}
@@ -250,6 +282,7 @@ pub enum InstructionTypeVec {
String(String),
defined(Defined),
vec(Vec_),
+ u128(u128),
}
#[derive(Deserialize, Serialize, Debug, Clone)]
@@ -289,7 +322,7 @@ pub struct TypeFields {
#[derive(Deserialize, Serialize, Debug, Clone, Default)]
pub struct Metadata {
- pub(crate) address: String,
+ pub address: String,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
diff --git a/crate/src/write_project_to_fs.rs b/crate/src/write_project_to_fs.rs
new file mode 100644
index 0000000..1eb88b5
--- /dev/null
+++ b/crate/src/write_project_to_fs.rs
@@ -0,0 +1,21 @@
+use crate::{Content, Error, TemplateFile};
+use std::{
+ fs::{create_dir_all, File},
+ io::Write,
+};
+
+pub fn write_project_to_fs(dinamyc_files: Vec, base_path: &str) -> Result<(), Error> {
+ for TemplateFile { path, content } in dinamyc_files {
+ let path_with_base = format!("{}/{}", &base_path, path);
+ let prefix = std::path::Path::new(&path_with_base)
+ .parent()
+ .unwrap_or(std::path::Path::new(&base_path));
+ create_dir_all(prefix)?;
+ let mut file = File::create(path_with_base)?;
+ match content {
+ Content::String(content) => file.write_all(content.as_bytes())?,
+ Content::Vec(content) => file.write_all(&content)?,
+ };
+ }
+ Ok(())
+}
diff --git a/crate/tests/generate_project.rs b/crate/tests/generate_project.rs
new file mode 100644
index 0000000..430634c
--- /dev/null
+++ b/crate/tests/generate_project.rs
@@ -0,0 +1,56 @@
+use soda_sol::{generate_project, structs::Metadata, Template, IDL};
+
+fn empty_idl() -> IDL {
+ IDL {
+ name: "test".to_string(),
+ version: "test".to_string(),
+ instructions: vec![],
+ accounts: vec![],
+ types: vec![],
+ events: vec![],
+ errors: vec![],
+ metadata: Metadata {
+ address: "".to_string(),
+ },
+ }
+}
+
+fn empty_template() -> Template {
+ Template {
+ files: vec![],
+ helpers: vec![],
+ metadata: soda_sol::TemplateMetadata {
+ name: "test".to_string(),
+ description: "test".to_string(),
+ version: "".to_string(),
+ authors: "".to_string(),
+ image: "".to_string(),
+ tags: "".to_string(),
+ },
+ }
+}
+
+#[test]
+fn generate_project_with_empty_template() {
+ assert_eq!(
+ generate_project(empty_template(), &empty_idl()).unwrap().len(),
+ 0
+ )
+}
+
+#[test]
+fn generate_project_with_empty_idl() {
+ assert_eq!(
+ generate_project(empty_template(), &empty_idl()).unwrap().len(),
+ 0
+ )
+}
+
+#[test]
+fn generate_from_fs() {
+ let template = soda_sol::load_template("./tests/test.soda").unwrap();
+ let idl_path = "./tests/idl.json";
+ let idl = serde_json::from_str::(&std::fs::read_to_string(idl_path).unwrap()).unwrap();
+
+ assert!(generate_project(template, &idl).is_ok())
+}
diff --git a/crate/tests/idl.json b/crate/tests/idl.json
new file mode 100644
index 0000000..5b2c15a
--- /dev/null
+++ b/crate/tests/idl.json
@@ -0,0 +1,197 @@
+{
+ "version": "0.1.0",
+ "name": "four_in_line",
+ "instructions": [
+ {
+ "name": "createGame",
+ "accounts": [
+ {
+ "name": "game",
+ "isMut": true,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "game"
+ },
+ {
+ "kind": "arg",
+ "type": "string",
+ "path": "name"
+ }
+ ]
+ }
+ },
+ {
+ "name": "payer",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "players",
+ "type": {
+ "vec": "publicKey"
+ }
+ },
+ {
+ "name": "gameType",
+ "type": "string"
+ }
+ ]
+ },
+ {
+ "name": "playGame",
+ "accounts": [
+ {
+ "name": "game",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "payer",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": [
+ {
+ "name": "play",
+ "type": "u8"
+ }
+ ]
+ }
+ ],
+ "accounts": [
+ {
+ "name": "Game",
+ "type": {
+ "kind": "struct",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "gameType",
+ "type": "string"
+ },
+ {
+ "name": "board",
+ "type": {
+ "vec": {
+ "defined": "Play"
+ }
+ }
+ },
+ {
+ "name": "players",
+ "type": {
+ "vec": "publicKey"
+ }
+ },
+ {
+ "name": "turn",
+ "type": "u8"
+ },
+ {
+ "name": "status",
+ "type": "string"
+ }
+ ]
+ }
+ }
+ ],
+ "types": [
+ {
+ "name": "Play",
+ "type": {
+ "kind": "enum",
+ "variants": [
+ {
+ "name": "Empty"
+ },
+ {
+ "name": "X"
+ },
+ {
+ "name": "O"
+ }
+ ]
+ }
+ }
+ ],
+ "events": [
+ {
+ "name": "GameCreated",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ }
+ ]
+ },
+ {
+ "name": "GameUpdated",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "board",
+ "type": {
+ "vec": {
+ "defined": "Play"
+ }
+ },
+ "index": false
+ },
+ {
+ "name": "status",
+ "type": "string",
+ "index": false
+ }
+ ]
+ }
+ ],
+ "errors": [
+ {
+ "code": 6000,
+ "name": "IncorrectUser",
+ "msg": "Isn't your turn to play"
+ },
+ {
+ "code": 6001,
+ "name": "InvalidCell",
+ "msg": "You can't use this cell now"
+ },
+ {
+ "code": 6002,
+ "name": "FinishedGame",
+ "msg": "You can't play, this game status is ended"
+ }
+ ],
+ "metadata": {
+ "address": "D17ocZkgrhNYizSp415zod3Nsd7BHBqjSTAFnPU9WG8A"
+ }
+}
\ No newline at end of file
diff --git a/crate/tests/idl_struct.rs b/crate/tests/idl_struct.rs
new file mode 100644
index 0000000..3c91c6d
--- /dev/null
+++ b/crate/tests/idl_struct.rs
@@ -0,0 +1,15 @@
+use soda_sol::structs::IDL;
+use walkdir::WalkDir;
+
+pub fn load_idl(idl_path: &str) -> Result> {
+ let idl = serde_json::from_str::(&std::fs::read_to_string(idl_path)?)?;
+ Ok(idl)
+}
+
+#[test]
+ fn load_idl_from_fs() {
+ for idl_path in WalkDir::new("./tests/idls").into_iter().filter_map(|e| e.ok()).filter(|e| e.path().extension().unwrap_or_default() == "json") {
+ println!("Loading IDL: {}", idl_path.path().to_str().unwrap());
+ assert!(load_idl(idl_path.path().to_str().unwrap()).is_ok())
+ }
+ }
\ No newline at end of file
diff --git a/crate/tests/idls/1NKyU3qShZC3oJgvCCftAHDi5TFxcJwfyUz2FeZsiwE.json b/crate/tests/idls/1NKyU3qShZC3oJgvCCftAHDi5TFxcJwfyUz2FeZsiwE.json
new file mode 100644
index 0000000..b6b022b
--- /dev/null
+++ b/crate/tests/idls/1NKyU3qShZC3oJgvCCftAHDi5TFxcJwfyUz2FeZsiwE.json
@@ -0,0 +1 @@
+{"version":"0.5.0","name":"crate_redeem_in_kind","instructions":[{"name":"redeem","accounts":[{"name":"withdrawAuthority","isMut":false,"isSigner":false},{"name":"crateToken","isMut":false,"isSigner":false},{"name":"crateMint","isMut":true,"isSigner":false},{"name":"crateSource","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"crateTokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]}],"events":[{"name":"RedeemEvent","fields":[{"name":"crateKey","type":"publicKey","index":true},{"name":"source","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]}]}
\ No newline at end of file
diff --git a/crate/tests/idls/ARoWLTBWoWrKMvxEiaE2EH9DrWyV7mLpKywGDWxBGeq9.json b/crate/tests/idls/ARoWLTBWoWrKMvxEiaE2EH9DrWyV7mLpKywGDWxBGeq9.json
new file mode 100644
index 0000000..c466947
--- /dev/null
+++ b/crate/tests/idls/ARoWLTBWoWrKMvxEiaE2EH9DrWyV7mLpKywGDWxBGeq9.json
@@ -0,0 +1 @@
+{"version":"0.1.9","name":"arrow_sunny","instructions":[{"name":"newArrow","accounts":[{"name":"arrow","isMut":true,"isSigner":false},{"name":"arrowMint","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"beneficiary","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"vendorMint","isMut":false,"isSigner":false},{"name":"sunnyProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"vaultBump","type":"u8"}]},{"name":"initArrowInternalMiner","accounts":[{"name":"arrow","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"pool","isMut":false,"isSigner":false},{"name":"vault","isMut":false,"isSigner":false},{"name":"miner","accounts":[{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false}]},{"name":"mineProgram","isMut":false,"isSigner":false},{"name":"sunnyProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"internalMinerBump","type":"u8"}]},{"name":"initArrowVendorMiner","accounts":[{"name":"arrow","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"pool","isMut":false,"isSigner":false},{"name":"vault","isMut":false,"isSigner":false},{"name":"miner","accounts":[{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false}]},{"name":"mineProgram","isMut":false,"isSigner":false},{"name":"sunnyProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"vendorMinerBump","type":"u8"}]},{"name":"depositVendor","accounts":[{"name":"arrow","isMut":false,"isSigner":false},{"name":"arrowStake","accounts":[{"name":"arrowMint","isMut":true,"isSigner":false},{"name":"depositor","isMut":false,"isSigner":true},{"name":"depositorArrowTokens","isMut":true,"isSigner":false}]},{"name":"depositorStakedTokens","isMut":true,"isSigner":false},{"name":"vaultVendorTokenAccount","isMut":true,"isSigner":false},{"name":"vendorStake","accounts":[{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false}]},{"name":"pool","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mineProgram","isMut":false,"isSigner":false},{"name":"sunnyProgram","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"stakeInternal","accounts":[{"name":"arrow","isMut":false,"isSigner":false},{"name":"internalMint","isMut":true,"isSigner":false},{"name":"vaultInternalTokenAccount","isMut":true,"isSigner":false},{"name":"internalStake","accounts":[{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false}]},{"name":"pool","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mineProgram","isMut":false,"isSigner":false},{"name":"sunnyProgram","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false}],"args":[]},{"name":"unstakeInternal","accounts":[{"name":"arrowStake","accounts":[{"name":"arrowMint","isMut":true,"isSigner":false},{"name":"depositor","isMut":false,"isSigner":true},{"name":"depositorArrowTokens","isMut":true,"isSigner":false}]},{"name":"stake","accounts":[{"name":"arrow","isMut":false,"isSigner":false},{"name":"internalMint","isMut":true,"isSigner":false},{"name":"vaultInternalTokenAccount","isMut":true,"isSigner":false},{"name":"internalStake","accounts":[{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false}]},{"name":"pool","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mineProgram","isMut":false,"isSigner":false},{"name":"sunnyProgram","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false}]}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawVendorTokens","accounts":[{"name":"stake","accounts":[{"name":"arrow","isMut":false,"isSigner":false},{"name":"arrowStake","accounts":[{"name":"arrowMint","isMut":true,"isSigner":false},{"name":"depositor","isMut":false,"isSigner":true},{"name":"depositorArrowTokens","isMut":true,"isSigner":false}]},{"name":"depositorStakedTokens","isMut":true,"isSigner":false},{"name":"vaultVendorTokenAccount","isMut":true,"isSigner":false},{"name":"vendorStake","accounts":[{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false}]},{"name":"pool","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mineProgram","isMut":false,"isSigner":false},{"name":"sunnyProgram","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false}]},{"name":"sunnyPoolFeeDestination","isMut":true,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"claim","accounts":[{"name":"arrow","isMut":false,"isSigner":false},{"name":"vaultRewardsTokenAccount","isMut":true,"isSigner":false},{"name":"claimFeeTokenAccount","isMut":true,"isSigner":false},{"name":"stakeTokenAccount","isMut":true,"isSigner":false},{"name":"stake","accounts":[{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false}]},{"name":"mintWrapper","isMut":true,"isSigner":false},{"name":"minter","isMut":true,"isSigner":false},{"name":"rewardsTokenMint","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"mineProgram","isMut":false,"isSigner":false},{"name":"mintWrapperProgram","isMut":false,"isSigner":false},{"name":"sunnyProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawRewardsToBeneficiary","accounts":[{"name":"arrow","isMut":false,"isSigner":false},{"name":"beneficiaryAccount","isMut":true,"isSigner":false},{"name":"arrowFeeAccount","isMut":true,"isSigner":false},{"name":"sunnyPoolFeeAccount","isMut":true,"isSigner":false},{"name":"arrowStagingAccount","isMut":true,"isSigner":false},{"name":"vaultRewardsTokenAccount","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"sunnyProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Arrow","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"beneficiary","type":"publicKey"},{"name":"pool","type":"publicKey"},{"name":"vault","type":"publicKey"},{"name":"vendorMiner","type":{"defined":"ArrowMiner"}},{"name":"internalMiner","type":{"defined":"ArrowMiner"}}]}}],"types":[{"name":"ArrowMiner","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"rewarder","type":"publicKey"},{"name":"quarry","type":"publicKey"},{"name":"miner","type":"publicKey"},{"name":"minerVault","type":"publicKey"},{"name":"rewardsMint","type":"publicKey"},{"name":"mintWrapper","type":"publicKey"},{"name":"claimFeeTokenAccount","type":"publicKey"},{"name":"vaultStakedTokenAccount","type":"publicKey"},{"name":"vaultRewardsTokenAccount","type":"publicKey"},{"name":"sunnyPoolRewardsFeeAccount","type":"publicKey"}]}}],"events":[{"name":"NewArrowEvent","fields":[{"name":"arrow","type":"publicKey","index":false},{"name":"beneficiary","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"vendorMint","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"MintEvent","fields":[{"name":"arrow","type":"publicKey","index":false},{"name":"depositor","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"BurnEvent","fields":[{"name":"arrow","type":"publicKey","index":false},{"name":"depositor","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]}],"errors":[{"code":6000,"name":"InvalidRewardsMint","msg":"Invalid rewards mint."},{"code":6001,"name":"NewArrowMintAuthorityInvalid","msg":"The Arrow's mint's authority must be the arrow."},{"code":6002,"name":"NewArrowFreezeAuthorityInvalid","msg":"The Arrow's freeze authority must be the arrow."},{"code":6003,"name":"NewArrowNonZeroSupply","msg":"Arrow mint must have zero supply"},{"code":6004,"name":"NewArrowDecimalMismatch","msg":"Arrow decimals must match the staked token"},{"code":6005,"name":"InitArrowMinersAlreadyInitialized","msg":"Miners already initialized."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/AnatoLyYrd5iaAe36Lvq2oS4nuVDnRAb3KBVCARt4XiZ.json b/crate/tests/idls/AnatoLyYrd5iaAe36Lvq2oS4nuVDnRAb3KBVCARt4XiZ.json
new file mode 100644
index 0000000..dec4dbd
--- /dev/null
+++ b/crate/tests/idls/AnatoLyYrd5iaAe36Lvq2oS4nuVDnRAb3KBVCARt4XiZ.json
@@ -0,0 +1 @@
+{"version":"0.1.1","name":"venko","instructions":[{"name":"createStream","accounts":[{"name":"streamMint","isMut":true,"isSigner":false},{"name":"stream","isMut":true,"isSigner":false},{"name":"underlyingMint","isMut":false,"isSigner":false},{"name":"underlyingTokens","isMut":false,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"crateToken","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"crateTokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"streamBump","type":"u8"},{"name":"crateBump","type":"u8"},{"name":"startTs","type":"i64"},{"name":"cliffTs","type":"i64"},{"name":"endTs","type":"i64"},{"name":"revoker","type":"publicKey"}]},{"name":"redeem","accounts":[{"name":"streamMint","isMut":true,"isSigner":false},{"name":"stream","isMut":true,"isSigner":false},{"name":"sourceStreamTokens","isMut":true,"isSigner":false},{"name":"underlyingTokens","isMut":true,"isSigner":false},{"name":"destinationTokens","isMut":true,"isSigner":false},{"name":"crateToken","isMut":false,"isSigner":false},{"name":"userAuthority","isMut":false,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"crateTokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"revoke","accounts":[{"name":"stream","isMut":true,"isSigner":false},{"name":"crateToken","isMut":false,"isSigner":false},{"name":"underlyingTokens","isMut":true,"isSigner":false},{"name":"destinationTokens","isMut":true,"isSigner":false},{"name":"revoker","isMut":false,"isSigner":true},{"name":"crateTokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Stream","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"revoker","type":"publicKey"},{"name":"crateToken","type":"publicKey"},{"name":"underlyingMint","type":"publicKey"},{"name":"underlyingTokens","type":"publicKey"},{"name":"initialAmount","type":"u64"},{"name":"redeemedAmount","type":"u64"},{"name":"startTs","type":"i64"},{"name":"cliffTs","type":"i64"},{"name":"endTs","type":"i64"}]}}],"events":[{"name":"StreamCreateEvent","fields":[{"name":"stream","type":"publicKey","index":true},{"name":"mint","type":"publicKey","index":true},{"name":"amount","type":"u64","index":false},{"name":"startTs","type":"i64","index":false},{"name":"cliffTs","type":"i64","index":false},{"name":"endTs","type":"i64","index":false}]},{"name":"RedeemEvent","fields":[{"name":"stream","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"amountRemaining","type":"u64","index":false}]},{"name":"RevokeEvent","fields":[{"name":"stream","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"revoker","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"InvalidSchedule","msg":"Stream must end after its start time."},{"code":6001,"name":"InsufficientWithdrawalBalance","msg":"Insufficient withdrawal balance."},{"code":6002,"name":"InsufficientStreamTokens","msg":"Insufficient stream token balance."},{"code":6003,"name":"Irrevocable","msg":"Stream is irrevocable."},{"code":6004,"name":"NotRevoker","msg":"Must be revoker to perform this operation."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/BANKhiCgEYd7QmcWwPLkqvTuuLN6qEwXDZgTe6HEbwv1.json b/crate/tests/idls/BANKhiCgEYd7QmcWwPLkqvTuuLN6qEwXDZgTe6HEbwv1.json
new file mode 100644
index 0000000..93fe63b
--- /dev/null
+++ b/crate/tests/idls/BANKhiCgEYd7QmcWwPLkqvTuuLN6qEwXDZgTe6HEbwv1.json
@@ -0,0 +1 @@
+{"version":"0.1.4","name":"bankman","instructions":[{"name":"newBank","accounts":[{"name":"bank","isMut":true,"isSigner":false},{"name":"crateMint","isMut":false,"isSigner":false},{"name":"crateToken","isMut":true,"isSigner":false},{"name":"brrrIssueAuthority","isMut":false,"isSigner":false},{"name":"burnWithdrawAuthority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"admin","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"crateTokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"bankBump","type":"u8"},{"name":"crateBump","type":"u8"}]},{"name":"authorizeCollateral","accounts":[{"name":"bank","isMut":false,"isSigner":false},{"name":"collateral","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"curator","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"setCollateralHardCap","accounts":[{"name":"bank","isMut":false,"isSigner":false},{"name":"collateral","isMut":true,"isSigner":false},{"name":"curator","isMut":false,"isSigner":true}],"args":[{"name":"hardCap","type":"u64"}]},{"name":"setCurator","accounts":[{"name":"bank","isMut":true,"isSigner":false},{"name":"bankman","isMut":false,"isSigner":true},{"name":"nextCurator","isMut":false,"isSigner":false}],"args":[]},{"name":"setBankman","accounts":[{"name":"bank","isMut":true,"isSigner":false},{"name":"bankman","isMut":false,"isSigner":true},{"name":"nextBankman","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Bank","type":{"kind":"struct","fields":[{"name":"crateToken","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"crateMint","type":"publicKey"},{"name":"curator","type":"publicKey"},{"name":"bankman","type":"publicKey"}]}},{"name":"Collateral","type":{"kind":"struct","fields":[{"name":"bank","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"hardCap","type":"u64"}]}}],"events":[{"name":"NewBankEvent","fields":[{"name":"bank","type":"publicKey","index":false},{"name":"curator","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"AddCollateralEvent","fields":[{"name":"bank","type":"publicKey","index":false},{"name":"collateral","type":"publicKey","index":false},{"name":"curator","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"SetCuratorEvent","fields":[{"name":"bank","type":"publicKey","index":false},{"name":"curator","type":"publicKey","index":false},{"name":"previousCurator","type":"publicKey","index":false},{"name":"bankman","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"SetBankmanEvent","fields":[{"name":"bank","type":"publicKey","index":false},{"name":"bankman","type":"publicKey","index":false},{"name":"previousBankman","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"SetCollateralHardCapEvent","fields":[{"name":"bank","type":"publicKey","index":false},{"name":"collateral","type":"publicKey","index":false},{"name":"hardCap","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]}],"errors":[{"code":6000,"name":"UnauthorizedNotCurator","msg":"Must be curator."},{"code":6001,"name":"UnauthorizedNotBankman","msg":"Must be the bankman."},{"code":6002,"name":"PoolNotFoundInSnapshot","msg":"Pool not found in snapshot."},{"code":6003,"name":"PoolAlreadyAdded","msg":"Cannot add a pool that has already been added."},{"code":6004,"name":"NewBankSupplyMustBeZero","msg":"new_bank: supply must be zero"},{"code":6005,"name":"NewBankWrongDecimals","msg":"new_bank: cash must have 6 decimals"},{"code":6006,"name":"NewBankAlreadyInitialized","msg":"new_bank: crate already initialized"}]}
\ No newline at end of file
diff --git a/crate/tests/idls/BRRRot6ig147TBU6EGp7TMesmQrwu729CbG6qu2ZUHWm.json b/crate/tests/idls/BRRRot6ig147TBU6EGp7TMesmQrwu729CbG6qu2ZUHWm.json
new file mode 100644
index 0000000..5f831cd
--- /dev/null
+++ b/crate/tests/idls/BRRRot6ig147TBU6EGp7TMesmQrwu729CbG6qu2ZUHWm.json
@@ -0,0 +1 @@
+{"version":"0.1.4","name":"brrr","instructions":[{"name":"printCash","accounts":[{"name":"common","accounts":[{"name":"bank","isMut":false,"isSigner":false},{"name":"collateral","isMut":false,"isSigner":false},{"name":"crateToken","isMut":false,"isSigner":false},{"name":"crateMint","isMut":true,"isSigner":false},{"name":"crateCollateralTokens","isMut":true,"isSigner":false},{"name":"saberSwap","accounts":[{"name":"arrow","isMut":false,"isSigner":false},{"name":"saberSwap","isMut":false,"isSigner":false},{"name":"poolMint","isMut":false,"isSigner":false},{"name":"reserveA","isMut":false,"isSigner":false},{"name":"reserveB","isMut":false,"isSigner":false}]},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"crateTokenProgram","isMut":false,"isSigner":false}]},{"name":"depositor","isMut":true,"isSigner":true},{"name":"depositorSource","isMut":true,"isSigner":false},{"name":"mintDestination","isMut":true,"isSigner":false},{"name":"issueAuthority","isMut":false,"isSigner":false}],"args":[{"name":"depositAmount","type":"u64"}]},{"name":"burnCash","accounts":[{"name":"common","accounts":[{"name":"bank","isMut":false,"isSigner":false},{"name":"collateral","isMut":false,"isSigner":false},{"name":"crateToken","isMut":false,"isSigner":false},{"name":"crateMint","isMut":true,"isSigner":false},{"name":"crateCollateralTokens","isMut":true,"isSigner":false},{"name":"saberSwap","accounts":[{"name":"arrow","isMut":false,"isSigner":false},{"name":"saberSwap","isMut":false,"isSigner":false},{"name":"poolMint","isMut":false,"isSigner":false},{"name":"reserveA","isMut":false,"isSigner":false},{"name":"reserveB","isMut":false,"isSigner":false}]},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"crateTokenProgram","isMut":false,"isSigner":false}]},{"name":"burner","isMut":true,"isSigner":true},{"name":"burnedCashSource","isMut":true,"isSigner":false},{"name":"withdrawDestination","isMut":true,"isSigner":false},{"name":"authorFeeDestination","isMut":true,"isSigner":false},{"name":"protocolFeeDestination","isMut":true,"isSigner":false},{"name":"withdrawAuthority","isMut":false,"isSigner":false}],"args":[{"name":"burnAmount","type":"u64"}]}],"events":[{"name":"PrintCashEvent","fields":[{"name":"depositor","type":"publicKey","index":false},{"name":"collateralMint","type":"publicKey","index":false},{"name":"printAmount","type":"u64","index":false},{"name":"depositAmount","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"BurnCashEvent","fields":[{"name":"burner","type":"publicKey","index":false},{"name":"collateralMint","type":"publicKey","index":false},{"name":"burnAmount","type":"u64","index":false},{"name":"withdrawAmount","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]}],"errors":[{"code":6000,"name":"CollateralHardCapHit","msg":"Too many of this LP token are being used as collateral."},{"code":6001,"name":"InsufficientFunds","msg":"Insufficient pool funds."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/CRATwLpu6YZEeiVq9ajjxs61wPQ9f29s1UoQR9siJCRs.json b/crate/tests/idls/CRATwLpu6YZEeiVq9ajjxs61wPQ9f29s1UoQR9siJCRs.json
new file mode 100644
index 0000000..2cf3736
--- /dev/null
+++ b/crate/tests/idls/CRATwLpu6YZEeiVq9ajjxs61wPQ9f29s1UoQR9siJCRs.json
@@ -0,0 +1 @@
+{"version":"0.5.0","name":"crate_token","instructions":[{"name":"newCrate","accounts":[{"name":"crateToken","isMut":true,"isSigner":false},{"name":"crateMint","isMut":false,"isSigner":false},{"name":"feeToSetter","isMut":false,"isSigner":false},{"name":"feeSetterAuthority","isMut":false,"isSigner":false},{"name":"issueAuthority","isMut":false,"isSigner":false},{"name":"withdrawAuthority","isMut":false,"isSigner":false},{"name":"authorFeeTo","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"setIssueFee","accounts":[{"name":"crateToken","isMut":true,"isSigner":false},{"name":"feeSetter","isMut":false,"isSigner":true}],"args":[{"name":"issueFeeBps","type":"u16"}]},{"name":"setWithdrawFee","accounts":[{"name":"crateToken","isMut":true,"isSigner":false},{"name":"feeSetter","isMut":false,"isSigner":true}],"args":[{"name":"withdrawFeeBps","type":"u16"}]},{"name":"setFeeTo","accounts":[{"name":"crateToken","isMut":true,"isSigner":false},{"name":"feeToSetter","isMut":false,"isSigner":true},{"name":"authorFeeTo","isMut":false,"isSigner":false}],"args":[]},{"name":"setFeeToSetter","accounts":[{"name":"crateToken","isMut":true,"isSigner":false},{"name":"feeToSetter","isMut":false,"isSigner":true},{"name":"nextFeeToSetter","isMut":false,"isSigner":false}],"args":[]},{"name":"issue","accounts":[{"name":"crateToken","isMut":false,"isSigner":false},{"name":"crateMint","isMut":true,"isSigner":false},{"name":"issueAuthority","isMut":false,"isSigner":true},{"name":"mintDestination","isMut":true,"isSigner":false},{"name":"authorFeeDestination","isMut":true,"isSigner":false},{"name":"protocolFeeDestination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"crateToken","isMut":false,"isSigner":false},{"name":"crateUnderlying","isMut":true,"isSigner":false},{"name":"withdrawAuthority","isMut":false,"isSigner":true},{"name":"withdrawDestination","isMut":true,"isSigner":false},{"name":"authorFeeDestination","isMut":true,"isSigner":false},{"name":"protocolFeeDestination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]}],"accounts":[{"name":"CrateToken","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"feeSetterAuthority","type":"publicKey"},{"name":"feeToSetter","type":"publicKey"},{"name":"issueAuthority","type":"publicKey"},{"name":"withdrawAuthority","type":"publicKey"},{"name":"authorFeeTo","type":"publicKey"},{"name":"issueFeeBps","type":"u16"},{"name":"withdrawFeeBps","type":"u16"}]}}],"types":[{"name":"ErrorCode","type":{"kind":"enum","variants":[{"name":"MaxFeeExceeded"},{"name":"InvalidFreezeAuthority"}]}}],"events":[{"name":"NewCrateEvent","fields":[{"name":"crateKey","type":"publicKey","index":false},{"name":"issueAuthority","type":"publicKey","index":false},{"name":"withdrawAuthority","type":"publicKey","index":false}]},{"name":"IssueEvent","fields":[{"name":"crateKey","type":"publicKey","index":false},{"name":"destination","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"authorFee","type":"u64","index":false},{"name":"protocolFee","type":"u64","index":false}]},{"name":"WithdrawEvent","fields":[{"name":"crateKey","type":"publicKey","index":false},{"name":"token","type":"publicKey","index":false},{"name":"destination","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"authorFee","type":"u64","index":false},{"name":"protocolFee","type":"u64","index":false}]}]}
\ No newline at end of file
diff --git a/crate/tests/idls/Crt7UoUR6QgrFrN7j8rmSQpUTNWNSitSwWvsWGf1qZ5t.json b/crate/tests/idls/Crt7UoUR6QgrFrN7j8rmSQpUTNWNSitSwWvsWGf1qZ5t.json
new file mode 100644
index 0000000..223bb2d
--- /dev/null
+++ b/crate/tests/idls/Crt7UoUR6QgrFrN7j8rmSQpUTNWNSitSwWvsWGf1qZ5t.json
@@ -0,0 +1 @@
+{"version":"1.1.2","name":"continuation_router","instructions":[{"name":"createAtaIfNotExists","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"ata","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"begin","accounts":[{"name":"continuation","isMut":true,"isSigner":false},{"name":"random","isMut":false,"isSigner":false},{"name":"input","isMut":false,"isSigner":false},{"name":"output","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"amountIn","type":"u64"},{"name":"minimumAmountOut","type":"u64"},{"name":"numSteps","type":"u16"}]},{"name":"beginV2","accounts":[{"name":"continuation","isMut":true,"isSigner":false},{"name":"input","isMut":false,"isSigner":false},{"name":"output","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}],"args":[{"name":"amountIn","type":"u64"},{"name":"minimumAmountOut","type":"u64"},{"name":"numSteps","type":"u16"}]},{"name":"end","accounts":[{"name":"continuation","isMut":true,"isSigner":false},{"name":"output","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":false}],"args":[]},{"name":"ssSwap","accounts":[{"name":"continuation","accounts":[{"name":"continuation","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}]},{"name":"action","accounts":[{"name":"swap","accounts":[{"name":"swap","isMut":false,"isSigner":false},{"name":"swapAuthority","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false}]},{"name":"input","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}]},{"name":"output","accounts":[{"name":"userToken","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}]},{"name":"fees","isMut":true,"isSigner":false}]}]}],"args":[]},{"name":"ssWithdrawOne","accounts":[{"name":"continuation","accounts":[{"name":"continuation","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}]},{"name":"action","accounts":[{"name":"swap","accounts":[{"name":"swap","isMut":false,"isSigner":false},{"name":"swapAuthority","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false}]},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"inputLp","isMut":true,"isSigner":false},{"name":"quoteReserves","isMut":true,"isSigner":false},{"name":"output","accounts":[{"name":"userToken","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}]},{"name":"fees","isMut":true,"isSigner":false}]}]}],"args":[]},{"name":"ssDepositA","accounts":[{"name":"continuation","accounts":[{"name":"continuation","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}]},{"name":"action","accounts":[{"name":"inner","accounts":[{"name":"swap","accounts":[{"name":"swap","isMut":false,"isSigner":false},{"name":"swapAuthority","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false}]},{"name":"inputA","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}]},{"name":"inputB","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}]},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"outputLp","isMut":true,"isSigner":false}]}]}],"args":[]},{"name":"ssDepositB","accounts":[{"name":"continuation","accounts":[{"name":"continuation","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}]},{"name":"action","accounts":[{"name":"inner","accounts":[{"name":"swap","accounts":[{"name":"swap","isMut":false,"isSigner":false},{"name":"swapAuthority","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false}]},{"name":"inputA","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}]},{"name":"inputB","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}]},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"outputLp","isMut":true,"isSigner":false}]}]}],"args":[]},{"name":"adWithdraw","accounts":[{"name":"continuation","accounts":[{"name":"continuation","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}]},{"name":"action","accounts":[{"name":"input","isMut":false,"isSigner":false},{"name":"output","isMut":false,"isSigner":false}]}],"args":[]},{"name":"adDeposit","accounts":[{"name":"continuation","accounts":[{"name":"continuation","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}]},{"name":"action","accounts":[{"name":"input","isMut":false,"isSigner":false},{"name":"output","isMut":false,"isSigner":false}]}],"args":[]}],"accounts":[{"name":"Continuation","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"payer","type":"publicKey"},{"name":"initialAmountIn","type":{"defined":"TokenAmount"}},{"name":"input","type":"publicKey"},{"name":"amountIn","type":{"defined":"TokenAmount"}},{"name":"stepsLeft","type":"u16"},{"name":"output","type":"publicKey"},{"name":"outputInitialBalance","type":"u64"},{"name":"minimumAmountOut","type":{"defined":"TokenAmount"}},{"name":"nonce","type":"u8"}]}}],"types":[{"name":"TokenAmount","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"amount","type":"u64"}]}},{"name":"ActionType","type":{"kind":"enum","variants":[{"name":"SSSwap"},{"name":"SSWithdrawOne"},{"name":"SSDepositA"},{"name":"SSDepositB"},{"name":"ADWithdraw"},{"name":"ADDeposit"}]}}],"events":[{"name":"SwapActionEvent","fields":[{"name":"actionType","type":{"defined":"ActionType"},"index":false},{"name":"owner","type":"publicKey","index":false},{"name":"inputAmount","type":{"defined":"TokenAmount"},"index":false},{"name":"outputAccount","type":"publicKey","index":false},{"name":"outputAmount","type":{"defined":"TokenAmount"},"index":false}]},{"name":"SwapCompleteEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"amountIn","type":{"defined":"TokenAmount"},"index":false},{"name":"amountOut","type":{"defined":"TokenAmount"},"index":false}]}],"errors":[{"code":6000,"name":"PathInputOutputMismatch","msg":"Path input does not match prior output."},{"code":6001,"name":"TransitiveSwapCalculationError","msg":"Error in a transitive swap input/output calculation."},{"code":6002,"name":"OverflowSwapResult","msg":"Swap result overflowed when checking balance difference."},{"code":6003,"name":"BalanceLower","msg":"Swap resulted in a balance lower than the original balance."},{"code":6004,"name":"ZeroSwap","msg":"Cannot perform a zero swap."},{"code":6005,"name":"InputOwnerMismatch","msg":"Input owner does not match continuation owner."},{"code":6006,"name":"InputMintMismatch","msg":"Input mint does not match continuation input mint."},{"code":6007,"name":"OutputOwnerMismatch","msg":"Output owner does not match continuation owner."},{"code":6008,"name":"NoMoreSteps","msg":"No more steps to process."},{"code":6009,"name":"InsufficientInputBalance","msg":"Insufficient input balance"},{"code":6010,"name":"EndIncomplete","msg":"Not all steps were processed."},{"code":6011,"name":"MinimumOutNotMet","msg":"Minimum amount out not met."},{"code":6012,"name":"OutputMintMismatch","msg":"Output mint does not match continuation output mint."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json b/crate/tests/idls/DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json
new file mode 100644
index 0000000..88375d0
--- /dev/null
+++ b/crate/tests/idls/DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json
@@ -0,0 +1 @@
+{"version":"1.1.2","name":"add_decimals","instructions":[{"name":"initializeWrapper","accounts":[{"name":"wrapper","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":false,"isSigner":false},{"name":"underlyingMint","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"deposit","accounts":[{"name":"wrapper","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"userWrappedTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"depositAmount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"wrapper","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"userWrappedTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxBurnAmount","type":"u64"}]},{"name":"withdrawAll","accounts":[{"name":"wrapper","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"userWrappedTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"WrappedToken","type":{"kind":"struct","fields":[{"name":"decimals","type":"u8"},{"name":"multiplier","type":"u64"},{"name":"wrapperUnderlyingMint","type":"publicKey"},{"name":"wrapperUnderlyingTokens","type":"publicKey"},{"name":"wrapperMint","type":"publicKey"},{"name":"nonce","type":"u8"}]}}],"events":[{"name":"InitEvent","fields":[{"name":"payer","type":"publicKey","index":false},{"name":"decimals","type":"u8","index":false},{"name":"multiplier","type":"u64","index":false},{"name":"wrapperUnderlyingMint","type":"publicKey","index":false},{"name":"wrapperUnderlyingTokens","type":"publicKey","index":false},{"name":"wrapperMint","type":"publicKey","index":false}]},{"name":"DepositEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"underlyingMint","type":"publicKey","index":false},{"name":"wrappedMint","type":"publicKey","index":false},{"name":"depositAmount","type":"u64","index":false},{"name":"mintAmount","type":"u64","index":false}]},{"name":"WithdrawEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"underlyingMint","type":"publicKey","index":false},{"name":"wrappedMint","type":"publicKey","index":false},{"name":"withdrawAmount","type":"u64","index":false},{"name":"burnAmount","type":"u64","index":false},{"name":"dustAmount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"InitNonEmptyAccount","msg":"Wrapper underlying tokens account must be empty."},{"code":6001,"name":"InitWrapperSupplyNonZero","msg":"Supply of the wrapper mint is non-zero"},{"code":6002,"name":"InitWrapperUnderlyingOwnerMismatch","msg":"Owner of the wrapper underlying tokens account must be the wrapper"},{"code":6003,"name":"InitWrapperUnderlyingMintMismatch","msg":"Underlying mint does not match underlying tokens account mint"},{"code":6004,"name":"InitMintAuthorityMismatch","msg":"Mint authority mismatch"},{"code":6005,"name":"InitMultiplierOverflow","msg":"Initial decimals too high"},{"code":6006,"name":"InitWrapperDecimalsTooLow","msg":"The number of target decimals must be greater than or equal to the underlying asset's decimals."},{"code":6007,"name":"MintAmountOverflow","msg":"Mint amount overflow. This error happens when the token cannot support this many decimals added to the token."},{"code":6008,"name":"InvalidBurnAmount","msg":"Failed to convert burn amount from withdraw amount."},{"code":6009,"name":"InvalidWithdrawAmount","msg":"Failed to convert withdraw amount from wrapped amount."},{"code":6010,"name":"InsufficientUnderlyingBalance","msg":"User does not have enough underlying tokens"},{"code":6011,"name":"InsufficientWrappedBalance","msg":"User does not have enough wrapped tokens"},{"code":6012,"name":"ZeroAmount","msg":"Cannot send zero tokens"},{"code":6013,"name":"UnknownAction","msg":"Unknown router action"},{"code":6014,"name":"InitFreezeAuthorityMismatch","msg":"Freeze authority mismatch"}]}
\ No newline at end of file
diff --git a/crate/tests/idls/GaugesLJrnVjNNWLReiw3Q7xQhycSBRgeHGTMDUaX231.json b/crate/tests/idls/GaugesLJrnVjNNWLReiw3Q7xQhycSBRgeHGTMDUaX231.json
new file mode 100644
index 0000000..014eaea
--- /dev/null
+++ b/crate/tests/idls/GaugesLJrnVjNNWLReiw3Q7xQhycSBRgeHGTMDUaX231.json
@@ -0,0 +1 @@
+{"version":"0.4.1","name":"gauge","instructions":[{"name":"createGaugemeister","accounts":[{"name":"gaugemeister","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Gaugemeister"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"base","isMut":false,"isSigner":true},{"name":"operator","isMut":false,"isSigner":false},{"name":"locker","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"foreman","type":"publicKey"},{"name":"epochDurationSeconds","type":"u32"},{"name":"firstEpochStartsAt","type":"u64"}]},{"name":"createGaugemeisterV2","accounts":[{"name":"gaugemeister","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Gaugemeister"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"base","isMut":false,"isSigner":true},{"name":"operator","isMut":false,"isSigner":false},{"name":"locker","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"foreman","type":"publicKey"},{"name":"epochDurationSeconds","type":"u32"},{"name":"firstEpochStartsAt","type":"u64"}]},{"name":"createGauge","accounts":[{"name":"gauge","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Gauge"},{"kind":"account","type":"publicKey","account":"Gaugemeister","path":"gaugemeister"},{"kind":"account","type":"publicKey","account":"quarry_mine :: Quarry","path":"quarry"}]}},{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"quarry","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"createGaugeV2","accounts":[{"name":"gauge","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Gauge"},{"kind":"account","type":"publicKey","account":"Gaugemeister","path":"gaugemeister"},{"kind":"account","type":"publicKey","account":"quarry_mine :: Quarry","path":"quarry"}]}},{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"quarry","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createGaugeVoter","accounts":[{"name":"gaugeVoter","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"GaugeVoter"},{"kind":"account","type":"publicKey","account":"Gaugemeister","path":"gaugemeister"},{"kind":"account","type":"publicKey","account":"locked_voter :: Escrow","path":"escrow"}]}},{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"escrow","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"createGaugeVoterV2","accounts":[{"name":"gaugeVoter","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"GaugeVoter"},{"kind":"account","type":"publicKey","account":"Gaugemeister","path":"gaugemeister"},{"kind":"account","type":"publicKey","account":"locked_voter :: Escrow","path":"escrow"}]}},{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"escrow","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createGaugeVote","accounts":[{"name":"gaugeVote","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"GaugeVote"},{"kind":"account","type":"publicKey","account":"GaugeVoter","path":"gauge_voter"},{"kind":"account","type":"publicKey","account":"Gauge","path":"gauge"}]}},{"name":"gaugeVoter","isMut":false,"isSigner":false},{"name":"gauge","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"createGaugeVoteV2","accounts":[{"name":"gaugeVote","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"GaugeVote"},{"kind":"account","type":"publicKey","account":"GaugeVoter","path":"gauge_voter"},{"kind":"account","type":"publicKey","account":"Gauge","path":"gauge"}]}},{"name":"gaugeVoter","isMut":false,"isSigner":false},{"name":"gauge","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createEpochGauge","accounts":[{"name":"gauge","isMut":false,"isSigner":false},{"name":"epochGauge","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"EpochGauge"},{"kind":"account","type":"publicKey","account":"Gauge","path":"gauge"},{"kind":"arg","type":"u32","path":"voting_epoch"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"votingEpoch","type":"u32"}]},{"name":"prepareEpochGaugeVoter","accounts":[{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"locker","isMut":false,"isSigner":false},{"name":"escrow","isMut":false,"isSigner":false},{"name":"gaugeVoter","isMut":false,"isSigner":false},{"name":"epochGaugeVoter","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"EpochGaugeVoter"},{"kind":"account","type":"publicKey","account":"GaugeVoter","path":"gauge_voter"},{"kind":"account","type":"publicKey","account":"Gaugemeister","path":"gaugemeister"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"prepareEpochGaugeVoterV2","accounts":[{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"locker","isMut":false,"isSigner":false},{"name":"escrow","isMut":false,"isSigner":false},{"name":"gaugeVoter","isMut":false,"isSigner":false},{"name":"epochGaugeVoter","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"EpochGaugeVoter"},{"kind":"account","type":"publicKey","account":"GaugeVoter","path":"gauge_voter"},{"kind":"account","type":"publicKey","account":"Gaugemeister","path":"gaugemeister"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"resetEpochGaugeVoter","accounts":[{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"locker","isMut":false,"isSigner":false},{"name":"escrow","isMut":false,"isSigner":false},{"name":"gaugeVoter","isMut":false,"isSigner":false},{"name":"epochGaugeVoter","isMut":true,"isSigner":false}],"args":[]},{"name":"gaugeSetVote","accounts":[{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"gauge","isMut":false,"isSigner":false},{"name":"gaugeVoter","isMut":true,"isSigner":false},{"name":"gaugeVote","isMut":true,"isSigner":false},{"name":"escrow","isMut":false,"isSigner":false},{"name":"voteDelegate","isMut":false,"isSigner":true}],"args":[{"name":"weight","type":"u32"}]},{"name":"gaugeCommitVote","accounts":[{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"gauge","isMut":false,"isSigner":false},{"name":"gaugeVoter","isMut":false,"isSigner":false},{"name":"gaugeVote","isMut":false,"isSigner":false},{"name":"epochGauge","isMut":true,"isSigner":false},{"name":"epochGaugeVoter","isMut":true,"isSigner":false},{"name":"epochGaugeVote","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"EpochGaugeVote"},{"kind":"account","type":"publicKey","account":"GaugeVote","path":"gauge_vote"},{"kind":"account","type":"u32","account":"EpochGaugeVoter","path":"epoch_gauge_voter.voting_epoch"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"voteBump","type":"u8"}]},{"name":"gaugeCommitVoteV2","accounts":[{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"gauge","isMut":false,"isSigner":false},{"name":"gaugeVoter","isMut":false,"isSigner":false},{"name":"gaugeVote","isMut":false,"isSigner":false},{"name":"epochGauge","isMut":true,"isSigner":false},{"name":"epochGaugeVoter","isMut":true,"isSigner":false},{"name":"epochGaugeVote","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"EpochGaugeVote"},{"kind":"account","type":"publicKey","account":"GaugeVote","path":"gauge_vote"},{"kind":"account","type":"u32","account":"EpochGaugeVoter","path":"epoch_gauge_voter.voting_epoch"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"gaugeRevertVote","accounts":[{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"gauge","isMut":false,"isSigner":false},{"name":"gaugeVoter","isMut":false,"isSigner":false},{"name":"gaugeVote","isMut":false,"isSigner":false},{"name":"epochGauge","isMut":true,"isSigner":false},{"name":"epochGaugeVoter","isMut":true,"isSigner":false},{"name":"escrow","isMut":false,"isSigner":false},{"name":"voteDelegate","isMut":false,"isSigner":true},{"name":"epochGaugeVote","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true}],"args":[]},{"name":"gaugeEnable","accounts":[{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"gauge","isMut":true,"isSigner":false},{"name":"foreman","isMut":false,"isSigner":true}],"args":[]},{"name":"gaugeDisable","accounts":[{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"gauge","isMut":true,"isSigner":false},{"name":"foreman","isMut":false,"isSigner":true}],"args":[]},{"name":"triggerNextEpoch","accounts":[{"name":"gaugemeister","isMut":true,"isSigner":false}],"args":[]},{"name":"syncGauge","accounts":[{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"gauge","isMut":false,"isSigner":false},{"name":"epochGauge","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"operator","isMut":true,"isSigner":false},{"name":"rewarder","isMut":true,"isSigner":false},{"name":"quarryMineProgram","isMut":false,"isSigner":false},{"name":"quarryOperatorProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"syncDisabledGauge","accounts":[{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"gauge","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"operator","isMut":true,"isSigner":false},{"name":"rewarder","isMut":true,"isSigner":false},{"name":"quarryMineProgram","isMut":false,"isSigner":false},{"name":"quarryOperatorProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setGaugemeisterParams","accounts":[{"name":"gaugemeister","isMut":true,"isSigner":false},{"name":"foreman","isMut":false,"isSigner":true}],"args":[{"name":"newEpochDurationSeconds","type":"u32"},{"name":"newForeman","type":"publicKey"}]},{"name":"closeEpochGaugeVote","accounts":[{"name":"epochGaugeVote","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"EpochGaugeVote"},{"kind":"account","type":"publicKey","account":"GaugeVote","path":"gauge_vote"},{"kind":"arg","type":"u32","path":"voting_epoch"}]}},{"name":"gaugemeister","isMut":false,"isSigner":false},{"name":"gauge","isMut":false,"isSigner":false},{"name":"gaugeVoter","isMut":false,"isSigner":false},{"name":"gaugeVote","isMut":false,"isSigner":false},{"name":"escrow","isMut":false,"isSigner":false},{"name":"voteDelegate","isMut":false,"isSigner":true},{"name":"recipient","isMut":true,"isSigner":false}],"args":[{"name":"votingEpoch","type":"u32"}]}],"accounts":[{"name":"Gaugemeister","type":{"kind":"struct","fields":[{"name":"base","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"rewarder","type":"publicKey"},{"name":"operator","type":"publicKey"},{"name":"locker","type":"publicKey"},{"name":"foreman","type":"publicKey"},{"name":"epochDurationSeconds","type":"u32"},{"name":"currentRewardsEpoch","type":"u32"},{"name":"nextEpochStartsAt","type":"u64"},{"name":"lockerTokenMint","type":"publicKey"},{"name":"lockerGovernor","type":"publicKey"}]}},{"name":"Gauge","type":{"kind":"struct","fields":[{"name":"gaugemeister","type":"publicKey"},{"name":"quarry","type":"publicKey"},{"name":"isDisabled","type":"bool"}]}},{"name":"GaugeVoter","type":{"kind":"struct","fields":[{"name":"gaugemeister","type":"publicKey"},{"name":"escrow","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"totalWeight","type":"u32"},{"name":"weightChangeSeqno","type":"u64"}]}},{"name":"GaugeVote","type":{"kind":"struct","fields":[{"name":"gaugeVoter","type":"publicKey"},{"name":"gauge","type":"publicKey"},{"name":"weight","type":"u32"}]}},{"name":"EpochGauge","type":{"kind":"struct","fields":[{"name":"gauge","type":"publicKey"},{"name":"votingEpoch","type":"u32"},{"name":"totalPower","type":"u64"}]}},{"name":"EpochGaugeVoter","type":{"kind":"struct","fields":[{"name":"gaugeVoter","type":"publicKey"},{"name":"votingEpoch","type":"u32"},{"name":"weightChangeSeqno","type":"u64"},{"name":"votingPower","type":"u64"},{"name":"allocatedPower","type":"u64"}]}},{"name":"EpochGaugeVote","type":{"kind":"struct","fields":[{"name":"allocatedPower","type":"u64"}]}}],"events":[{"name":"EpochGaugeCreateEvent","fields":[{"name":"gaugemeister","type":"publicKey","index":true},{"name":"quarry","type":"publicKey","index":true},{"name":"votingEpoch","type":"u32","index":false}]},{"name":"GaugeVoteCreateEvent","fields":[{"name":"gaugemeister","type":"publicKey","index":true},{"name":"gauge","type":"publicKey","index":true},{"name":"quarry","type":"publicKey","index":true},{"name":"gaugeVoterOwner","type":"publicKey","index":true}]},{"name":"GaugeVoterCreateEvent","fields":[{"name":"gaugemeister","type":"publicKey","index":true},{"name":"rewarder","type":"publicKey","index":true},{"name":"gaugeVoterOwner","type":"publicKey","index":true}]},{"name":"GaugeCreateEvent","fields":[{"name":"gaugemeister","type":"publicKey","index":true},{"name":"rewarder","type":"publicKey","index":true},{"name":"quarry","type":"publicKey","index":true},{"name":"gaugeVoterOwner","type":"publicKey","index":true}]},{"name":"GaugemeisterCreateEvent","fields":[{"name":"gaugemeister","type":"publicKey","index":false},{"name":"rewarder","type":"publicKey","index":false},{"name":"lockerTokenMint","type":"publicKey","index":false},{"name":"lockerGovernor","type":"publicKey","index":false},{"name":"foreman","type":"publicKey","index":false},{"name":"firstRewardsEpoch","type":"u64","index":false}]},{"name":"CommitGaugeVoteEvent","fields":[{"name":"gaugemeister","type":"publicKey","index":true},{"name":"gauge","type":"publicKey","index":true},{"name":"quarry","type":"publicKey","index":true},{"name":"gaugeVoterOwner","type":"publicKey","index":true},{"name":"votingEpoch","type":"u32","index":false},{"name":"voteSharesForNextEpoch","type":"u64","index":false},{"name":"updatedAllocatedPower","type":"u64","index":false},{"name":"updatedTotalPower","type":"u64","index":false}]},{"name":"GaugeDisableEvent","fields":[{"name":"gauge","type":"publicKey","index":false},{"name":"gaugemeister","type":"publicKey","index":false},{"name":"foreman","type":"publicKey","index":false}]},{"name":"GaugeEnableEvent","fields":[{"name":"gauge","type":"publicKey","index":false},{"name":"gaugemeister","type":"publicKey","index":false},{"name":"foreman","type":"publicKey","index":false}]},{"name":"RevertGaugeVoteEvent","fields":[{"name":"gaugemeister","type":"publicKey","index":true},{"name":"gauge","type":"publicKey","index":true},{"name":"quarry","type":"publicKey","index":true},{"name":"gaugeVoterOwner","type":"publicKey","index":true},{"name":"votingEpoch","type":"u32","index":false},{"name":"subtractedPower","type":"u64","index":false},{"name":"updatedAllocatedPower","type":"u64","index":false},{"name":"updatedTotalPower","type":"u64","index":false}]},{"name":"SetGaugeVoteEvent","fields":[{"name":"gaugemeister","type":"publicKey","index":true},{"name":"gauge","type":"publicKey","index":true},{"name":"quarry","type":"publicKey","index":true},{"name":"gaugeVoterOwner","type":"publicKey","index":true},{"name":"voteDelegate","type":"publicKey","index":true},{"name":"prevTotalWeight","type":"u32","index":false},{"name":"totalWeight","type":"u32","index":false},{"name":"weightChangeSeqno","type":"u64","index":false}]},{"name":"PrepareEpochGaugeVoterEvent","fields":[{"name":"gaugemeister","type":"publicKey","index":true},{"name":"rewarder","type":"publicKey","index":true},{"name":"locker","type":"publicKey","index":true},{"name":"gaugeVoterOwner","type":"publicKey","index":true},{"name":"votingEpoch","type":"u32","index":false},{"name":"votingPower","type":"u64","index":false},{"name":"weightChangeSeqno","type":"u64","index":false}]},{"name":"ResetEpochGaugeVoterEvent","fields":[{"name":"gaugemeister","type":"publicKey","index":true},{"name":"gaugeVoterOwner","type":"publicKey","index":true},{"name":"prevVotingPower","type":"u64","index":false},{"name":"votingPower","type":"u64","index":false},{"name":"prevWeightChangeSeqno","type":"u64","index":false},{"name":"weightChangeSeqno","type":"u64","index":false}]},{"name":"SetGaugemeisterParamsEvent","fields":[{"name":"prevForeman","type":"publicKey","index":false},{"name":"newForeman","type":"publicKey","index":false},{"name":"prevEpochDurationSeconds","type":"u32","index":false},{"name":"newEpochDurationSeconds","type":"u32","index":false}]},{"name":"SyncGaugeEvent","fields":[{"name":"gauge","type":"publicKey","index":false},{"name":"gaugemeister","type":"publicKey","index":false},{"name":"epoch","type":"u32","index":false},{"name":"previousShare","type":"u64","index":false},{"name":"newShare","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"UnauthorizedNotForeman","msg":"You must be the foreman to perform this action."},{"code":6001,"name":"GaugeEpochCannotBeZero","msg":"Cannot sync gauges at the 0th epoch."},{"code":6002,"name":"GaugeWrongEpoch","msg":"The gauge is not set to the current epoch."},{"code":6003,"name":"NextEpochNotReached","msg":"The start time for the next epoch has not yet been reached."},{"code":6004,"name":"CannotVoteMustReset","msg":"Must set all votes to 0 before changing votes."},{"code":6005,"name":"CannotVoteGaugeDisabled","msg":"Cannot vote since gauge is disabled; all you may do is set weight to 0."},{"code":6006,"name":"VoteAlreadyCommitted","msg":"You have already committed your vote to this gauge."},{"code":6007,"name":"CannotCommitGaugeDisabled","msg":"Cannot commit votes since gauge is disabled; all you may do is set weight to 0."},{"code":6008,"name":"EpochGaugeNotVoting","msg":"Voting on this epoch gauge is closed."},{"code":6009,"name":"WeightSeqnoChanged","msg":"Gauge voter voting weights have been modified since you started committing your votes. Please withdraw your votes and try again."},{"code":6010,"name":"EpochClosed","msg":"You may no longer modify votes for this epoch."},{"code":6011,"name":"AllocatedPowerMustBeZero","msg":"You must have zero allocated power in order to reset the epoch gauge."},{"code":6012,"name":"CloseEpochNotElapsed","msg":"The epoch in which you are closing an account for has not yet elapsed."},{"code":6013,"name":"UnauthorizedNotDelegate","msg":"You must be the vote delegate of the escrow to perform this action."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/GokivDYuQXPZCWRkwMhdH2h91KpDQXBEmpgBgs55bnpH.json b/crate/tests/idls/GokivDYuQXPZCWRkwMhdH2h91KpDQXBEmpgBgs55bnpH.json
new file mode 100644
index 0000000..3e98b36
--- /dev/null
+++ b/crate/tests/idls/GokivDYuQXPZCWRkwMhdH2h91KpDQXBEmpgBgs55bnpH.json
@@ -0,0 +1 @@
+{"version":"0.11.1","name":"smart_wallet","instructions":[{"name":"createSmartWallet","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"smartWallet","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"GokiSmartWallet"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"maxOwners","type":"u8"},{"name":"owners","type":{"vec":"publicKey"}},{"name":"threshold","type":"u64"},{"name":"minimumDelay","type":"i64"}]},{"name":"setOwners","accounts":[{"name":"smartWallet","isMut":true,"isSigner":true}],"args":[{"name":"owners","type":{"vec":"publicKey"}}]},{"name":"changeThreshold","accounts":[{"name":"smartWallet","isMut":true,"isSigner":true}],"args":[{"name":"threshold","type":"u64"}]},{"name":"createTransaction","accounts":[{"name":"smartWallet","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"GokiTransaction"},{"kind":"account","type":"publicKey","account":"SmartWallet","path":"smart_wallet"},{"kind":"account","type":"u64","account":"SmartWallet","path":"smart_wallet.num_transactions"}]}},{"name":"proposer","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"instructions","type":{"vec":{"defined":"TXInstruction"}}}]},{"name":"createTransactionWithTimelock","accounts":[{"name":"smartWallet","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"GokiTransaction"},{"kind":"account","type":"publicKey","account":"SmartWallet","path":"smart_wallet"},{"kind":"account","type":"u64","account":"SmartWallet","path":"smart_wallet.num_transactions"}]}},{"name":"proposer","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"instructions","type":{"vec":{"defined":"TXInstruction"}}},{"name":"eta","type":"i64"}]},{"name":"approve","accounts":[{"name":"smartWallet","isMut":false,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}],"args":[]},{"name":"unapprove","accounts":[{"name":"smartWallet","isMut":false,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}],"args":[]},{"name":"executeTransaction","accounts":[{"name":"smartWallet","isMut":false,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}],"args":[]},{"name":"executeTransactionDerived","accounts":[{"name":"smartWallet","isMut":false,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}],"args":[{"name":"index","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"ownerInvokeInstruction","accounts":[{"name":"smartWallet","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}],"args":[{"name":"index","type":"u64"},{"name":"bump","type":"u8"},{"name":"ix","type":{"defined":"TXInstruction"}}]},{"name":"ownerInvokeInstructionV2","accounts":[{"name":"smartWallet","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true}],"args":[{"name":"index","type":"u64"},{"name":"bump","type":"u8"},{"name":"invoker","type":"publicKey"},{"name":"data","type":"bytes"}]},{"name":"createSubaccountInfo","accounts":[{"name":"subaccountInfo","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"GokiSubaccountInfo"},{"kind":"arg","type":"publicKey","path":"subaccount"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"subaccount","type":"publicKey"},{"name":"smartWallet","type":"publicKey"},{"name":"index","type":"u64"},{"name":"subaccountType","type":{"defined":"SubaccountType"}}]}],"accounts":[{"name":"SmartWallet","type":{"kind":"struct","fields":[{"name":"base","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"threshold","type":"u64"},{"name":"minimumDelay","type":"i64"},{"name":"gracePeriod","type":"i64"},{"name":"ownerSetSeqno","type":"u32"},{"name":"numTransactions","type":"u64"},{"name":"owners","type":{"vec":"publicKey"}},{"name":"reserved","type":{"array":["u64",16]}}]}},{"name":"Transaction","type":{"kind":"struct","fields":[{"name":"smartWallet","type":"publicKey"},{"name":"index","type":"u64"},{"name":"bump","type":"u8"},{"name":"proposer","type":"publicKey"},{"name":"instructions","type":{"vec":{"defined":"TXInstruction"}}},{"name":"signers","type":{"vec":"bool"}},{"name":"ownerSetSeqno","type":"u32"},{"name":"eta","type":"i64"},{"name":"executor","type":"publicKey"},{"name":"executedAt","type":"i64"}]}},{"name":"SubaccountInfo","type":{"kind":"struct","fields":[{"name":"smartWallet","type":"publicKey"},{"name":"subaccountType","type":{"defined":"SubaccountType"}},{"name":"index","type":"u64"}]}}],"types":[{"name":"TXInstruction","type":{"kind":"struct","fields":[{"name":"programId","type":"publicKey"},{"name":"keys","type":{"vec":{"defined":"TXAccountMeta"}}},{"name":"data","type":"bytes"}]}},{"name":"TXAccountMeta","type":{"kind":"struct","fields":[{"name":"pubkey","type":"publicKey"},{"name":"isSigner","type":"bool"},{"name":"isWritable","type":"bool"}]}},{"name":"SubaccountType","type":{"kind":"enum","variants":[{"name":"Derived"},{"name":"OwnerInvoker"}]}}],"events":[{"name":"WalletCreateEvent","fields":[{"name":"smartWallet","type":"publicKey","index":false},{"name":"owners","type":{"vec":"publicKey"},"index":false},{"name":"threshold","type":"u64","index":false},{"name":"minimumDelay","type":"i64","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"WalletSetOwnersEvent","fields":[{"name":"smartWallet","type":"publicKey","index":false},{"name":"owners","type":{"vec":"publicKey"},"index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"WalletChangeThresholdEvent","fields":[{"name":"smartWallet","type":"publicKey","index":false},{"name":"threshold","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"TransactionCreateEvent","fields":[{"name":"smartWallet","type":"publicKey","index":false},{"name":"transaction","type":"publicKey","index":false},{"name":"proposer","type":"publicKey","index":false},{"name":"instructions","type":{"vec":{"defined":"TXInstruction"}},"index":false},{"name":"eta","type":"i64","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"TransactionApproveEvent","fields":[{"name":"smartWallet","type":"publicKey","index":false},{"name":"transaction","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"TransactionUnapproveEvent","fields":[{"name":"smartWallet","type":"publicKey","index":false},{"name":"transaction","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"TransactionExecuteEvent","fields":[{"name":"smartWallet","type":"publicKey","index":false},{"name":"transaction","type":"publicKey","index":false},{"name":"executor","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]}],"errors":[{"code":6000,"name":"InvalidOwner","msg":"The given owner is not part of this smart wallet."},{"code":6001,"name":"InvalidETA","msg":"Estimated execution block must satisfy delay."},{"code":6002,"name":"DelayTooHigh","msg":"Delay greater than the maximum."},{"code":6003,"name":"NotEnoughSigners","msg":"Not enough owners signed this transaction."},{"code":6004,"name":"TransactionIsStale","msg":"Transaction is past the grace period."},{"code":6005,"name":"TransactionNotReady","msg":"Transaction hasn't surpassed time lock."},{"code":6006,"name":"AlreadyExecuted","msg":"The given transaction has already been executed."},{"code":6007,"name":"InvalidThreshold","msg":"Threshold must be less than or equal to the number of owners."},{"code":6008,"name":"OwnerSetChanged","msg":"Owner set has changed since the creation of the transaction."},{"code":6009,"name":"SubaccountOwnerMismatch","msg":"Subaccount does not belong to smart wallet."},{"code":6010,"name":"BufferFinalized","msg":"Buffer already finalized."},{"code":6011,"name":"BufferBundleNotFound","msg":"Buffer bundle not found."},{"code":6012,"name":"BufferBundleOutOfRange","msg":"Buffer index specified is out of range."},{"code":6013,"name":"BufferBundleNotFinalized","msg":"Buffer has not been finalized."},{"code":6014,"name":"BufferBundleExecuted","msg":"Buffer bundle has already been executed."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/Govz1VyoyLD5BL6CSCxUJLVLsQHRwjfFj1prNsdNg5Jw.json b/crate/tests/idls/Govz1VyoyLD5BL6CSCxUJLVLsQHRwjfFj1prNsdNg5Jw.json
new file mode 100644
index 0000000..7562a79
--- /dev/null
+++ b/crate/tests/idls/Govz1VyoyLD5BL6CSCxUJLVLsQHRwjfFj1prNsdNg5Jw.json
@@ -0,0 +1 @@
+{"version":"0.5.6","name":"govern","instructions":[{"name":"createGovernor","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"governor","isMut":true,"isSigner":false},{"name":"smartWallet","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"electorate","type":"publicKey"},{"name":"params","type":{"defined":"GovernanceParameters"}}]},{"name":"createProposal","accounts":[{"name":"governor","isMut":true,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"proposer","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"instructions","type":{"vec":{"defined":"ProposalInstruction"}}}]},{"name":"activateProposal","accounts":[{"name":"governor","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"electorate","isMut":false,"isSigner":true}],"args":[]},{"name":"cancelProposal","accounts":[{"name":"governor","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"proposer","isMut":false,"isSigner":true}],"args":[]},{"name":"queueProposal","accounts":[{"name":"governor","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false},{"name":"smartWallet","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"smartWalletProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"txBump","type":"u8"}]},{"name":"newVote","accounts":[{"name":"proposal","isMut":false,"isSigner":false},{"name":"vote","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"voter","type":"publicKey"}]},{"name":"setVote","accounts":[{"name":"governor","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"vote","isMut":true,"isSigner":false},{"name":"electorate","isMut":false,"isSigner":true}],"args":[{"name":"side","type":"u8"},{"name":"weight","type":"u64"}]},{"name":"setGovernanceParams","accounts":[{"name":"governor","isMut":true,"isSigner":false},{"name":"smartWallet","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"GovernanceParameters"}}]},{"name":"setElectorate","accounts":[{"name":"governor","isMut":true,"isSigner":false},{"name":"smartWallet","isMut":false,"isSigner":true}],"args":[{"name":"newElectorate","type":"publicKey"}]},{"name":"createProposalMeta","accounts":[{"name":"proposal","isMut":false,"isSigner":false},{"name":"proposer","isMut":false,"isSigner":true},{"name":"proposalMeta","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"title","type":"string"},{"name":"descriptionLink","type":"string"}]}],"accounts":[{"name":"Governor","type":{"kind":"struct","fields":[{"name":"base","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"proposalCount","type":"u64"},{"name":"electorate","type":"publicKey"},{"name":"smartWallet","type":"publicKey"},{"name":"params","type":{"defined":"GovernanceParameters"}}]}},{"name":"Proposal","type":{"kind":"struct","fields":[{"name":"governor","type":"publicKey"},{"name":"index","type":"u64"},{"name":"bump","type":"u8"},{"name":"proposer","type":"publicKey"},{"name":"quorumVotes","type":"u64"},{"name":"forVotes","type":"u64"},{"name":"againstVotes","type":"u64"},{"name":"abstainVotes","type":"u64"},{"name":"canceledAt","type":"i64"},{"name":"createdAt","type":"i64"},{"name":"activatedAt","type":"i64"},{"name":"votingEndsAt","type":"i64"},{"name":"queuedAt","type":"i64"},{"name":"queuedTransaction","type":"publicKey"},{"name":"instructions","type":{"vec":{"defined":"ProposalInstruction"}}}]}},{"name":"ProposalMeta","type":{"kind":"struct","fields":[{"name":"proposal","type":"publicKey"},{"name":"title","type":"string"},{"name":"descriptionLink","type":"string"}]}},{"name":"Vote","type":{"kind":"struct","fields":[{"name":"proposal","type":"publicKey"},{"name":"voter","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"side","type":"u8"},{"name":"weight","type":"u64"}]}}],"types":[{"name":"GovernanceParameters","type":{"kind":"struct","fields":[{"name":"votingDelay","type":"u64"},{"name":"votingPeriod","type":"u64"},{"name":"quorumVotes","type":"u64"},{"name":"timelockDelaySeconds","type":"i64"}]}},{"name":"ProposalInstruction","type":{"kind":"struct","fields":[{"name":"programId","type":"publicKey"},{"name":"keys","type":{"vec":{"defined":"ProposalAccountMeta"}}},{"name":"data","type":"bytes"}]}},{"name":"ProposalAccountMeta","type":{"kind":"struct","fields":[{"name":"pubkey","type":"publicKey"},{"name":"isSigner","type":"bool"},{"name":"isWritable","type":"bool"}]}},{"name":"ProposalState","type":{"kind":"enum","variants":[{"name":"Draft"},{"name":"Active"},{"name":"Canceled"},{"name":"Defeated"},{"name":"Succeeded"},{"name":"Queued"}]}},{"name":"VoteSide","type":{"kind":"enum","variants":[{"name":"Pending"},{"name":"Against"},{"name":"For"},{"name":"Abstain"}]}}],"events":[{"name":"GovernorCreateEvent","fields":[{"name":"governor","type":"publicKey","index":false},{"name":"electorate","type":"publicKey","index":false},{"name":"smartWallet","type":"publicKey","index":false},{"name":"parameters","type":{"defined":"GovernanceParameters"},"index":false}]},{"name":"ProposalCreateEvent","fields":[{"name":"governor","type":"publicKey","index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"index","type":"u64","index":false},{"name":"instructions","type":{"vec":{"defined":"ProposalInstruction"}},"index":false}]},{"name":"ProposalActivateEvent","fields":[{"name":"governor","type":"publicKey","index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"votingEndsAt","type":"i64","index":false}]},{"name":"ProposalCancelEvent","fields":[{"name":"governor","type":"publicKey","index":false},{"name":"proposal","type":"publicKey","index":false}]},{"name":"ProposalQueueEvent","fields":[{"name":"governor","type":"publicKey","index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"transaction","type":"publicKey","index":false}]},{"name":"VoteSetEvent","fields":[{"name":"governor","type":"publicKey","index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"voter","type":"publicKey","index":false},{"name":"vote","type":"publicKey","index":false},{"name":"side","type":"u8","index":false},{"name":"weight","type":"u64","index":false}]},{"name":"ProposalMetaCreateEvent","fields":[{"name":"governor","type":"publicKey","index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"title","type":"string","index":false},{"name":"descriptionLink","type":"string","index":false}]},{"name":"GovernorSetParamsEvent","fields":[{"name":"governor","type":"publicKey","index":false},{"name":"prevParams","type":{"defined":"GovernanceParameters"},"index":false},{"name":"params","type":{"defined":"GovernanceParameters"},"index":false}]},{"name":"GovernorSetElectorateEvent","fields":[{"name":"governor","type":"publicKey","index":false},{"name":"prevElectorate","type":"publicKey","index":false},{"name":"newElectorate","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"InvalidVoteSide","msg":"Invalid vote side."},{"code":6001,"name":"GovernorNotFound","msg":"The owner of the smart wallet doesn't match with current."},{"code":6002,"name":"VotingDelayNotMet","msg":"The proposal cannot be activated since it has not yet passed the voting delay."},{"code":6003,"name":"ProposalNotDraft","msg":"Only drafts can be canceled."},{"code":6004,"name":"ProposalNotActive","msg":"The proposal must be active."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/LockKXdYQVMbhhckwH3BxoYJ9FYatcZjwNGEuCwY33Q.json b/crate/tests/idls/LockKXdYQVMbhhckwH3BxoYJ9FYatcZjwNGEuCwY33Q.json
new file mode 100644
index 0000000..750c8a6
--- /dev/null
+++ b/crate/tests/idls/LockKXdYQVMbhhckwH3BxoYJ9FYatcZjwNGEuCwY33Q.json
@@ -0,0 +1 @@
+{"version":"1.1.2","name":"lockup","instructions":[{"name":"availableForWithdrawal","accounts":[{"name":"release","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false}],"args":[]}],"state":{"struct":{"name":"Lockup","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"pendingOwner","type":"publicKey"}]}},"methods":[{"name":"new","accounts":[{"name":"auth","accounts":[{"name":"owner","isMut":false,"isSigner":true}]},{"name":"mintProxyState","isMut":false,"isSigner":false},{"name":"mintProxyProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createRelease","accounts":[{"name":"auth","accounts":[{"name":"owner","isMut":false,"isSigner":true}]},{"name":"minterInfo","isMut":false,"isSigner":false},{"name":"beneficiary","isMut":false,"isSigner":false},{"name":"release","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"mintProxyProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"releaseAmount","type":"u64"},{"name":"startTs","type":"i64"},{"name":"endTs","type":"i64"}]},{"name":"revokeRelease","accounts":[{"name":"auth","accounts":[{"name":"owner","isMut":false,"isSigner":true}]},{"name":"release","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":false}],"args":[]},{"name":"transferOwnership","accounts":[{"name":"owner","isMut":false,"isSigner":true}],"args":[{"name":"nextOwner","type":"publicKey"}]},{"name":"acceptOwnership","accounts":[{"name":"owner","isMut":false,"isSigner":true}],"args":[]},{"name":"withdraw","accounts":[{"name":"proxyMintAuthority","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"beneficiary","isMut":false,"isSigner":true},{"name":"release","isMut":true,"isSigner":false},{"name":"tokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"unusedClock","isMut":false,"isSigner":false},{"name":"minterInfo","isMut":true,"isSigner":false},{"name":"mintProxyProgram","isMut":false,"isSigner":false},{"name":"mintProxyState","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawWithAmount","accounts":[{"name":"proxyMintAuthority","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"beneficiary","isMut":false,"isSigner":true},{"name":"release","isMut":true,"isSigner":false},{"name":"tokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"unusedClock","isMut":false,"isSigner":false},{"name":"minterInfo","isMut":true,"isSigner":false},{"name":"mintProxyProgram","isMut":false,"isSigner":false},{"name":"mintProxyState","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]}]},"accounts":[{"name":"Release","type":{"kind":"struct","fields":[{"name":"beneficiary","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"mintProxyProgram","type":"publicKey"},{"name":"minterInfo","type":"publicKey"},{"name":"outstanding","type":"u64"},{"name":"startBalance","type":"u64"},{"name":"createdTs","type":"i64"},{"name":"startTs","type":"i64"},{"name":"endTs","type":"i64"},{"name":"nonce","type":"u8"}]}}],"events":[{"name":"ReleaseCreatedEvent","fields":[{"name":"beneficiary","type":"publicKey","index":true},{"name":"mint","type":"publicKey","index":true},{"name":"releaseAmount","type":"u64","index":false},{"name":"createdAt","type":"i64","index":false},{"name":"startAt","type":"i64","index":false},{"name":"endAt","type":"i64","index":false}]},{"name":"WithdrawEvent","fields":[{"name":"beneficiary","type":"publicKey","index":true},{"name":"mint","type":"publicKey","index":true},{"name":"outstandingAmount","type":"u64","index":false},{"name":"withdrawAmount","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]}],"errors":[{"code":6000,"name":"InvalidBeneficiary","msg":"The provided beneficiary was not valid."},{"code":6001,"name":"InvalidDepositAmount","msg":"The release deposit amount must be greater than zero."},{"code":6002,"name":"InvalidProgramAddress","msg":"The Whitelist entry is not a valid program address."},{"code":6003,"name":"InvalidSchedule","msg":"Invalid release schedule given."},{"code":6004,"name":"InvalidTokenMint","msg":"The provided token mint did not match the mint on the release account."},{"code":6005,"name":"InsufficientWithdrawalBalance","msg":"Insufficient withdrawal balance."},{"code":6006,"name":"Unauthorized","msg":"Unauthorized access."},{"code":6007,"name":"PendingOwnerMismatch","msg":"Pending owner mismatch."},{"code":6008,"name":"InvalidMintProxyProgram","msg":"The mint proxy program provided was not valid."},{"code":6009,"name":"MinterUnauthorized","msg":"The Release must be an authorized minter on the mint proxy."},{"code":6010,"name":"MinterInfoProgramMismatch","msg":"The minter info is not owned by the expected mint proxy."},{"code":6011,"name":"MinterAllowanceTooLow","msg":"The minter must have an allowance of at least the release amount."},{"code":6012,"name":"MinterInfoMismatch","msg":"Minter info mismatch"},{"code":6013,"name":"ReleaseMismatch","msg":"Release mismatch"},{"code":6014,"name":"ProxyMintAuthorityMismatch","msg":"Proxy mint authority mismatch"},{"code":6015,"name":"MintProxyMintMismatch","msg":"Mint proxy mint mismatch"},{"code":6016,"name":"DestinationMintMismatch","msg":"Withdraw destination mint mismatch"},{"code":6017,"name":"TokenProgramMismatch","msg":"Token program mismatch"},{"code":6018,"name":"ReleaseAlreadyRedeemedFrom","msg":"Release already redeemed from"},{"code":6019,"name":"U64Overflow","msg":"U64 overflow."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/LocktDzaV1W2Bm9DeZeiyz4J9zs4fRqNiYqQyracRXw.json b/crate/tests/idls/LocktDzaV1W2Bm9DeZeiyz4J9zs4fRqNiYqQyracRXw.json
new file mode 100644
index 0000000..7fb7421
--- /dev/null
+++ b/crate/tests/idls/LocktDzaV1W2Bm9DeZeiyz4J9zs4fRqNiYqQyracRXw.json
@@ -0,0 +1 @@
+{"version":"0.5.6","name":"locked_voter","instructions":[{"name":"newLocker","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"locker","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"governor","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"params","type":{"defined":"LockerParams"}}]},{"name":"newEscrow","accounts":[{"name":"locker","isMut":false,"isSigner":false},{"name":"escrow","isMut":true,"isSigner":false},{"name":"escrowOwner","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"lock","accounts":[{"name":"locker","isMut":true,"isSigner":false},{"name":"escrow","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"escrowOwner","isMut":false,"isSigner":true},{"name":"sourceTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"duration","type":"i64"}]},{"name":"lockWithWhitelist","accounts":[{"name":"lock","accounts":[{"name":"locker","isMut":true,"isSigner":false},{"name":"escrow","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"escrowOwner","isMut":false,"isSigner":true},{"name":"sourceTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}]},{"name":"instructionsSysvar","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"duration","type":"i64"}]},{"name":"lockWithWhitelistEntry","accounts":[{"name":"lock","accounts":[{"name":"locker","isMut":true,"isSigner":false},{"name":"escrow","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"escrowOwner","isMut":false,"isSigner":true},{"name":"sourceTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}]},{"name":"instructionsSysvar","isMut":false,"isSigner":false},{"name":"whitelistEntry","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"duration","type":"i64"}]},{"name":"lockPermissionless","accounts":[{"name":"locker","isMut":true,"isSigner":false},{"name":"escrow","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"escrowOwner","isMut":false,"isSigner":true},{"name":"sourceTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"duration","type":"i64"}]},{"name":"exit","accounts":[{"name":"locker","isMut":true,"isSigner":false},{"name":"escrow","isMut":true,"isSigner":false},{"name":"escrowOwner","isMut":false,"isSigner":true},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"destinationTokens","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"activateProposal","accounts":[{"name":"locker","isMut":false,"isSigner":false},{"name":"governor","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"escrow","isMut":false,"isSigner":false},{"name":"escrowOwner","isMut":false,"isSigner":true},{"name":"governProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"castVote","accounts":[{"name":"locker","isMut":false,"isSigner":false},{"name":"escrow","isMut":false,"isSigner":false},{"name":"voteDelegate","isMut":false,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false},{"name":"vote","isMut":true,"isSigner":false},{"name":"governor","isMut":false,"isSigner":false},{"name":"governProgram","isMut":false,"isSigner":false}],"args":[{"name":"side","type":"u8"}]},{"name":"setVoteDelegate","accounts":[{"name":"escrow","isMut":true,"isSigner":false},{"name":"escrowOwner","isMut":false,"isSigner":true}],"args":[{"name":"newDelegate","type":"publicKey"}]},{"name":"setLockerParams","accounts":[{"name":"locker","isMut":true,"isSigner":false},{"name":"governor","isMut":false,"isSigner":false},{"name":"smartWallet","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"LockerParams"}}]},{"name":"approveProgramLockPrivilege","accounts":[{"name":"locker","isMut":false,"isSigner":false},{"name":"whitelistEntry","isMut":true,"isSigner":false},{"name":"governor","isMut":false,"isSigner":false},{"name":"smartWallet","isMut":false,"isSigner":true},{"name":"executableId","isMut":false,"isSigner":false},{"name":"whitelistedOwner","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"revokeProgramLockPrivilege","accounts":[{"name":"locker","isMut":false,"isSigner":false},{"name":"whitelistEntry","isMut":true,"isSigner":false},{"name":"governor","isMut":false,"isSigner":false},{"name":"smartWallet","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true}],"args":[]}],"accounts":[{"name":"Locker","type":{"kind":"struct","fields":[{"name":"base","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"tokenMint","type":"publicKey"},{"name":"lockedSupply","type":"u64"},{"name":"governor","type":"publicKey"},{"name":"params","type":{"defined":"LockerParams"}}]}},{"name":"LockerWhitelistEntry","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"locker","type":"publicKey"},{"name":"programId","type":"publicKey"},{"name":"owner","type":"publicKey"}]}},{"name":"Escrow","type":{"kind":"struct","fields":[{"name":"locker","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"tokens","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"escrowStartedAt","type":"i64"},{"name":"escrowEndsAt","type":"i64"},{"name":"voteDelegate","type":"publicKey"}]}}],"types":[{"name":"LockerParams","type":{"kind":"struct","fields":[{"name":"whitelistEnabled","type":"bool"},{"name":"maxStakeVoteMultiplier","type":"u8"},{"name":"minStakeDuration","type":"u64"},{"name":"maxStakeDuration","type":"u64"},{"name":"proposalActivationMinVotes","type":"u64"}]}}],"events":[{"name":"ApproveLockPrivilegeEvent","fields":[{"name":"locker","type":"publicKey","index":false},{"name":"programId","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"ExitEscrowEvent","fields":[{"name":"escrowOwner","type":"publicKey","index":false},{"name":"locker","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false},{"name":"lockerSupply","type":"u64","index":false},{"name":"releasedAmount","type":"u64","index":false}]},{"name":"LockEvent","fields":[{"name":"locker","type":"publicKey","index":false},{"name":"escrowOwner","type":"publicKey","index":false},{"name":"tokenMint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"lockerSupply","type":"u64","index":false},{"name":"duration","type":"i64","index":false},{"name":"prevEscrowEndsAt","type":"i64","index":false},{"name":"nextEscrowEndsAt","type":"i64","index":false},{"name":"nextEscrowStartedAt","type":"i64","index":false}]},{"name":"NewEscrowEvent","fields":[{"name":"escrow","type":"publicKey","index":false},{"name":"escrowOwner","type":"publicKey","index":false},{"name":"locker","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"NewLockerEvent","fields":[{"name":"governor","type":"publicKey","index":false},{"name":"locker","type":"publicKey","index":false},{"name":"tokenMint","type":"publicKey","index":false},{"name":"params","type":{"defined":"LockerParams"},"index":false}]},{"name":"RevokeLockPrivilegeEvent","fields":[{"name":"locker","type":"publicKey","index":false},{"name":"programId","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"LockerSetParamsEvent","fields":[{"name":"locker","type":"publicKey","index":false},{"name":"prevParams","type":{"defined":"LockerParams"},"index":false},{"name":"params","type":{"defined":"LockerParams"},"index":false}]},{"name":"SetVoteDelegateEvent","fields":[{"name":"escrowOwner","type":"publicKey","index":false},{"name":"oldDelegate","type":"publicKey","index":false},{"name":"newDelegate","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"ProgramNotWhitelisted","msg":"CPI caller not whitelisted to invoke lock instruction."},{"code":6001,"name":"LockupDurationTooShort","msg":"Lockup duration must at least be the min stake duration."},{"code":6002,"name":"LockupDurationTooLong","msg":"Lockup duration must at most be the max stake duration."},{"code":6003,"name":"RefreshCannotShorten","msg":"A voting escrow refresh cannot shorten the escrow time remaining."},{"code":6004,"name":"EscrowNotEnded","msg":"Escrow has not ended."},{"code":6005,"name":"MustProvideWhitelist","msg":"Program whitelist enabled; please provide whitelist entry and instructions sysvar or use the 'lock_with_whitelist' instruction."},{"code":6006,"name":"EscrowOwnerNotWhitelisted","msg":"CPI caller not whitelisted for escrow owner to invoke lock instruction."},{"code":6007,"name":"MustCallLockWithWhitelistEntry","msg":"Must call `lock_with_whitelist_entry` to lock via CPI."},{"code":6008,"name":"MustCallLockPermissionless","msg":"Must call `lock_permissionless` since this DAO does not have a CPI whitelist."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/MRKGLMizK9XSTaD1d1jbVkdHZbQVCSnPpYiTw9aKQv8.json b/crate/tests/idls/MRKGLMizK9XSTaD1d1jbVkdHZbQVCSnPpYiTw9aKQv8.json
new file mode 100644
index 0000000..e27ef44
--- /dev/null
+++ b/crate/tests/idls/MRKGLMizK9XSTaD1d1jbVkdHZbQVCSnPpYiTw9aKQv8.json
@@ -0,0 +1 @@
+{"version":"0.3.3","name":"merkle_distributor","instructions":[{"name":"newDistributor","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"distributor","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"MerkleDistributor"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"mint","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"root","type":{"array":["u8",32]}},{"name":"maxTotalClaim","type":"u64"},{"name":"maxNumNodes","type":"u64"}]},{"name":"claim","accounts":[{"name":"distributor","isMut":true,"isSigner":false},{"name":"claimStatus","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"ClaimStatus"},{"kind":"arg","type":"u64","path":"index"},{"kind":"account","type":"publicKey","account":"MerkleDistributor","path":"distributor"}]}},{"name":"from","isMut":true,"isSigner":false},{"name":"to","isMut":true,"isSigner":false},{"name":"claimant","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"index","type":"u64"},{"name":"amount","type":"u64"},{"name":"proof","type":{"vec":{"array":["u8",32]}}}]}],"accounts":[{"name":"MerkleDistributor","type":{"kind":"struct","fields":[{"name":"base","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"root","type":{"array":["u8",32]}},{"name":"mint","type":"publicKey"},{"name":"maxTotalClaim","type":"u64"},{"name":"maxNumNodes","type":"u64"},{"name":"totalAmountClaimed","type":"u64"},{"name":"numNodesClaimed","type":"u64"}]}},{"name":"ClaimStatus","type":{"kind":"struct","fields":[{"name":"isClaimed","type":"bool"},{"name":"claimant","type":"publicKey"},{"name":"claimedAt","type":"i64"},{"name":"amount","type":"u64"}]}}],"events":[{"name":"ClaimedEvent","fields":[{"name":"index","type":"u64","index":false},{"name":"claimant","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"InvalidProof","msg":"Invalid Merkle proof."},{"code":6001,"name":"DropAlreadyClaimed","msg":"Drop already claimed."},{"code":6002,"name":"ExceededMaxClaim","msg":"Exceeded maximum claim amount."},{"code":6003,"name":"ExceededMaxNumNodes","msg":"Exceeded maximum number of claimed nodes."},{"code":6004,"name":"Unauthorized","msg":"Account is not authorized to execute this instruction"},{"code":6005,"name":"OwnerMismatch","msg":"Token account owner did not match intended owner"}]}
\ No newline at end of file
diff --git a/crate/tests/idls/NFTUJzSHuUCsMMqMRJpB7PmbsaU7Wm51acdPk2FXMLn.json b/crate/tests/idls/NFTUJzSHuUCsMMqMRJpB7PmbsaU7Wm51acdPk2FXMLn.json
new file mode 100644
index 0000000..e96700f
--- /dev/null
+++ b/crate/tests/idls/NFTUJzSHuUCsMMqMRJpB7PmbsaU7Wm51acdPk2FXMLn.json
@@ -0,0 +1 @@
+{"version":"0.11.1","name":"token_signer","instructions":[{"name":"invokeSignedInstruction","accounts":[{"name":"ownerAuthority","isMut":false,"isSigner":true},{"name":"nftAccount","isMut":false,"isSigner":false},{"name":"nftPda","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"GokiTokenSigner"},{"kind":"account","type":"publicKey","account":"TokenAccount","path":"nft_account.mint"}]}}],"args":[{"name":"data","type":"bytes"}]}],"errors":[{"code":6000,"name":"Unauthorized","msg":"Unauthorized."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/PLKZAXAYmZSfQv61tL2XALX4c14fcEhJj2CJhU1KcKd.json b/crate/tests/idls/PLKZAXAYmZSfQv61tL2XALX4c14fcEhJj2CJhU1KcKd.json
new file mode 100644
index 0000000..11f34ef
--- /dev/null
+++ b/crate/tests/idls/PLKZAXAYmZSfQv61tL2XALX4c14fcEhJj2CJhU1KcKd.json
@@ -0,0 +1 @@
+{"version":"0.1.4","name":"permalock","instructions":[{"name":"createPermalock","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"permalock","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Permalock"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"locker","isMut":false,"isSigner":false},{"name":"escrow","isMut":true,"isSigner":false},{"name":"permalockPendingTokens","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"lockedVoterProgram","isMut":false,"isSigner":false}],"args":[{"name":"escrowBump","type":"u8"}]},{"name":"refreshLock","accounts":[{"name":"permalock","isMut":false,"isSigner":false},{"name":"locker","isMut":true,"isSigner":false},{"name":"escrow","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"permalockPendingTokens","isMut":true,"isSigner":false},{"name":"lockedVoterProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setVoteDelegate","accounts":[{"name":"permalock","isMut":false,"isSigner":false},{"name":"escrow","isMut":true,"isSigner":false},{"name":"newDelegate","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"lockedVoterProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setOwner","accounts":[{"name":"permalock","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"newOwner","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Permalock","type":{"kind":"struct","fields":[{"name":"base","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",7]}},{"name":"escrow","type":"publicKey"},{"name":"stakedTokenMint","type":"publicKey"},{"name":"pendingTokens","type":"publicKey"},{"name":"owner","type":"publicKey"}]}}],"errors":[{"code":6000,"name":"UnauthorizedNotOwner","msg":"Only the owner of the Permalock may execute this instruction."},{"code":6001,"name":"UnauthorizedNotOperator","msg":"Only the operator of the Permalock may execute this instruction."},{"code":6002,"name":"CopyRewardsEpochIsZero","msg":"Current rewards epoch must be non-zero to copy."},{"code":6003,"name":"CopyWrongEpochGauge","msg":"Provided epoch gauge must be of the current rewards epoch."},{"code":6004,"name":"TAPExpired","msg":"The supplied token of appreciation may no longer be minted."},{"code":6005,"name":"GaugeMappingNotEnabled","msg":"The gauge mapping is not enabled."},{"code":6006,"name":"TAPDecimalMismatch","msg":"The TAP decimals must be equivalent to the staked token's decimals."},{"code":6007,"name":"UnauthorizedNotBenefactor","msg":"Only the benefactor of the TAP may execute this instruction."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/QMMD16kjauP5knBwxNUJRZ1Z5o3deBuFrqVjBVmmqto.json b/crate/tests/idls/QMMD16kjauP5knBwxNUJRZ1Z5o3deBuFrqVjBVmmqto.json
new file mode 100644
index 0000000..fc769f4
--- /dev/null
+++ b/crate/tests/idls/QMMD16kjauP5knBwxNUJRZ1Z5o3deBuFrqVjBVmmqto.json
@@ -0,0 +1 @@
+{"version":"5.0.2","name":"quarry_merge_mine","instructions":[{"name":"newPool","accounts":[{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"MergePool"},{"kind":"account","type":"publicKey","account":"Mint","path":"primary_mint"}]}},{"name":"primaryMint","isMut":false,"isSigner":false},{"name":"replicaMint","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"ReplicaMint"},{"kind":"account","type":"publicKey","account":"MergePool","path":"pool"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"mintBump","type":"u8"}]},{"name":"newPoolV2","accounts":[{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"MergePool"},{"kind":"account","type":"publicKey","account":"Mint","path":"primary_mint"}]}},{"name":"primaryMint","isMut":false,"isSigner":false},{"name":"replicaMint","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"ReplicaMint"},{"kind":"account","type":"publicKey","account":"MergePool","path":"pool"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"initMergeMiner","accounts":[{"name":"pool","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"mm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"MergeMiner"},{"kind":"account","type":"publicKey","account":"MergePool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"initMergeMinerV2","accounts":[{"name":"pool","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"mm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"MergeMiner"},{"kind":"account","type":"publicKey","account":"MergePool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initMiner","accounts":[{"name":"pool","isMut":false,"isSigner":false},{"name":"mm","isMut":false,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"minerVault","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"mineProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"initMinerV2","accounts":[{"name":"pool","isMut":false,"isSigner":false},{"name":"mm","isMut":false,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"minerVault","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"mineProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"stakePrimaryMiner","accounts":[{"name":"mmOwner","isMut":false,"isSigner":true},{"name":"mmPrimaryTokenAccount","isMut":true,"isSigner":false},{"name":"stake","accounts":[{"name":"pool","isMut":true,"isSigner":false},{"name":"mm","isMut":true,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mineProgram","isMut":false,"isSigner":false}]}],"args":[]},{"name":"stakeReplicaMiner","accounts":[{"name":"mmOwner","isMut":false,"isSigner":true},{"name":"replicaMint","isMut":true,"isSigner":false},{"name":"replicaMintTokenAccount","isMut":true,"isSigner":false},{"name":"stake","accounts":[{"name":"pool","isMut":true,"isSigner":false},{"name":"mm","isMut":true,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mineProgram","isMut":false,"isSigner":false}]}],"args":[]},{"name":"unstakePrimaryMiner","accounts":[{"name":"mmOwner","isMut":false,"isSigner":true},{"name":"mmPrimaryTokenAccount","isMut":true,"isSigner":false},{"name":"stake","accounts":[{"name":"pool","isMut":true,"isSigner":false},{"name":"mm","isMut":true,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mineProgram","isMut":false,"isSigner":false}]}],"args":[{"name":"amount","type":"u64"}]},{"name":"unstakeAllReplicaMiner","accounts":[{"name":"mmOwner","isMut":false,"isSigner":true},{"name":"replicaMint","isMut":true,"isSigner":false},{"name":"replicaMintTokenAccount","isMut":true,"isSigner":false},{"name":"stake","accounts":[{"name":"pool","isMut":true,"isSigner":false},{"name":"mm","isMut":true,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mineProgram","isMut":false,"isSigner":false}]}],"args":[]},{"name":"withdrawTokens","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"pool","isMut":false,"isSigner":false},{"name":"mm","isMut":true,"isSigner":false},{"name":"withdrawMint","isMut":false,"isSigner":false},{"name":"mmTokenAccount","isMut":true,"isSigner":false},{"name":"tokenDestination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"rescueTokens","accounts":[{"name":"mmOwner","isMut":false,"isSigner":true},{"name":"mergePool","isMut":false,"isSigner":false},{"name":"mm","isMut":false,"isSigner":false},{"name":"miner","isMut":false,"isSigner":false},{"name":"minerTokenAccount","isMut":true,"isSigner":false},{"name":"destinationTokenAccount","isMut":true,"isSigner":false},{"name":"quarryMineProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"claimRewards","accounts":[{"name":"mintWrapper","isMut":true,"isSigner":false},{"name":"mintWrapperProgram","isMut":false,"isSigner":false},{"name":"minter","isMut":true,"isSigner":false},{"name":"rewardsTokenMint","isMut":true,"isSigner":false},{"name":"rewardsTokenAccount","isMut":true,"isSigner":false},{"name":"claimFeeTokenAccount","isMut":true,"isSigner":false},{"name":"stakeTokenAccount","isMut":true,"isSigner":false},{"name":"stake","accounts":[{"name":"pool","isMut":true,"isSigner":false},{"name":"mm","isMut":true,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"miner","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mineProgram","isMut":false,"isSigner":false}]}],"args":[]}],"accounts":[{"name":"MergePool","type":{"kind":"struct","fields":[{"name":"primaryMint","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"replicaMint","type":"publicKey"},{"name":"mmCount","type":"u64"},{"name":"totalPrimaryBalance","type":"u64"},{"name":"totalReplicaBalance","type":"u64"},{"name":"reserved","type":{"array":["u64",16]}}]}},{"name":"MergeMiner","type":{"kind":"struct","fields":[{"name":"pool","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"index","type":"u64"},{"name":"primaryBalance","type":"u64"},{"name":"replicaBalance","type":"u64"}]}}],"events":[{"name":"NewMergePoolEvent","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"primaryMint","type":"publicKey","index":false}]},{"name":"InitMergeMinerEvent","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"mm","type":"publicKey","index":false},{"name":"primaryMint","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false}]},{"name":"InitMinerEvent","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"mm","type":"publicKey","index":false},{"name":"miner","type":"publicKey","index":false}]},{"name":"StakePrimaryEvent","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"mm","type":"publicKey","index":false},{"name":"miner","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"StakeReplicaEvent","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"mm","type":"publicKey","index":false},{"name":"miner","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"UnstakePrimaryEvent","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"mm","type":"publicKey","index":false},{"name":"miner","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"UnstakeReplicaEvent","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"mm","type":"publicKey","index":false},{"name":"miner","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"WithdrawTokensEvent","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"mm","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"ClaimEvent","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"mm","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"initialBalance","type":"u64","index":false},{"name":"endBalance","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"Unauthorized","msg":"Unauthorized."},{"code":6001,"name":"InsufficientBalance","msg":"Insufficient balance."},{"code":6002,"name":"InvalidMiner","msg":"Invalid miner for the given quarry."},{"code":6003,"name":"CannotWithdrawReplicaMint","msg":"Cannot withdraw a replica mint."},{"code":6004,"name":"OutstandingReplicaTokens","msg":"User must first withdraw from all replica quarries."},{"code":6005,"name":"ReplicaDecimalsMismatch","msg":"The replica mint must have the same number of decimals as the primary mint."},{"code":6006,"name":"ReplicaNonZeroSupply","msg":"The replica mint must have zero supply."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/QMNeHCGYnLVDn1icRAfQZpjPLBNkfGbSKRB83G5d8KB.json b/crate/tests/idls/QMNeHCGYnLVDn1icRAfQZpjPLBNkfGbSKRB83G5d8KB.json
new file mode 100644
index 0000000..865b385
--- /dev/null
+++ b/crate/tests/idls/QMNeHCGYnLVDn1icRAfQZpjPLBNkfGbSKRB83G5d8KB.json
@@ -0,0 +1 @@
+{"version":"5.0.2","name":"quarry_mine","instructions":[{"name":"newRewarder","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Rewarder"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"initialAuthority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"unusedAccount","isMut":false,"isSigner":false},{"name":"mintWrapper","isMut":false,"isSigner":false},{"name":"rewardsTokenMint","isMut":false,"isSigner":false},{"name":"claimFeeTokenAccount","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"newRewarderV2","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Rewarder"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"initialAuthority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"mintWrapper","isMut":false,"isSigner":false},{"name":"rewardsTokenMint","isMut":false,"isSigner":false},{"name":"claimFeeTokenAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"setPauseAuthority","accounts":[{"name":"auth","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false}]},{"name":"newPauseAuthority","isMut":false,"isSigner":false}],"args":[]},{"name":"pause","accounts":[{"name":"pauseAuthority","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false}],"args":[]},{"name":"unpause","accounts":[{"name":"pauseAuthority","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false}],"args":[]},{"name":"transferAuthority","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false}],"args":[{"name":"newAuthority","type":"publicKey"}]},{"name":"acceptAuthority","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false}],"args":[]},{"name":"setAnnualRewards","accounts":[{"name":"auth","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false}]}],"args":[{"name":"newRate","type":"u64"}]},{"name":"createQuarry","accounts":[{"name":"quarry","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Quarry"},{"kind":"account","type":{"defined":"Account<'info,Rewarder>"},"account":"MutableRewarderWithAuthority","path":"auth.rewarder"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_mint"}]}},{"name":"auth","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false}]},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"unusedAccount","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"createQuarryV2","accounts":[{"name":"quarry","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Quarry"},{"kind":"account","type":{"defined":"Account<'info,Rewarder>"},"account":"MutableRewarderWithAuthority","path":"auth.rewarder"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_mint"}]}},{"name":"auth","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false}]},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setRewardsShare","accounts":[{"name":"auth","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false}]},{"name":"quarry","isMut":true,"isSigner":false}],"args":[{"name":"newShare","type":"u64"}]},{"name":"setFamine","accounts":[{"name":"auth","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"rewarder","isMut":false,"isSigner":false}]},{"name":"quarry","isMut":true,"isSigner":false}],"args":[{"name":"famineTs","type":"i64"}]},{"name":"updateQuarryRewards","accounts":[{"name":"quarry","isMut":true,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false}],"args":[]},{"name":"createMiner","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"miner","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Miner"},{"kind":"account","type":"publicKey","account":"Quarry","path":"quarry"},{"kind":"account","type":"publicKey","path":"authority"}]}},{"name":"quarry","isMut":true,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"minerVault","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"createMinerV2","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"miner","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Miner"},{"kind":"account","type":"publicKey","account":"Quarry","path":"quarry"},{"kind":"account","type":"publicKey","path":"authority"}]}},{"name":"quarry","isMut":true,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"minerVault","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"claimRewards","accounts":[{"name":"mintWrapper","isMut":true,"isSigner":false},{"name":"mintWrapperProgram","isMut":false,"isSigner":false},{"name":"minter","isMut":true,"isSigner":false},{"name":"rewardsTokenMint","isMut":true,"isSigner":false},{"name":"rewardsTokenAccount","isMut":true,"isSigner":false},{"name":"claimFeeTokenAccount","isMut":true,"isSigner":false},{"name":"claim","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"miner","isMut":true,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"unusedMinerVault","isMut":false,"isSigner":false},{"name":"unusedTokenAccount","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false}]}],"args":[]},{"name":"claimRewardsV2","accounts":[{"name":"mintWrapper","isMut":true,"isSigner":false},{"name":"mintWrapperProgram","isMut":false,"isSigner":false},{"name":"minter","isMut":true,"isSigner":false},{"name":"rewardsTokenMint","isMut":true,"isSigner":false},{"name":"rewardsTokenAccount","isMut":true,"isSigner":false},{"name":"claimFeeTokenAccount","isMut":true,"isSigner":false},{"name":"claim","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"miner","isMut":true,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false}]}],"args":[]},{"name":"stakeTokens","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"miner","isMut":true,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false},{"name":"tokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawTokens","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"miner","isMut":true,"isSigner":false},{"name":"quarry","isMut":true,"isSigner":false},{"name":"minerVault","isMut":true,"isSigner":false},{"name":"tokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rewarder","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"rescueTokens","accounts":[{"name":"miner","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"minerTokenAccount","isMut":true,"isSigner":false},{"name":"destinationTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"extractFees","accounts":[{"name":"rewarder","isMut":false,"isSigner":false},{"name":"claimFeeTokenAccount","isMut":true,"isSigner":false},{"name":"feeToTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Rewarder","type":{"kind":"struct","fields":[{"name":"base","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"authority","type":"publicKey"},{"name":"pendingAuthority","type":"publicKey"},{"name":"numQuarries","type":"u16"},{"name":"annualRewardsRate","type":"u64"},{"name":"totalRewardsShares","type":"u64"},{"name":"mintWrapper","type":"publicKey"},{"name":"rewardsTokenMint","type":"publicKey"},{"name":"claimFeeTokenAccount","type":"publicKey"},{"name":"maxClaimFeeMillibps","type":"u64"},{"name":"pauseAuthority","type":"publicKey"},{"name":"isPaused","type":"bool"}]}},{"name":"Quarry","type":{"kind":"struct","fields":[{"name":"rewarder","type":"publicKey"},{"name":"tokenMintKey","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"index","type":"u16"},{"name":"tokenMintDecimals","type":"u8"},{"name":"famineTs","type":"i64"},{"name":"lastUpdateTs","type":"i64"},{"name":"rewardsPerTokenStored","type":"u128"},{"name":"annualRewardsRate","type":"u64"},{"name":"rewardsShare","type":"u64"},{"name":"totalTokensDeposited","type":"u64"},{"name":"numMiners","type":"u64"}]}},{"name":"Miner","type":{"kind":"struct","fields":[{"name":"quarry","type":"publicKey"},{"name":"authority","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"tokenVaultKey","type":"publicKey"},{"name":"rewardsEarned","type":"u64"},{"name":"rewardsPerTokenPaid","type":"u128"},{"name":"balance","type":"u64"},{"name":"index","type":"u64"}]}}],"types":[{"name":"StakeAction","type":{"kind":"enum","variants":[{"name":"Stake"},{"name":"Withdraw"}]}}],"events":[{"name":"MinerCreateEvent","fields":[{"name":"authority","type":"publicKey","index":false},{"name":"quarry","type":"publicKey","index":false},{"name":"miner","type":"publicKey","index":false}]},{"name":"NewRewarderEvent","fields":[{"name":"authority","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"ClaimEvent","fields":[{"name":"authority","type":"publicKey","index":false},{"name":"stakedToken","type":"publicKey","index":false},{"name":"rewardsToken","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"fees","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"StakeEvent","fields":[{"name":"authority","type":"publicKey","index":false},{"name":"token","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"WithdrawEvent","fields":[{"name":"authority","type":"publicKey","index":false},{"name":"token","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"RewarderAnnualRewardsUpdateEvent","fields":[{"name":"previousRate","type":"u64","index":false},{"name":"newRate","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"QuarryCreateEvent","fields":[{"name":"tokenMint","type":"publicKey","index":false},{"name":"timestamp","type":"i64","index":false}]},{"name":"QuarryRewardsUpdateEvent","fields":[{"name":"tokenMint","type":"publicKey","index":false},{"name":"annualRewardsRate","type":"u64","index":false},{"name":"rewardsShare","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]}],"errors":[{"code":6000,"name":"Unauthorized","msg":"You are not authorized to perform this action."},{"code":6001,"name":"InsufficientBalance","msg":"Insufficient staked balance for withdraw request."},{"code":6002,"name":"PendingAuthorityNotSet","msg":"Pending authority not set"},{"code":6003,"name":"InvalidRewardsShare","msg":"Invalid quarry rewards share"},{"code":6004,"name":"InsufficientAllowance","msg":"Insufficient allowance."},{"code":6005,"name":"NewVaultNotEmpty","msg":"New vault not empty."},{"code":6006,"name":"NotEnoughTokens","msg":"Not enough tokens."},{"code":6007,"name":"InvalidTimestamp","msg":"Invalid timestamp."},{"code":6008,"name":"InvalidMaxClaimFee","msg":"Invalid max claim fee."},{"code":6009,"name":"MaxAnnualRewardsRateExceeded","msg":"Max annual rewards rate exceeded."},{"code":6010,"name":"Paused","msg":"Rewarder is paused."},{"code":6011,"name":"UpperboundExceeded","msg":"Rewards earned exceeded quarry's upper bound."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/QMWoBmAyJLAsA1Lh9ugMTw2gciTihncciphzdNzdZYV.json b/crate/tests/idls/QMWoBmAyJLAsA1Lh9ugMTw2gciTihncciphzdNzdZYV.json
new file mode 100644
index 0000000..8c16e8c
--- /dev/null
+++ b/crate/tests/idls/QMWoBmAyJLAsA1Lh9ugMTw2gciTihncciphzdNzdZYV.json
@@ -0,0 +1 @@
+{"version":"5.0.2","name":"quarry_mint_wrapper","instructions":[{"name":"newWrapper","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"mintWrapper","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"MintWrapper"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"admin","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"},{"name":"hardCap","type":"u64"}]},{"name":"newWrapperV2","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"mintWrapper","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"MintWrapper"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"admin","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"hardCap","type":"u64"}]},{"name":"transferAdmin","accounts":[{"name":"mintWrapper","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"nextAdmin","isMut":false,"isSigner":false}],"args":[]},{"name":"acceptAdmin","accounts":[{"name":"mintWrapper","isMut":true,"isSigner":false},{"name":"pendingAdmin","isMut":false,"isSigner":true}],"args":[]},{"name":"newMinter","accounts":[{"name":"auth","accounts":[{"name":"mintWrapper","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}]},{"name":"newMinterAuthority","isMut":false,"isSigner":false},{"name":"minter","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"MintWrapperMinter"},{"kind":"account","type":{"defined":"Account<'info,MintWrapper>"},"account":"OnlyAdmin","path":"auth.mint_wrapper"},{"kind":"account","type":"publicKey","path":"new_minter_authority"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"newMinterV2","accounts":[{"name":"auth","accounts":[{"name":"mintWrapper","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}]},{"name":"newMinterAuthority","isMut":false,"isSigner":false},{"name":"minter","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"MintWrapperMinter"},{"kind":"account","type":{"defined":"Account<'info,MintWrapper>"},"account":"OnlyAdmin","path":"auth.mint_wrapper"},{"kind":"account","type":"publicKey","path":"new_minter_authority"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"minterUpdate","accounts":[{"name":"auth","accounts":[{"name":"mintWrapper","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}]},{"name":"minter","isMut":true,"isSigner":false}],"args":[{"name":"allowance","type":"u64"}]},{"name":"performMint","accounts":[{"name":"mintWrapper","isMut":true,"isSigner":false},{"name":"minterAuthority","isMut":false,"isSigner":true},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"minter","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]}],"accounts":[{"name":"MintWrapper","type":{"kind":"struct","fields":[{"name":"base","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"hardCap","type":"u64"},{"name":"admin","type":"publicKey"},{"name":"pendingAdmin","type":"publicKey"},{"name":"tokenMint","type":"publicKey"},{"name":"numMinters","type":"u64"},{"name":"totalAllowance","type":"u64"},{"name":"totalMinted","type":"u64"}]}},{"name":"Minter","type":{"kind":"struct","fields":[{"name":"mintWrapper","type":"publicKey"},{"name":"minterAuthority","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"index","type":"u64"},{"name":"allowance","type":"u64"},{"name":"totalMinted","type":"u64"}]}}],"events":[{"name":"NewMintWrapperEvent","fields":[{"name":"mintWrapper","type":"publicKey","index":false},{"name":"hardCap","type":"u64","index":false},{"name":"admin","type":"publicKey","index":false},{"name":"tokenMint","type":"publicKey","index":false}]},{"name":"MintWrapperAdminProposeEvent","fields":[{"name":"mintWrapper","type":"publicKey","index":false},{"name":"currentAdmin","type":"publicKey","index":false},{"name":"pendingAdmin","type":"publicKey","index":false}]},{"name":"MintWrapperAdminUpdateEvent","fields":[{"name":"mintWrapper","type":"publicKey","index":false},{"name":"previousAdmin","type":"publicKey","index":false},{"name":"admin","type":"publicKey","index":false}]},{"name":"NewMinterEvent","fields":[{"name":"mintWrapper","type":"publicKey","index":false},{"name":"minter","type":"publicKey","index":false},{"name":"index","type":"u64","index":false},{"name":"minterAuthority","type":"publicKey","index":false}]},{"name":"MinterAllowanceUpdateEvent","fields":[{"name":"mintWrapper","type":"publicKey","index":false},{"name":"minter","type":"publicKey","index":false},{"name":"previousAllowance","type":"u64","index":false},{"name":"allowance","type":"u64","index":false}]},{"name":"MinterMintEvent","fields":[{"name":"mintWrapper","type":"publicKey","index":false},{"name":"minter","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"destination","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"Unauthorized","msg":"You are not authorized to perform this action."},{"code":6001,"name":"HardcapExceeded","msg":"Cannot mint over hard cap."},{"code":6002,"name":"MinterAllowanceExceeded","msg":"Minter allowance exceeded."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/QRDxhMw1P2NEfiw5mYXG79bwfgHTdasY2xNP76XSea9.json b/crate/tests/idls/QRDxhMw1P2NEfiw5mYXG79bwfgHTdasY2xNP76XSea9.json
new file mode 100644
index 0000000..84d7283
--- /dev/null
+++ b/crate/tests/idls/QRDxhMw1P2NEfiw5mYXG79bwfgHTdasY2xNP76XSea9.json
@@ -0,0 +1 @@
+{"version":"5.0.2","name":"quarry_redeemer","instructions":[{"name":"createRedeemer","accounts":[{"name":"redeemer","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Redeemer"},{"kind":"account","type":"publicKey","account":"Mint","path":"iou_mint"},{"kind":"account","type":"publicKey","account":"Mint","path":"redemption_mint"}]}},{"name":"iouMint","isMut":false,"isSigner":false},{"name":"redemptionMint","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"redeemTokens","accounts":[{"name":"redeemer","isMut":true,"isSigner":false},{"name":"sourceAuthority","isMut":false,"isSigner":true},{"name":"iouMint","isMut":true,"isSigner":false},{"name":"iouSource","isMut":true,"isSigner":false},{"name":"redemptionVault","isMut":true,"isSigner":false},{"name":"redemptionDestination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"redeemAllTokens","accounts":[{"name":"redeemer","isMut":true,"isSigner":false},{"name":"sourceAuthority","isMut":false,"isSigner":true},{"name":"iouMint","isMut":true,"isSigner":false},{"name":"iouSource","isMut":true,"isSigner":false},{"name":"redemptionVault","isMut":true,"isSigner":false},{"name":"redemptionDestination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Redeemer","type":{"kind":"struct","fields":[{"name":"iouMint","type":"publicKey"},{"name":"redemptionMint","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"totalTokensRedeemed","type":"u64"}]}}],"events":[{"name":"RedeemTokensEvent","fields":[{"name":"user","type":"publicKey","index":false},{"name":"iouMint","type":"publicKey","index":false},{"name":"redemptionMint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"timestamp","type":"i64","index":false}]}],"errors":[{"code":6000,"name":"Unauthorized","msg":"Unauthorized."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/QREGBnEj9Sa5uR91AV8u3FxThgP5ZCvdZUW2bHAkfNc.json b/crate/tests/idls/QREGBnEj9Sa5uR91AV8u3FxThgP5ZCvdZUW2bHAkfNc.json
new file mode 100644
index 0000000..19a4d6d
--- /dev/null
+++ b/crate/tests/idls/QREGBnEj9Sa5uR91AV8u3FxThgP5ZCvdZUW2bHAkfNc.json
@@ -0,0 +1 @@
+{"version":"5.0.2","name":"quarry_registry","instructions":[{"name":"newRegistry","accounts":[{"name":"rewarder","isMut":false,"isSigner":false},{"name":"registry","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"QuarryRegistry"},{"kind":"account","type":"publicKey","account":"Rewarder","path":"rewarder"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxQuarries","type":"u16"},{"name":"bump","type":"u8"}]},{"name":"syncQuarry","accounts":[{"name":"quarry","isMut":false,"isSigner":false},{"name":"registry","isMut":true,"isSigner":false}],"args":[]}],"accounts":[{"name":"Registry","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"rewarder","type":"publicKey"},{"name":"tokens","type":{"vec":"publicKey"}}]}}]}
\ No newline at end of file
diff --git a/crate/tests/idls/QoP6NfrQbaGnccXQrMLUkog2tQZ4C1RFgJcwDnT8Kmz.json b/crate/tests/idls/QoP6NfrQbaGnccXQrMLUkog2tQZ4C1RFgJcwDnT8Kmz.json
new file mode 100644
index 0000000..f2301d6
--- /dev/null
+++ b/crate/tests/idls/QoP6NfrQbaGnccXQrMLUkog2tQZ4C1RFgJcwDnT8Kmz.json
@@ -0,0 +1 @@
+{"version":"5.0.2","name":"quarry_operator","instructions":[{"name":"createOperator","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"operator","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Operator"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"rewarder","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"quarryMineProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"createOperatorV2","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"operator","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Operator"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"rewarder","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"quarryMineProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setAdmin","accounts":[{"name":"operator","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"delegate","isMut":false,"isSigner":false}],"args":[]},{"name":"setRateSetter","accounts":[{"name":"operator","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"delegate","isMut":false,"isSigner":false}],"args":[]},{"name":"setQuarryCreator","accounts":[{"name":"operator","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"delegate","isMut":false,"isSigner":false}],"args":[]},{"name":"setShareAllocator","accounts":[{"name":"operator","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"delegate","isMut":false,"isSigner":false}],"args":[]},{"name":"delegateSetAnnualRewards","accounts":[{"name":"withDelegate","accounts":[{"name":"operator","isMut":true,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false},{"name":"quarryMineProgram","isMut":false,"isSigner":false}]}],"args":[{"name":"newRate","type":"u64"}]},{"name":"delegateCreateQuarry","accounts":[{"name":"withDelegate","accounts":[{"name":"operator","isMut":true,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false},{"name":"quarryMineProgram","isMut":false,"isSigner":false}]},{"name":"quarry","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"unusedAccount","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"delegateCreateQuarryV2","accounts":[{"name":"withDelegate","accounts":[{"name":"operator","isMut":true,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false},{"name":"quarryMineProgram","isMut":false,"isSigner":false}]},{"name":"quarry","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"delegateSetRewardsShare","accounts":[{"name":"withDelegate","accounts":[{"name":"operator","isMut":true,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false},{"name":"quarryMineProgram","isMut":false,"isSigner":false}]},{"name":"quarry","isMut":true,"isSigner":false}],"args":[{"name":"newShare","type":"u64"}]},{"name":"delegateSetFamine","accounts":[{"name":"withDelegate","accounts":[{"name":"operator","isMut":true,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":true},{"name":"rewarder","isMut":true,"isSigner":false},{"name":"quarryMineProgram","isMut":false,"isSigner":false}]},{"name":"quarry","isMut":true,"isSigner":false}],"args":[{"name":"famineTs","type":"i64"}]}],"accounts":[{"name":"Operator","type":{"kind":"struct","fields":[{"name":"base","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"rewarder","type":"publicKey"},{"name":"admin","type":"publicKey"},{"name":"rateSetter","type":"publicKey"},{"name":"quarryCreator","type":"publicKey"},{"name":"shareAllocator","type":"publicKey"},{"name":"lastModifiedTs","type":"i64"},{"name":"generation","type":"u64"}]}}],"errors":[{"code":6000,"name":"Unauthorized","msg":"Signer is not authorized to perform this action."},{"code":6001,"name":"PendingAuthorityNotSet","msg":"Pending authority must be set to the created operator."},{"code":6002,"name":"OperatorNotRewarderAuthority","msg":"Operator is not the Rewarder authority."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/RDM23yr8pr1kEAmhnFpaabPny6C9UVcEcok3Py5v86X.json b/crate/tests/idls/RDM23yr8pr1kEAmhnFpaabPny6C9UVcEcok3Py5v86X.json
new file mode 100644
index 0000000..825446f
--- /dev/null
+++ b/crate/tests/idls/RDM23yr8pr1kEAmhnFpaabPny6C9UVcEcok3Py5v86X.json
@@ -0,0 +1 @@
+{"version":"1.1.2","name":"redeemer","instructions":[{"name":"createRedeemer","accounts":[{"name":"redeemer","isMut":true,"isSigner":false},{"name":"tokens","accounts":[{"name":"iouMint","isMut":false,"isSigner":false},{"name":"redemptionMint","isMut":false,"isSigner":false},{"name":"redemptionVault","isMut":false,"isSigner":false}]},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"redeemTokens","accounts":[{"name":"redeemer","isMut":false,"isSigner":false},{"name":"tokens","accounts":[{"name":"iouMint","isMut":true,"isSigner":false},{"name":"redemptionMint","isMut":true,"isSigner":false},{"name":"redemptionVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}]},{"name":"sourceAuthority","isMut":false,"isSigner":true},{"name":"iouSource","isMut":true,"isSigner":false},{"name":"redemptionDestination","isMut":true,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"redeemTokensFromMintProxy","accounts":[{"name":"redeemCtx","accounts":[{"name":"redeemer","isMut":false,"isSigner":false},{"name":"tokens","accounts":[{"name":"iouMint","isMut":true,"isSigner":false},{"name":"redemptionMint","isMut":true,"isSigner":false},{"name":"redemptionVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}]},{"name":"sourceAuthority","isMut":false,"isSigner":true},{"name":"iouSource","isMut":true,"isSigner":false},{"name":"redemptionDestination","isMut":true,"isSigner":false}]},{"name":"mintProxyState","isMut":false,"isSigner":false},{"name":"proxyMintAuthority","isMut":false,"isSigner":false},{"name":"mintProxyProgram","isMut":false,"isSigner":false},{"name":"minterInfo","isMut":true,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"redeemAllTokensFromMintProxy","accounts":[{"name":"redeemCtx","accounts":[{"name":"redeemer","isMut":false,"isSigner":false},{"name":"tokens","accounts":[{"name":"iouMint","isMut":true,"isSigner":false},{"name":"redemptionMint","isMut":true,"isSigner":false},{"name":"redemptionVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}]},{"name":"sourceAuthority","isMut":false,"isSigner":true},{"name":"iouSource","isMut":true,"isSigner":false},{"name":"redemptionDestination","isMut":true,"isSigner":false}]},{"name":"mintProxyState","isMut":false,"isSigner":false},{"name":"proxyMintAuthority","isMut":false,"isSigner":false},{"name":"mintProxyProgram","isMut":false,"isSigner":false},{"name":"minterInfo","isMut":true,"isSigner":false}],"args":[]}],"accounts":[{"name":"Redeemer","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"iouMint","type":"publicKey"},{"name":"redemptionMint","type":"publicKey"},{"name":"redemptionVault","type":"publicKey"}]}}],"events":[{"name":"RedeemTokensEvent","fields":[{"name":"user","type":"publicKey","index":false},{"name":"iouMint","type":"publicKey","index":false},{"name":"destinationMint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"Unauthorized","msg":"Unauthorized."},{"code":6001,"name":"DecimalsMismatch","msg":"Redemption token and IOU token decimals must match"}]}
\ No newline at end of file
diff --git a/crate/tests/idls/SAVEd9pHcncknnMWdP8RSbhDUhw3nrzwmZ6F6RAUiio.json b/crate/tests/idls/SAVEd9pHcncknnMWdP8RSbhDUhw3nrzwmZ6F6RAUiio.json
new file mode 100644
index 0000000..f3ab81d
--- /dev/null
+++ b/crate/tests/idls/SAVEd9pHcncknnMWdP8RSbhDUhw3nrzwmZ6F6RAUiio.json
@@ -0,0 +1 @@
+{"version":"0.1.2","name":"save_token","instructions":[{"name":"createSave","accounts":[{"name":"saveMint","isMut":false,"isSigner":false},{"name":"save","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"SAVE"},{"kind":"account","type":"publicKey","account":"Mint","path":"save_mint"}]}},{"name":"yi","isMut":false,"isSigner":false},{"name":"yiMint","isMut":false,"isSigner":false},{"name":"yiTokens","isMut":false,"isSigner":false},{"name":"locker","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"minLockDuration","type":"u64"}]},{"name":"lock","accounts":[{"name":"save","isMut":false,"isSigner":false},{"name":"saveMint","isMut":true,"isSigner":false},{"name":"saveYiTokens","isMut":true,"isSigner":false},{"name":"userSaveTokens","isMut":true,"isSigner":false},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"lock","accounts":[{"name":"locker","isMut":true,"isSigner":false},{"name":"escrow","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false}]},{"name":"yi","accounts":[{"name":"yiToken","isMut":false,"isSigner":false},{"name":"yiMint","isMut":true,"isSigner":false},{"name":"yiUnderlyingTokens","isMut":true,"isSigner":false}]},{"name":"userAuthority","isMut":false,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"yiTokenProgram","isMut":false,"isSigner":false},{"name":"lockedVoterProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"duration","type":"u64"}]},{"name":"mintFromYi","accounts":[{"name":"common","accounts":[{"name":"save","isMut":false,"isSigner":false},{"name":"saveMint","isMut":true,"isSigner":false},{"name":"saveYiTokens","isMut":true,"isSigner":false},{"name":"to","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}]},{"name":"sourceYiTokens","isMut":true,"isSigner":false},{"name":"sourceAuthority","isMut":false,"isSigner":true}],"args":[{"name":"yiAmount","type":"u64"}]},{"name":"mintFromUnderlying","accounts":[{"name":"common","accounts":[{"name":"save","isMut":false,"isSigner":false},{"name":"saveMint","isMut":true,"isSigner":false},{"name":"saveYiTokens","isMut":true,"isSigner":false},{"name":"to","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}]},{"name":"yi","isMut":true,"isSigner":false},{"name":"yiMint","isMut":true,"isSigner":false},{"name":"yiUnderlyingTokens","isMut":true,"isSigner":false},{"name":"sourceUnderlyingTokens","isMut":true,"isSigner":false},{"name":"sourceAuthority","isMut":false,"isSigner":true},{"name":"yiProgram","isMut":false,"isSigner":false}],"args":[{"name":"underlyingAmount","type":"u64"}]}],"accounts":[{"name":"Save","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",7]}},{"name":"minLockDuration","type":"u64"},{"name":"underlyingMint","type":"publicKey"},{"name":"yiMint","type":"publicKey"},{"name":"yi","type":"publicKey"},{"name":"yiTokens","type":"publicKey"},{"name":"locker","type":"publicKey"}]}}],"errors":[{"code":6000,"name":"DurationExceeded","msg":"SAVE minimum duration not met."},{"code":6001,"name":"LockerMismatch","msg":"Tokens may only be locked in the SAVE's specified locker."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/SCHAtsf8mbjyjiv4LkhLKutTf6JnZAbdJKFkXQNMFHZ.json b/crate/tests/idls/SCHAtsf8mbjyjiv4LkhLKutTf6JnZAbdJKFkXQNMFHZ.json
new file mode 100644
index 0000000..20a6aca
--- /dev/null
+++ b/crate/tests/idls/SCHAtsf8mbjyjiv4LkhLKutTf6JnZAbdJKFkXQNMFHZ.json
@@ -0,0 +1 @@
+{"version":"0.10.1","name":"cpamm","instructions":[{"name":"newFactory","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"factory","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"Factory"},{"kind":"account","type":"publicKey","path":"base"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"newSwap","accounts":[{"name":"factory","isMut":true,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"SwapInfo"},{"kind":"account","type":"publicKey","account":"Factory","path":"factory"},{"kind":"account","type":{"defined":"Box>"},"account":"InitSwapToken","path":"token_0.mint"},{"kind":"account","type":{"defined":"Box>"},"account":"InitSwapToken","path":"token_1.mint"}]}},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"token0","accounts":[{"name":"mint","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"fees","isMut":false,"isSigner":false}]},{"name":"token1","accounts":[{"name":"mint","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"fees","isMut":false,"isSigner":false}]},{"name":"outputLp","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"newSwapMeta","accounts":[{"name":"swap","isMut":false,"isSigner":false},{"name":"swapMeta","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"SwapMeta"},{"kind":"account","type":"publicKey","account":"SwapInfo","path":"swap.factory"},{"kind":"account","type":"u64","account":"SwapInfo","path":"swap.index"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"swap","accounts":[{"name":"user","accounts":[{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"userAuthority","isMut":false,"isSigner":true}]},{"name":"input","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"fees","isMut":true,"isSigner":false}]},{"name":"output","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"fees","isMut":true,"isSigner":false}]}],"args":[{"name":"amountIn","type":"u64"},{"name":"minimumAmountOut","type":"u64"}]},{"name":"swapMax","accounts":[{"name":"user","accounts":[{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"userAuthority","isMut":false,"isSigner":true}]},{"name":"input","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"fees","isMut":true,"isSigner":false}]},{"name":"output","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"fees","isMut":true,"isSigner":false}]}],"args":[{"name":"minimumAmountOut","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"user","accounts":[{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"userAuthority","isMut":false,"isSigner":true}]},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"inputLp","isMut":true,"isSigner":false},{"name":"output0","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"fees","isMut":true,"isSigner":false}]},{"name":"output1","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"fees","isMut":true,"isSigner":false}]}],"args":[{"name":"amountIn","type":"u64"},{"name":"minimumAmountOut0","type":"u64"},{"name":"minimumAmountOut1","type":"u64"}]},{"name":"deposit","accounts":[{"name":"user","accounts":[{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"userAuthority","isMut":false,"isSigner":true}]},{"name":"input0","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}]},{"name":"input1","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}]},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"outputLp","isMut":true,"isSigner":false}],"args":[{"name":"poolTokenAmount","type":"u64"},{"name":"maximumAmountIn0","type":"u64"},{"name":"maximumAmountIn1","type":"u64"}]}],"accounts":[{"name":"Factory","type":{"kind":"struct","fields":[{"name":"base","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"numSwaps","type":"u64"},{"name":"admin","type":"publicKey"},{"name":"reserved","type":{"array":["u64",16]}}]}},{"name":"SwapMeta","type":{"kind":"struct","fields":[{"name":"factory","type":"publicKey"},{"name":"index","type":"u64"},{"name":"bump","type":"u8"},{"name":"swap","type":"publicKey"},{"name":"createdAt","type":"i64"},{"name":"createdBy","type":"publicKey"}]}},{"name":"SwapInfo","type":{"kind":"struct","fields":[{"name":"factory","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"index","type":"u64"},{"name":"adminKey","type":"publicKey"},{"name":"token0","type":{"defined":"SwapTokenInfo"}},{"name":"token1","type":{"defined":"SwapTokenInfo"}},{"name":"isPaused","type":"bool"},{"name":"poolMint","type":"publicKey"},{"name":"fees","type":{"defined":"SwapFees"}},{"name":"cumulativeStats","type":{"defined":"SwapCumulativeStats"}},{"name":"priceInfo","type":{"defined":"SwapPriceInfo"}},{"name":"reserved","type":{"array":["u64",16]}}]}}],"types":[{"name":"SwapCumulativeStats","type":{"kind":"struct","fields":[{"name":"token0","type":{"defined":"SwapCumulativeTokenStats"}},{"name":"token1","type":{"defined":"SwapCumulativeTokenStats"}},{"name":"totalLpMinted","type":"u128"},{"name":"totalLpRedeemed","type":"u128"}]}},{"name":"SwapCumulativeTokenStats","type":{"kind":"struct","fields":[{"name":"totalInputVolume","type":"u128"},{"name":"totalOutputVolume","type":"u128"},{"name":"totalDepositVolume","type":"u128"},{"name":"totalWithdrawVolume","type":"u128"},{"name":"totalTradeFees","type":"u64"}]}},{"name":"SwapPriceInfo","type":{"kind":"struct","fields":[{"name":"lastUpdateTs","type":"i64"},{"name":"price0CumulativeLast","type":"u128"},{"name":"price1CumulativeLast","type":"u128"}]}},{"name":"SwapTokenInfo","type":{"kind":"struct","fields":[{"name":"reserves","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"adminFees","type":"publicKey"}]}},{"name":"SwapFees","type":{"kind":"struct","fields":[{"name":"tradeFeeKbps","type":"u64"},{"name":"withdrawFeeKbps","type":"u64"},{"name":"adminTradeFeeKbps","type":"u64"},{"name":"adminWithdrawFeeKbps","type":"u64"}]}}],"events":[{"name":"NewPoolEvent","fields":[{"name":"lpMint","type":"publicKey","index":false},{"name":"mint0","type":"publicKey","index":false},{"name":"mint1","type":"publicKey","index":false},{"name":"initialLiquidity","type":"u64","index":false}]},{"name":"DepositEvent","fields":[{"name":"lpMint","type":"publicKey","index":false},{"name":"poolTokenAmount","type":"u64","index":false},{"name":"token0Amount","type":"u64","index":false},{"name":"token1Amount","type":"u64","index":false}]},{"name":"WithdrawEvent","fields":[{"name":"lpMint","type":"publicKey","index":false},{"name":"poolTokenAmount","type":"u64","index":false},{"name":"token0Amount","type":"u64","index":false},{"name":"token1Amount","type":"u64","index":false}]},{"name":"SwapEvent","fields":[{"name":"lpMint","type":"publicKey","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"sourceAmountSwapped","type":"u64","index":false},{"name":"destinationAmountSwapped","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"Paused","msg":"Swap pool is paused"},{"code":6001,"name":"ExceededSlippage","msg":"Swap instruction exceeds desired slippage limit"},{"code":6002,"name":"InsufficientLiquidity","msg":"Insufficient liquidity"},{"code":6003,"name":"InsufficientLiquidityPostWithdrawal","msg":"The withdrawal will result in the pool having too little liquidity. Withdraw less tokens."},{"code":6004,"name":"NewSwapMustHaveNonZeroSupply","msg":"New swap must have non-zero supply on its reserves"},{"code":6005,"name":"InitialLiquidityTooLow","msg":"Initial liquidity too low"},{"code":6006,"name":"SwapTokensNotSorted","msg":"Swap's token mints must be sorted"},{"code":6007,"name":"SwapTokensCannotBeEqual","msg":"Swap's token mints cannot be the same"},{"code":6008,"name":"SwapPoolMintSupply","msg":"Swap's pool mint supply must be zero"},{"code":6009,"name":"InvalidFee","msg":"Invalid fee"}]}
\ No newline at end of file
diff --git a/crate/tests/idls/SMANK4F5osjfVpKFH5LPzE6HPpbzSPu5iHPBhuor5xU.json b/crate/tests/idls/SMANK4F5osjfVpKFH5LPzE6HPpbzSPu5iHPBhuor5xU.json
new file mode 100644
index 0000000..feaa109
--- /dev/null
+++ b/crate/tests/idls/SMANK4F5osjfVpKFH5LPzE6HPpbzSPu5iHPBhuor5xU.json
@@ -0,0 +1 @@
+{"version":"0.1.14","name":"pools","instructions":[{"name":"newPoolManager","accounts":[{"name":"poolManager","isMut":true,"isSigner":false},{"name":"base","isMut":false,"isSigner":true},{"name":"admin","isMut":false,"isSigner":false},{"name":"operator","isMut":false,"isSigner":false},{"name":"beneficiary","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"importPoolPermissionless","accounts":[{"name":"poolManager","isMut":true,"isSigner":false},{"name":"swap","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tokenAFees","isMut":false,"isSigner":false},{"name":"tokenBFees","isMut":false,"isSigner":false},{"name":"lpMint","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bump","type":"u8"}]},{"name":"importPoolAsOperator","accounts":[{"name":"adminOrOperator","isMut":false,"isSigner":true},{"name":"importPool","accounts":[{"name":"poolManager","isMut":true,"isSigner":false},{"name":"swap","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tokenAFees","isMut":false,"isSigner":false},{"name":"tokenBFees","isMut":false,"isSigner":false},{"name":"lpMint","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}]}],"args":[{"name":"bump","type":"u8"}]},{"name":"rampA","accounts":[{"name":"poolManager","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"targetAmp","type":"u64"},{"name":"stopRampTs","type":"i64"}]},{"name":"stopRampA","accounts":[{"name":"poolManager","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"pauseSwap","accounts":[{"name":"poolManager","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"unpauseSwap","accounts":[{"name":"poolManager","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"commitNewAdmin","accounts":[{"name":"poolManager","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"newAdmin","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"applyNewAdmin","accounts":[{"name":"poolManager","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"setNewFees","accounts":[{"name":"poolManager","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"newFees","type":{"defined":"SwapFees"}}]},{"name":"sendFeesToBeneficiary","accounts":[{"name":"poolManager","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"feeAccount","isMut":true,"isSigner":false},{"name":"beneficiaryAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setOperator","accounts":[{"name":"poolManager","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"operator","isMut":false,"isSigner":false}],"args":[]},{"name":"setBeneficiary","accounts":[{"name":"poolManager","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"beneficiary","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"PoolManager","type":{"kind":"struct","fields":[{"name":"base","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"numPools","type":"u64"},{"name":"admin","type":"publicKey"},{"name":"pendingAdmin","type":"publicKey"},{"name":"initialFees","type":{"defined":"SwapFees"}},{"name":"minPermissionlessAmpFactor","type":"u64"},{"name":"maxPermissionlessAmpFactor","type":"u64"},{"name":"operator","type":"publicKey"},{"name":"beneficiary","type":"publicKey"}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"manager","type":"publicKey"},{"name":"mintA","type":"publicKey"},{"name":"mintB","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"swap","type":"publicKey"},{"name":"index","type":"u64"},{"name":"tokenAFees","type":"publicKey"},{"name":"tokenBFees","type":"publicKey"},{"name":"lpMint","type":"publicKey"},{"name":"tokenDecimals","type":"u8"},{"name":"permissionlessImport","type":"bool"}]}}],"types":[{"name":"SwapFees","type":{"kind":"struct","fields":[{"name":"adminTradeFeeNumerator","type":"u64"},{"name":"adminTradeFeeDenominator","type":"u64"},{"name":"adminWithdrawFeeNumerator","type":"u64"},{"name":"adminWithdrawFeeDenominator","type":"u64"},{"name":"tradeFeeNumerator","type":"u64"},{"name":"tradeFeeDenominator","type":"u64"},{"name":"withdrawFeeNumerator","type":"u64"},{"name":"withdrawFeeDenominator","type":"u64"}]}}],"errors":[{"code":6000,"name":"NotAdmin","msg":"Must be admin to perform this action."},{"code":6001,"name":"NotAdminOrOperator","msg":"Must be admin or operator to perform this action."},{"code":6002,"name":"InitialAmpOutOfRange","msg":"Initial amp factor out of range."},{"code":6003,"name":"InitialFeesMismatch","msg":"Swap fees do not match the configured initial parameters."},{"code":6004,"name":"SwapTokensNotSorted","msg":"Swap's token mints must be sorted."},{"code":6005,"name":"SwapTokensCannotBeEqual","msg":"Swap's token mints cannot be the same."},{"code":6006,"name":"InvalidFeeAccount","msg":"Specified fee account invalid."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/StakeSSzfxn391k3LvdKbZP5WVwWd6AsY1DNiXHjQfK.json b/crate/tests/idls/StakeSSzfxn391k3LvdKbZP5WVwWd6AsY1DNiXHjQfK.json
new file mode 100644
index 0000000..17e4022
--- /dev/null
+++ b/crate/tests/idls/StakeSSzfxn391k3LvdKbZP5WVwWd6AsY1DNiXHjQfK.json
@@ -0,0 +1 @@
+{"version":"0.2.8","name":"snapshots","instructions":[{"name":"createEscrowHistory","accounts":[{"name":"escrow","isMut":false,"isSigner":false},{"name":"escrowHistory","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"EscrowHistory"},{"kind":"account","type":"publicKey","account":"Escrow","path":"escrow"},{"kind":"arg","type":"u16","path":"era"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"era","type":"u16"}]},{"name":"createLockerHistory","accounts":[{"name":"locker","isMut":false,"isSigner":false},{"name":"lockerHistory","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"LockerHistory"},{"kind":"account","type":"publicKey","account":"Locker","path":"locker"},{"kind":"arg","type":"u16","path":"era"}]}},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"era","type":"u16"}]},{"name":"sync","accounts":[{"name":"locker","isMut":false,"isSigner":false},{"name":"escrow","isMut":false,"isSigner":false},{"name":"lockerHistory","isMut":true,"isSigner":false},{"name":"escrowHistory","isMut":true,"isSigner":false}],"args":[]}],"accounts":[{"name":"LockerHistory","type":{"kind":"struct","fields":[{"name":"locker","type":"publicKey"},{"name":"era","type":"u16"},{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",5]}},{"name":"veBalances","type":{"array":["u64",256]}},{"name":"veCounts","type":{"array":["u64",256]}}]}},{"name":"EscrowHistory","type":{"kind":"struct","fields":[{"name":"escrow","type":"publicKey"},{"name":"era","type":"u16"},{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",5]}},{"name":"veBalances","type":{"array":["u64",256]}}]}}],"errors":[{"code":6000,"name":"LockerEscrowMismatch","msg":"Locker/escrow mismatch."},{"code":6001,"name":"EraMismatch","msg":"Era mismatch."},{"code":6002,"name":"EscrowBalanceDecreased","msg":"Escrow balances cannot decrease."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/UBEBk5idELqykEEaycYtQ7iBVrCg6NmvFSzMpdr22mL.json b/crate/tests/idls/UBEBk5idELqykEEaycYtQ7iBVrCg6NmvFSzMpdr22mL.json
new file mode 100644
index 0000000..b2f17c4
--- /dev/null
+++ b/crate/tests/idls/UBEBk5idELqykEEaycYtQ7iBVrCg6NmvFSzMpdr22mL.json
@@ -0,0 +1 @@
+{"version":"1.1.2","name":"mint_proxy","instructions":[],"state":{"struct":{"name":"MintProxy","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"hardCap","type":"u64"},{"name":"proxyMintAuthority","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"pendingOwner","type":"publicKey"},{"name":"stateAssociatedAccount","type":"publicKey"},{"name":"tokenMint","type":"publicKey"}]}},"methods":[{"name":"new","accounts":[{"name":"mintAuthority","isMut":false,"isSigner":true},{"name":"proxyMintAuthority","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"},{"name":"hardCap","type":"u64"}]},{"name":"transferOwnership","accounts":[{"name":"owner","isMut":false,"isSigner":true}],"args":[{"name":"nextOwner","type":"publicKey"}]},{"name":"acceptOwnership","accounts":[{"name":"owner","isMut":false,"isSigner":true}],"args":[]},{"name":"minterAdd","accounts":[{"name":"auth","accounts":[{"name":"owner","isMut":false,"isSigner":true}]},{"name":"minter","isMut":false,"isSigner":false},{"name":"minterInfo","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"allowance","type":"u64"}]},{"name":"minterUpdate","accounts":[{"name":"auth","accounts":[{"name":"owner","isMut":false,"isSigner":true}]},{"name":"minterInfo","isMut":true,"isSigner":false}],"args":[{"name":"allowance","type":"u64"}]},{"name":"minterRemove","accounts":[{"name":"auth","accounts":[{"name":"owner","isMut":false,"isSigner":true}]},{"name":"minter","isMut":false,"isSigner":false},{"name":"minterInfo","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":false}],"args":[]},{"name":"performMint","accounts":[{"name":"proxyMintAuthority","isMut":false,"isSigner":false},{"name":"minter","isMut":false,"isSigner":true},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"minterInfo","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"setMintAuthority","accounts":[{"name":"auth","accounts":[{"name":"owner","isMut":false,"isSigner":true}]},{"name":"proxyMintAuthority","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"newAuthority","type":"publicKey"}]}]},"accounts":[{"name":"MinterInfo","type":{"kind":"struct","fields":[{"name":"minter","type":"publicKey"},{"name":"allowance","type":"u64"},{"name":"nonce","type":"u8"}]}},{"name":"MintProxyInfo","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"hardCap","type":"u64"},{"name":"proxyMintAuthority","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"pendingOwner","type":"publicKey"},{"name":"stateAssociatedAccount","type":"publicKey"},{"name":"tokenMint","type":"publicKey"}]}}],"errors":[{"code":6000,"name":"Unauthorized","msg":"You are not authorized to perform this action."},{"code":6001,"name":"HardcapExceeded","msg":"Cannot mint over hard cap."},{"code":6002,"name":"InvalidFreezeAuthority","msg":"Provided token mint has a freeze authority"},{"code":6003,"name":"InvalidTokenMint","msg":"Provided token mint was invalid."},{"code":6004,"name":"InvalidProxyAuthority","msg":"Provided proxy authority was invalid."},{"code":6005,"name":"NotEnoughAccounts","msg":"Not enough remaining accounts in relay context."},{"code":6006,"name":"WhitelistEntryAlreadyExists","msg":"Whitelist entry already exists."},{"code":6007,"name":"WhitelistEntryNotFound","msg":"Whitelist entry not found."},{"code":6008,"name":"WhitelistFull","msg":"Whitelist is full."},{"code":6009,"name":"TokenProgramIDMismatch","msg":"Invalid token program ID."},{"code":6010,"name":"PendingOwnerMismatch","msg":"Pending owner mismatch."},{"code":6011,"name":"MinterAllowanceExceeded","msg":"Minter allowance exceeded."},{"code":6012,"name":"U64Overflow","msg":"U64 overflow."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/YiiTopEnX2vyoWdXuG45ovDFYZars4XZ4w6td6RVTFm.json b/crate/tests/idls/YiiTopEnX2vyoWdXuG45ovDFYZars4XZ4w6td6RVTFm.json
new file mode 100644
index 0000000..6b44825
--- /dev/null
+++ b/crate/tests/idls/YiiTopEnX2vyoWdXuG45ovDFYZars4XZ4w6td6RVTFm.json
@@ -0,0 +1 @@
+{"version":"0.3.0","name":"yi","instructions":[{"name":"createYiToken","accounts":[{"name":"mint","isMut":false,"isSigner":false},{"name":"yiToken","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"YiToken"},{"kind":"account","type":"publicKey","account":"Mint","path":"mint"}]}},{"name":"underlyingTokenMint","isMut":false,"isSigner":false},{"name":"underlyingTokens","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createYiTokenWithFees","accounts":[{"name":"mint","isMut":false,"isSigner":false},{"name":"yiToken","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"YiToken"},{"kind":"account","type":"publicKey","account":"Mint","path":"mint"}]}},{"name":"underlyingTokenMint","isMut":false,"isSigner":false},{"name":"underlyingTokens","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeFeeMillibps","type":"u32"},{"name":"unstakeFeeMillibps","type":"u32"}]},{"name":"stake","accounts":[{"name":"yiToken","isMut":false,"isSigner":false},{"name":"yiMint","isMut":true,"isSigner":false},{"name":"sourceTokens","isMut":true,"isSigner":false},{"name":"sourceAuthority","isMut":false,"isSigner":true},{"name":"yiUnderlyingTokens","isMut":true,"isSigner":false},{"name":"destinationYiTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"unstake","accounts":[{"name":"yiToken","isMut":false,"isSigner":false},{"name":"yiMint","isMut":true,"isSigner":false},{"name":"sourceYiTokens","isMut":true,"isSigner":false},{"name":"sourceAuthority","isMut":false,"isSigner":true},{"name":"yiUnderlyingTokens","isMut":true,"isSigner":false},{"name":"destinationUnderlyingTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]}],"accounts":[{"name":"YiToken","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",7]}},{"name":"underlyingTokenMint","type":"publicKey"},{"name":"underlyingTokens","type":"publicKey"},{"name":"stakeFeeMillibps","type":"u32"},{"name":"unstakeFeeMillibps","type":"u32"}]}}],"errors":[{"code":6000,"name":"DecimalMismatch","msg":"Decimal mismatch."},{"code":6001,"name":"InvalidStakeFee","msg":"Stake fee cannot exceed 100%."},{"code":6002,"name":"InvalidUnstakeFee","msg":"Unstake fee cannot exceed 100%."}]}
\ No newline at end of file
diff --git a/crate/tests/idls/escrow.json b/crate/tests/idls/escrow.json
new file mode 100644
index 0000000..f356663
--- /dev/null
+++ b/crate/tests/idls/escrow.json
@@ -0,0 +1,472 @@
+{
+ "version": "0.1.0",
+ "name": "anchor_escrow_2023_timed",
+ "instructions": [
+ {
+ "name": "make",
+ "accounts": [
+ {
+ "name": "maker",
+ "isMut": true,
+ "isSigner": true
+ },
+ {
+ "name": "makerAta",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "makerToken",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "takerToken",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "auth",
+ "isMut": false,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "auth"
+ }
+ ]
+ }
+ },
+ {
+ "name": "vault",
+ "isMut": true,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "vault"
+ },
+ {
+ "kind": "account",
+ "type": "publicKey",
+ "account": "Escrow",
+ "path": "escrow"
+ }
+ ]
+ }
+ },
+ {
+ "name": "escrow",
+ "isMut": true,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "escrow"
+ },
+ {
+ "kind": "account",
+ "type": "publicKey",
+ "path": "maker"
+ },
+ {
+ "kind": "arg",
+ "type": "u64",
+ "path": "seed"
+ }
+ ]
+ }
+ },
+ {
+ "name": "tokenProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "associatedTokenProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": [
+ {
+ "name": "seed",
+ "type": "u64"
+ },
+ {
+ "name": "depositAmount",
+ "type": "u64"
+ },
+ {
+ "name": "offerAmount",
+ "type": "u64"
+ },
+ {
+ "name": "expiry",
+ "type": "u64"
+ }
+ ]
+ },
+ {
+ "name": "refund",
+ "accounts": [
+ {
+ "name": "maker",
+ "isMut": true,
+ "isSigner": true
+ },
+ {
+ "name": "makerAta",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "makerToken",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "auth",
+ "isMut": false,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "auth"
+ }
+ ]
+ }
+ },
+ {
+ "name": "vault",
+ "isMut": true,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "vault"
+ },
+ {
+ "kind": "account",
+ "type": "publicKey",
+ "account": "Escrow",
+ "path": "escrow"
+ }
+ ]
+ }
+ },
+ {
+ "name": "escrow",
+ "isMut": true,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "escrow"
+ },
+ {
+ "kind": "account",
+ "type": "publicKey",
+ "path": "maker"
+ },
+ {
+ "kind": "account",
+ "type": "u64",
+ "account": "Escrow",
+ "path": "escrow.seed"
+ }
+ ]
+ },
+ "relations": [
+ "maker",
+ "maker_token"
+ ]
+ },
+ {
+ "name": "tokenProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "associatedTokenProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": []
+ },
+ {
+ "name": "update",
+ "accounts": [
+ {
+ "name": "maker",
+ "isMut": true,
+ "isSigner": true
+ },
+ {
+ "name": "newTakerToken",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "escrow",
+ "isMut": true,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "escrow"
+ },
+ {
+ "kind": "account",
+ "type": "publicKey",
+ "path": "maker"
+ },
+ {
+ "kind": "account",
+ "type": "u64",
+ "account": "Escrow",
+ "path": "escrow.seed"
+ }
+ ]
+ },
+ "relations": [
+ "maker"
+ ]
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": [
+ {
+ "name": "offerAmount",
+ "type": "u64"
+ },
+ {
+ "name": "expiry",
+ "type": "u64"
+ }
+ ]
+ },
+ {
+ "name": "take",
+ "accounts": [
+ {
+ "name": "maker",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "makerReceiveAta",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "makerToken",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "taker",
+ "isMut": true,
+ "isSigner": true
+ },
+ {
+ "name": "takerAta",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "takerReceiveAta",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "takerToken",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "auth",
+ "isMut": false,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "auth"
+ }
+ ]
+ }
+ },
+ {
+ "name": "vault",
+ "isMut": true,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "vault"
+ },
+ {
+ "kind": "account",
+ "type": "publicKey",
+ "account": "Escrow",
+ "path": "escrow"
+ }
+ ]
+ }
+ },
+ {
+ "name": "escrow",
+ "isMut": true,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "escrow"
+ },
+ {
+ "kind": "account",
+ "type": "publicKey",
+ "path": "maker"
+ },
+ {
+ "kind": "account",
+ "type": "u64",
+ "account": "Escrow",
+ "path": "escrow.seed"
+ }
+ ]
+ },
+ "relations": [
+ "maker",
+ "taker_token",
+ "maker_token"
+ ]
+ },
+ {
+ "name": "tokenProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "associatedTokenProgram",
+ "isMut": false,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": []
+ }
+ ],
+ "accounts": [
+ {
+ "name": "Escrow",
+ "type": {
+ "kind": "struct",
+ "fields": [
+ {
+ "name": "maker",
+ "type": "publicKey"
+ },
+ {
+ "name": "makerToken",
+ "type": "publicKey"
+ },
+ {
+ "name": "takerToken",
+ "type": "publicKey"
+ },
+ {
+ "name": "offerAmount",
+ "type": "u64"
+ },
+ {
+ "name": "seed",
+ "type": "u64"
+ },
+ {
+ "name": "expiry",
+ "type": "u64"
+ },
+ {
+ "name": "authBump",
+ "type": "u8"
+ },
+ {
+ "name": "vaultBump",
+ "type": "u8"
+ },
+ {
+ "name": "escrowBump",
+ "type": "u8"
+ }
+ ]
+ }
+ }
+ ],
+ "errors": [
+ {
+ "code": 6000,
+ "name": "AuthBumpError",
+ "msg": "Unable to get auth bump"
+ },
+ {
+ "code": 6001,
+ "name": "VaultBumpError",
+ "msg": "Unable to get vault bump"
+ },
+ {
+ "code": 6002,
+ "name": "EscrowBumpError",
+ "msg": "Unable to get escrow bump"
+ },
+ {
+ "code": 6003,
+ "name": "MaxExpiryExceeded",
+ "msg": "Your expiration is too far into the future"
+ },
+ {
+ "code": 6004,
+ "name": "Expired",
+ "msg": "Escrow has expired"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/crate/tests/idls/four_in_line.json b/crate/tests/idls/four_in_line.json
new file mode 100644
index 0000000..5b2c15a
--- /dev/null
+++ b/crate/tests/idls/four_in_line.json
@@ -0,0 +1,197 @@
+{
+ "version": "0.1.0",
+ "name": "four_in_line",
+ "instructions": [
+ {
+ "name": "createGame",
+ "accounts": [
+ {
+ "name": "game",
+ "isMut": true,
+ "isSigner": false,
+ "pda": {
+ "seeds": [
+ {
+ "kind": "const",
+ "type": "string",
+ "value": "game"
+ },
+ {
+ "kind": "arg",
+ "type": "string",
+ "path": "name"
+ }
+ ]
+ }
+ },
+ {
+ "name": "payer",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "players",
+ "type": {
+ "vec": "publicKey"
+ }
+ },
+ {
+ "name": "gameType",
+ "type": "string"
+ }
+ ]
+ },
+ {
+ "name": "playGame",
+ "accounts": [
+ {
+ "name": "game",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "payer",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": [
+ {
+ "name": "play",
+ "type": "u8"
+ }
+ ]
+ }
+ ],
+ "accounts": [
+ {
+ "name": "Game",
+ "type": {
+ "kind": "struct",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "gameType",
+ "type": "string"
+ },
+ {
+ "name": "board",
+ "type": {
+ "vec": {
+ "defined": "Play"
+ }
+ }
+ },
+ {
+ "name": "players",
+ "type": {
+ "vec": "publicKey"
+ }
+ },
+ {
+ "name": "turn",
+ "type": "u8"
+ },
+ {
+ "name": "status",
+ "type": "string"
+ }
+ ]
+ }
+ }
+ ],
+ "types": [
+ {
+ "name": "Play",
+ "type": {
+ "kind": "enum",
+ "variants": [
+ {
+ "name": "Empty"
+ },
+ {
+ "name": "X"
+ },
+ {
+ "name": "O"
+ }
+ ]
+ }
+ }
+ ],
+ "events": [
+ {
+ "name": "GameCreated",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ }
+ ]
+ },
+ {
+ "name": "GameUpdated",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "board",
+ "type": {
+ "vec": {
+ "defined": "Play"
+ }
+ },
+ "index": false
+ },
+ {
+ "name": "status",
+ "type": "string",
+ "index": false
+ }
+ ]
+ }
+ ],
+ "errors": [
+ {
+ "code": 6000,
+ "name": "IncorrectUser",
+ "msg": "Isn't your turn to play"
+ },
+ {
+ "code": 6001,
+ "name": "InvalidCell",
+ "msg": "You can't use this cell now"
+ },
+ {
+ "code": 6002,
+ "name": "FinishedGame",
+ "msg": "You can't play, this game status is ended"
+ }
+ ],
+ "metadata": {
+ "address": "D17ocZkgrhNYizSp415zod3Nsd7BHBqjSTAFnPU9WG8A"
+ }
+}
\ No newline at end of file
diff --git a/crate/tests/idls/idl.json b/crate/tests/idls/idl.json
new file mode 100644
index 0000000..10fc4f6
--- /dev/null
+++ b/crate/tests/idls/idl.json
@@ -0,0 +1,26 @@
+{
+ "version": "0.1.0",
+ "name": "",
+ "instructions": [
+ {
+ "name": "initialize",
+ "accounts": [],
+ "args": []
+ }
+ ],
+ "accounts": [
+ {
+ "name": "dgd",
+ "type": {
+ "kind": "struct",
+ "fields": []
+ }
+ }
+ ],
+ "types": [],
+ "events": [],
+ "errors": [],
+ "metadata": {
+ "address": ""
+ }
+}
\ No newline at end of file
diff --git a/crate/tests/idls/mate.json b/crate/tests/idls/mate.json
new file mode 100644
index 0000000..cf06245
--- /dev/null
+++ b/crate/tests/idls/mate.json
@@ -0,0 +1,797 @@
+{
+ "version": "0.1.0",
+ "name": "mate",
+ "instructions": [
+ {
+ "name": "createGroup",
+ "accounts": [
+ {
+ "name": "group",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "payer",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "ratio",
+ "type": "u16"
+ },
+ {
+ "name": "members",
+ "type": {
+ "vec": "publicKey"
+ }
+ }
+ ]
+ },
+ {
+ "name": "createProject",
+ "accounts": [
+ {
+ "name": "project",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "payer",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "group",
+ "type": "string"
+ },
+ {
+ "name": "projectType",
+ "type": "string"
+ },
+ {
+ "name": "ratio",
+ "type": "u16"
+ },
+ {
+ "name": "payments",
+ "type": {
+ "vec": {
+ "defined": "Payment"
+ }
+ }
+ },
+ {
+ "name": "commonExpenses",
+ "type": "u64"
+ },
+ {
+ "name": "currency",
+ "type": "string"
+ },
+ {
+ "name": "amount",
+ "type": "u64"
+ },
+ {
+ "name": "milestones",
+ "type": "u8"
+ },
+ {
+ "name": "startDate",
+ "type": "u64"
+ },
+ {
+ "name": "endDate",
+ "type": "u64"
+ },
+ {
+ "name": "client",
+ "type": "publicKey"
+ }
+ ]
+ },
+ {
+ "name": "payProject",
+ "accounts": [
+ {
+ "name": "project",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "payer",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "group",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "member0",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "member1",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "member2",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "member3",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "member4",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "member5",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "member6",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "member7",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "member8",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "member9",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": []
+ },
+ {
+ "name": "useProjectTreasury",
+ "accounts": [
+ {
+ "name": "project",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "payer",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "receiver",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": [
+ {
+ "name": "amount",
+ "type": "u64"
+ }
+ ]
+ },
+ {
+ "name": "confirmProjectParticipation",
+ "accounts": [
+ {
+ "name": "project",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "user",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": []
+ },
+ {
+ "name": "rejectProjectParticipation",
+ "accounts": [
+ {
+ "name": "project",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "user",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": []
+ },
+ {
+ "name": "updateProject",
+ "accounts": [
+ {
+ "name": "project",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "payer",
+ "isMut": true,
+ "isSigner": false
+ },
+ {
+ "name": "systemProgram",
+ "isMut": false,
+ "isSigner": false
+ }
+ ],
+ "args": [
+ {
+ "name": "ratio",
+ "type": "u16"
+ },
+ {
+ "name": "payments",
+ "type": {
+ "vec": {
+ "defined": "Payment"
+ }
+ }
+ },
+ {
+ "name": "commonExpenses",
+ "type": "u64"
+ },
+ {
+ "name": "currency",
+ "type": "string"
+ },
+ {
+ "name": "amount",
+ "type": "u64"
+ },
+ {
+ "name": "milestones",
+ "type": "u8"
+ },
+ {
+ "name": "startDate",
+ "type": "u64"
+ },
+ {
+ "name": "endDate",
+ "type": "u64"
+ },
+ {
+ "name": "client",
+ "type": "publicKey"
+ }
+ ]
+ }
+ ],
+ "accounts": [
+ {
+ "name": "Group",
+ "type": {
+ "kind": "struct",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "ratio",
+ "type": "u16"
+ },
+ {
+ "name": "members",
+ "type": {
+ "vec": "publicKey"
+ }
+ },
+ {
+ "name": "bump",
+ "type": "u8"
+ }
+ ]
+ }
+ },
+ {
+ "name": "Project",
+ "type": {
+ "kind": "struct",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "group",
+ "type": "string"
+ },
+ {
+ "name": "projectType",
+ "type": "string"
+ },
+ {
+ "name": "ratio",
+ "type": "u16"
+ },
+ {
+ "name": "members",
+ "type": {
+ "vec": {
+ "defined": "Member"
+ }
+ }
+ },
+ {
+ "name": "currency",
+ "type": "string"
+ },
+ {
+ "name": "status",
+ "type": "string"
+ },
+ {
+ "name": "amount",
+ "type": "u64"
+ },
+ {
+ "name": "commonExpenses",
+ "type": "u64"
+ },
+ {
+ "name": "startDate",
+ "type": "u64"
+ },
+ {
+ "name": "endDate",
+ "type": "u64"
+ },
+ {
+ "name": "client",
+ "type": "publicKey"
+ },
+ {
+ "name": "milestones",
+ "type": "u8"
+ },
+ {
+ "name": "bump",
+ "type": "u8"
+ }
+ ]
+ }
+ }
+ ],
+ "types": [
+ {
+ "name": "Payment",
+ "type": {
+ "kind": "struct",
+ "fields": [
+ {
+ "name": "member",
+ "type": "publicKey"
+ },
+ {
+ "name": "amount",
+ "type": "u64"
+ }
+ ]
+ }
+ },
+ {
+ "name": "Member",
+ "type": {
+ "kind": "struct",
+ "fields": [
+ {
+ "name": "pubkey",
+ "type": "publicKey"
+ },
+ {
+ "name": "amount",
+ "type": "u64"
+ },
+ {
+ "name": "status",
+ "type": "string"
+ }
+ ]
+ }
+ }
+ ],
+ "events": [
+ {
+ "name": "GroupCreated",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "ratio",
+ "type": "u16",
+ "index": false
+ },
+ {
+ "name": "members",
+ "type": {
+ "vec": "publicKey"
+ },
+ "index": false
+ }
+ ]
+ },
+ {
+ "name": "ProjectCreated",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "group",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "projectType",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "ratio",
+ "type": "u16",
+ "index": false
+ },
+ {
+ "name": "members",
+ "type": {
+ "vec": {
+ "defined": "Member"
+ }
+ },
+ "index": false
+ },
+ {
+ "name": "currency",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "status",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "amount",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "commonExpenses",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "startDate",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "endDate",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "client",
+ "type": "publicKey",
+ "index": false
+ },
+ {
+ "name": "milestones",
+ "type": "u8",
+ "index": false
+ }
+ ]
+ },
+ {
+ "name": "ProyectPaid",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "status",
+ "type": "string",
+ "index": false
+ }
+ ]
+ },
+ {
+ "name": "ProyectTreasuryUsed",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "amount",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "receiver",
+ "type": "publicKey",
+ "index": false
+ }
+ ]
+ },
+ {
+ "name": "ProyectRejected",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "status",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "causer",
+ "type": "publicKey",
+ "index": false
+ }
+ ]
+ },
+ {
+ "name": "ProyectParticipationConfirmed",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "member",
+ "type": "publicKey",
+ "index": false
+ }
+ ]
+ },
+ {
+ "name": "ProyectSigned",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "group",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "projectType",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "ratio",
+ "type": "u16",
+ "index": false
+ },
+ {
+ "name": "members",
+ "type": {
+ "vec": {
+ "defined": "Member"
+ }
+ },
+ "index": false
+ },
+ {
+ "name": "currency",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "status",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "amount",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "commonExpenses",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "startDate",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "endDate",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "client",
+ "type": "publicKey",
+ "index": false
+ },
+ {
+ "name": "milestones",
+ "type": "u8",
+ "index": false
+ }
+ ]
+ },
+ {
+ "name": "ProjectUpdated",
+ "fields": [
+ {
+ "name": "name",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "group",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "projectType",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "ratio",
+ "type": "u16",
+ "index": false
+ },
+ {
+ "name": "members",
+ "type": {
+ "vec": {
+ "defined": "Member"
+ }
+ },
+ "index": false
+ },
+ {
+ "name": "currency",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "status",
+ "type": "string",
+ "index": false
+ },
+ {
+ "name": "amount",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "commonExpenses",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "startDate",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "endDate",
+ "type": "u64",
+ "index": false
+ },
+ {
+ "name": "client",
+ "type": "publicKey",
+ "index": false
+ },
+ {
+ "name": "milestones",
+ "type": "u8",
+ "index": false
+ }
+ ]
+ }
+ ],
+ "errors": [
+ {
+ "code": 6000,
+ "name": "NotMember",
+ "msg": "User isn't a member of this project"
+ },
+ {
+ "code": 6001,
+ "name": "InvalidActionForProjectCurrentStatus",
+ "msg": "Cannot perform the requested instruction because the current project status"
+ },
+ {
+ "code": 6002,
+ "name": "OnlyCanUpdateRejectedProjects",
+ "msg": "Only rejected projects can be updated"
+ }
+ ],
+ "metadata": {
+ "address": "G6DTAAexRyc8Dg9zDKdiFMGsXQE14rke6r4P2P1N43TL"
+ }
+}
\ No newline at end of file
diff --git a/crate/tests/save_template.rs b/crate/tests/save_template.rs
new file mode 100644
index 0000000..ff95ca1
--- /dev/null
+++ b/crate/tests/save_template.rs
@@ -0,0 +1,163 @@
+use soda_sol::{save_template, Template, TemplateMetadata, TemplateFile, Content, TemplateHelper};
+
+#[test]
+fn create_a_template_file() {
+ let metadata = TemplateMetadata {name:"test".to_string(),description:"test".to_string(), version: "".to_string(), authors: "".to_string(), image: "".to_string(), tags: "".to_string() };
+
+ assert_eq!(
+ save_template(
+ Template {
+ files: vec![],
+ helpers: vec![],
+ metadata
+ },
+ "./tests/test.soda"
+ ).unwrap(),
+ ()
+ )
+}
+
+
+
+#[test]
+fn create_a_template_file_with_files() {
+ let metadata = TemplateMetadata {name:"test".to_string(),description:"test".to_string(), version: "".to_string(), authors: "".to_string(), image: "".to_string(), tags: "".to_string() };
+
+ assert_eq!(
+ save_template(
+ Template {
+ files: vec![TemplateFile {
+ path: "test".to_string(),
+ content: Content::String("test".to_string())
+ }],
+ helpers: vec![],
+ metadata
+ },
+ "./tests/test.soda"
+ ).unwrap(),
+ ()
+ )
+}
+
+#[test]
+fn create_a_template_file_with_helpers() {
+ let metadata = TemplateMetadata {name:"test".to_string(),description:"test".to_string(), version: "".to_string(), authors: "".to_string(), image: "".to_string(), tags: "".to_string() };
+
+ assert_eq!(
+ save_template(
+ Template {
+ files: vec![],
+ helpers: vec![TemplateHelper {
+ helper_name: "test".to_string(),
+ script: "test".to_string()
+ }],
+ metadata
+ },
+ "./tests/test.soda"
+ ).unwrap(),
+ ()
+ )
+}
+
+#[test]
+fn create_a_template_file_with_helpers_and_files() {
+ let metadata = TemplateMetadata {name:"test".to_string(),description:"test".to_string(), version: "".to_string(), authors: "".to_string(), image: "".to_string(), tags: "".to_string() };
+
+ assert_eq!(
+ save_template(
+ Template {
+ files: vec![TemplateFile {
+ path: "test".to_string(),
+ content: Content::String("test".to_string())
+ }],
+ helpers: vec![TemplateHelper {
+ helper_name: "test".to_string(),
+ script: "test".to_string()
+ }],
+ metadata
+ },
+ "./tests/test.soda"
+ ).unwrap(),
+ ()
+ )
+}
+
+#[test]
+fn create_a_template_file_with_metadata() {
+ let metadata = TemplateMetadata {name:"test".to_string(),description:"test".to_string(), version: "".to_string(), authors: "".to_string(), image: "".to_string(), tags: "".to_string() };
+
+ assert_eq!(
+ save_template(
+ Template {
+ files: vec![],
+ helpers: vec![],
+ metadata
+ },
+ "./tests/test.soda"
+ ).unwrap(),
+ ()
+ )
+}
+
+#[test]
+fn create_a_template_file_with_metadata_and_files() {
+ let metadata = TemplateMetadata {name:"test".to_string(),description:"test".to_string(), version: "".to_string(), authors: "".to_string(), image: "".to_string(), tags: "".to_string() };
+
+ assert_eq!(
+ save_template(
+ Template {
+ files: vec![TemplateFile {
+ path: "test".to_string(),
+ content: Content::String("test".to_string())
+ }],
+ helpers: vec![],
+ metadata
+ },
+ "./tests/test.soda"
+ ).unwrap(),
+ ()
+ )
+}
+
+#[test]
+fn create_a_template_file_with_metadata_and_helpers() {
+ let metadata = TemplateMetadata {name:"test".to_string(),description:"test".to_string(), version: "".to_string(), authors: "".to_string(), image: "".to_string(), tags: "".to_string() };
+
+ assert_eq!(
+ save_template(
+ Template {
+ files: vec![],
+ helpers: vec![TemplateHelper {
+ helper_name: "test".to_string(),
+ script: "test".to_string()
+ }],
+ metadata
+ },
+ "./tests/test.soda"
+ ).unwrap(),
+ ()
+ )
+}
+
+#[test]
+fn create_a_template_file_with_metadata_helpers_and_files() {
+ let metadata = TemplateMetadata {name:"test".to_string(),description:"test".to_string(), version: "".to_string(), authors: "".to_string(), image: "".to_string(), tags: "".to_string() };
+
+ assert_eq!(
+ save_template(
+ Template {
+ files: vec![TemplateFile {
+ path: "test".to_string(),
+ content: Content::String("test".to_string())
+ }],
+ helpers: vec![TemplateHelper {
+ helper_name: "test".to_string(),
+ script: "test".to_string()
+ }],
+ metadata
+ },
+ "./tests/test.soda"
+ ).unwrap(),
+ ()
+ )
+}
diff --git a/crate/tests/test.soda b/crate/tests/test.soda
new file mode 100644
index 0000000..677a1fe
Binary files /dev/null and b/crate/tests/test.soda differ
diff --git a/templates/anchor_nextjs/files/anchor_workspace/programs/{{name}}/src/lib.rs.hbs b/templates/anchor_nextjs/files/anchor_workspace/programs/{{name}}/src/lib.rs.hbs
index 18eacbc..e782e6b 100644
--- a/templates/anchor_nextjs/files/anchor_workspace/programs/{{name}}/src/lib.rs.hbs
+++ b/templates/anchor_nextjs/files/anchor_workspace/programs/{{name}}/src/lib.rs.hbs
@@ -43,14 +43,23 @@ pub struct {{this.name}} {
}
{{/each}}
-// Additional structures
+// Types
{{#each types}}
#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
+{{#if (eq this.type.kind "struct")}}
pub struct {{this.name}} {
- {{#each this.type_.fields}}
- pub {{this.name}}: {{this.type}},
+ {{#each this.type.fields}}
+ pub {{this.name}}: {{{type_from_account_field this.type}}},
+ {{/each}}
+}
+{{/if}}
+{{#if (eq this.type.kind "enum")}}
+pub enum {{this.name}} {
+ {{#each this.type.variants}}
+ {{this.name}},
{{/each}}
}
+{{/if}}
{{/each}}
// Events
diff --git a/templates/anchor_nextjs/files/client/src/pages/examples.tsx.hbs b/templates/anchor_nextjs/files/client/src/pages/examples.tsx.hbs
deleted file mode 100644
index fe66fcc..0000000
--- a/templates/anchor_nextjs/files/client/src/pages/examples.tsx.hbs
+++ /dev/null
@@ -1,112 +0,0 @@
-import { FC, useEffect, useState } from "react";
-import { useAnchorWallet, useConnection } from "@solana/wallet-adapter-react";
-import type { NextPage } from "next";
-import Head from "next/head";
-import { useProgram } from "../utils/useProgram";
-import { useRouter } from "next/router"
-
-{{#each instructions}}
-import {
- {{name}}
-} from '../utils/callInstructions'
-{{/each}}
-
-
-const Examples: NextPage = (props) => {
- const wallet = useAnchorWallet();
- const { connection } = useConnection();
- const { program } = useProgram({ connection, wallet });
-
-// React UseStates hooks for managing args
-{{#each instructions}}
-//for {{name}}
-{{#each args}}
-const [{{name}}_for_{{../name}} , set{{name}}_for_{{../name}}] = useState()
-{{/each}}
-{{/each}}
-
-//handler functions for inputs feilds
-{{#each instructions}}
-{{#each args}}
-const {{name}}handler_for_{{../name}} = (e) => {
- set{{name}}_for_{{../name}}(e.target.value)
-}
-{{/each}}
-{{/each}}
-
-// variables for accounts
-const authority = ""
-const systemProgram = ""
-const clock = ""
-
-{{#each instructions}}
-{{#each accounts}}
-{{#if this.isMut}}
-{{#unless this.isSigner}}
-const {{name}}_for_{{../name}} = ""
-{{/unless}}
-{{/if}}
-{{/each}}
-{{/each}}
-
-
-{{#each events}}
- useEffect(() => {
- if (!program) return;
- const listener = program.addEventListener(
- "{{name}}",
- async (event, _slot, _sig) => {
- console.log(event);
- }
- );
-
- return () => {
- program.removeEventListener(listener);
- };
- }, [program]);
-
-{{/each}}
- return (
- <>
-
- {{name}}
-
-
-
- {{#each instructions}}
-
- for {{name}}
- {{#each args}}
-
- {{name}}
-
-
- {{/each}}
-
- {{/each}}
-
- // buttons for calling instructions
- {{#each instructions}}
-
- {{/each}}
- >
- );
-};
-
-export default Examples;
diff --git a/templates/anchor_nextjs/files/client/.gitignore.hbs b/templates/anchor_nextjs/files/web_client/.gitignore.hbs
similarity index 100%
rename from templates/anchor_nextjs/files/client/.gitignore.hbs
rename to templates/anchor_nextjs/files/web_client/.gitignore.hbs
diff --git a/templates/anchor_nextjs/files/client/manifest.json.hbs b/templates/anchor_nextjs/files/web_client/manifest.json.hbs
similarity index 100%
rename from templates/anchor_nextjs/files/client/manifest.json.hbs
rename to templates/anchor_nextjs/files/web_client/manifest.json.hbs
diff --git a/templates/anchor_nextjs/files/client/next-env.d.ts.hbs b/templates/anchor_nextjs/files/web_client/next-env.d.ts.hbs
similarity index 100%
rename from templates/anchor_nextjs/files/client/next-env.d.ts.hbs
rename to templates/anchor_nextjs/files/web_client/next-env.d.ts.hbs
diff --git a/templates/anchor_nextjs/files/client/next.config.js.hbs b/templates/anchor_nextjs/files/web_client/next.config.js.hbs
similarity index 100%
rename from templates/anchor_nextjs/files/client/next.config.js.hbs
rename to templates/anchor_nextjs/files/web_client/next.config.js.hbs
diff --git a/templates/anchor_nextjs/files/client/package.json.hbs b/templates/anchor_nextjs/files/web_client/package.json.hbs
similarity index 100%
rename from templates/anchor_nextjs/files/client/package.json.hbs
rename to templates/anchor_nextjs/files/web_client/package.json.hbs
diff --git a/templates/anchor_nextjs/files/client/postcss.config.js.hbs b/templates/anchor_nextjs/files/web_client/postcss.config.js.hbs
similarity index 100%
rename from templates/anchor_nextjs/files/client/postcss.config.js.hbs
rename to templates/anchor_nextjs/files/web_client/postcss.config.js.hbs
diff --git a/templates/anchor_nextjs/files/client/src/contexts/ClientWalletProvider.tsx.hbs b/templates/anchor_nextjs/files/web_client/src/contexts/ClientWalletProvider.tsx.hbs
similarity index 100%
rename from templates/anchor_nextjs/files/client/src/contexts/ClientWalletProvider.tsx.hbs
rename to templates/anchor_nextjs/files/web_client/src/contexts/ClientWalletProvider.tsx.hbs
diff --git a/templates/anchor_nextjs/files/client/src/manifest.json.hbs b/templates/anchor_nextjs/files/web_client/src/manifest.json.hbs
similarity index 100%
rename from templates/anchor_nextjs/files/client/src/manifest.json.hbs
rename to templates/anchor_nextjs/files/web_client/src/manifest.json.hbs
diff --git a/templates/anchor_nextjs/files/client/src/pages/_app.tsx.hbs b/templates/anchor_nextjs/files/web_client/src/pages/_app.tsx.hbs
similarity index 100%
rename from templates/anchor_nextjs/files/client/src/pages/_app.tsx.hbs
rename to templates/anchor_nextjs/files/web_client/src/pages/_app.tsx.hbs
diff --git a/templates/anchor_nextjs/files/client/src/pages/_document.js.hbs b/templates/anchor_nextjs/files/web_client/src/pages/_document.js.hbs
similarity index 94%
rename from templates/anchor_nextjs/files/client/src/pages/_document.js.hbs
rename to templates/anchor_nextjs/files/web_client/src/pages/_document.js.hbs
index 106a123..db36a41 100644
--- a/templates/anchor_nextjs/files/client/src/pages/_document.js.hbs
+++ b/templates/anchor_nextjs/files/web_client/src/pages/_document.js.hbs
@@ -1,5 +1,5 @@
import Document, { Html, Head, Main, NextScript } from "next/document";
-
+import Examples from './examples'
class MyDocument extends Document {
render() {
return (
@@ -33,6 +33,7 @@ class MyDocument extends Document {
+