diff --git a/Cargo.lock b/Cargo.lock index ce9a192..76b74ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -65,6 +65,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -1792,6 +1801,52 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pinocchio" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a8a77bf74e1c4050a2bad53648acfcfbf22b1806473f9c9334a58ef3b38ec4" + +[[package]] +name = "pinocchio-log" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95bd1d0d8a24f2637cb6748232912b6bae67b643b7ca903c68e670e704cd0652" +dependencies = [ + "pinocchio-log-macro", +] + +[[package]] +name = "pinocchio-log-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "657779675430389ebbf00b8a5b6325e64157795ac61702fadf8756ac8806a50e" +dependencies = [ + "quote", + "regex", + "syn 1.0.109", +] + +[[package]] +name = "pinocchio-pubkey" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "103d69776a0c585bb01b344d641346593adb4496120796df229722a9bab2db68" +dependencies = [ + "five8_const", + "pinocchio", +] + +[[package]] +name = "pinocchio-system" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "691ec8989fcb86fba22f22deb3cae9d440dc174d1735dd785b172349e9cbbb8a" +dependencies = [ + "pinocchio", + "pinocchio-pubkey", +] + [[package]] name = "pkg-config" version = "0.3.31" @@ -1971,6 +2026,35 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rpassword" version = "7.3.1" @@ -3343,6 +3427,10 @@ version = "3.0.3" dependencies = [ "bytemuck", "num_enum", + "pinocchio", + "pinocchio-log", + "pinocchio-pubkey", + "pinocchio-system", "solana-program", "spl-associated-token-account", "spl-token 4.0.2", diff --git a/Cargo.toml b/Cargo.toml index fba8352..2d2b272 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,9 +28,13 @@ solana-cli-config = "^2.1" solana-program = "^2.1" solana-sdk = "^2.1" spl-token = { features = ["no-entrypoint"], version = "^4" } -spl-associated-token-account = { features = [ "no-entrypoint" ], version = "6.0" } +spl-associated-token-account = { features = ["no-entrypoint"], version = "6.0" } thiserror = "1.0.57" tokio = "1.35" quote = "1.0" toml = "0.8.19" serde_json = "1.0.137" +pinocchio = "0.7.1" +pinocchio-log = "0.3.0" +pinocchio-pubkey = "0.2.2" +pinocchio-system = "0.2.1" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 84c16d4..47ff72d 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -11,8 +11,14 @@ repository.workspace = true keywords.workspace = true [features] -deafult = [] +default = [] spl = ["spl-token", "spl-associated-token-account"] +pinocchio = [ + "dep:pinocchio", + "dep:pinocchio-log", + "dep:pinocchio-pubkey", + "dep:pinocchio-system", +] [dependencies] bytemuck.workspace = true @@ -21,4 +27,7 @@ solana-program.workspace = true spl-token = { workspace = true, optional = true } spl-associated-token-account = { workspace = true, optional = true } thiserror.workspace = true - +pinocchio = { workspace = true, optional = true } +pinocchio-log = { workspace = true, optional = true } +pinocchio-pubkey = { workspace = true, optional = true } +pinocchio-system = { workspace = true, optional = true } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index e3bd4f1..1074218 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,17 +1,25 @@ +#[cfg(not(feature = "pinocchio"))] mod cpi; mod loaders; mod log; pub mod macros; +#[cfg(feature = "pinocchio")] +mod pinocchio_cpi; mod traits; mod utils; +#[cfg(not(feature = "pinocchio"))] pub use cpi::*; pub use log::*; +#[cfg(feature = "pinocchio")] +pub use pinocchio_cpi::*; pub use traits::*; pub use utils::*; pub use bytemuck::{Pod, Zeroable}; pub use num_enum::{IntoPrimitive, TryFromPrimitive}; + +#[cfg(not(feature = "pinocchio"))] pub use solana_program::{ account_info::AccountInfo, clock::Clock, @@ -23,4 +31,22 @@ pub use solana_program::{ system_program, sysvar, sysvar::Sysvar, }; + pub use thiserror::Error; + +#[cfg(feature = "pinocchio")] +pub use pinocchio::{ + account_info::AccountInfo, + entrypoint, + instruction::{AccountMeta, Instruction}, + msg, + program_error::ProgramError, + pubkey::{self, Pubkey}, + ProgramResult, +}; + +#[cfg(feature = "pinocchio")] +pub use pinocchio_pubkey::declare_id; + +#[cfg(feature = "pinocchio")] +pub use pinocchio_system as system_program; diff --git a/lib/src/loaders.rs b/lib/src/loaders.rs index b466ce3..5e5061c 100644 --- a/lib/src/loaders.rs +++ b/lib/src/loaders.rs @@ -1,6 +1,11 @@ use bytemuck::Pod; + +#[cfg(not(feature = "pinocchio"))] use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; +#[cfg(feature = "pinocchio")] +use pinocchio::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + use crate::{ trace, AccountDeserialize, AccountInfoValidation, AsAccount, CloseAccount, Discriminator, LamportTransfer, @@ -12,6 +17,7 @@ use solana_program::program_pack::Pack; #[cfg(feature = "spl")] use crate::{AccountValidation, AsSplToken}; +#[cfg(not(feature = "pinocchio"))] impl AccountInfoValidation for AccountInfo<'_> { #[track_caller] fn is_empty(&self) -> Result<&Self, ProgramError> { @@ -115,6 +121,7 @@ impl AccountInfoValidation for AccountInfo<'_> { } } +#[cfg(not(feature = "pinocchio"))] impl AsAccount for AccountInfo<'_> { #[track_caller] fn as_account(&self, program_id: &Pubkey) -> Result<&T, ProgramError> @@ -162,6 +169,7 @@ impl AsAccount for AccountInfo<'_> { } } +#[cfg(not(feature = "pinocchio"))] impl<'a, 'info> LamportTransfer<'a, 'info> for AccountInfo<'info> { #[inline(always)] fn send(&'a self, lamports: u64, to: &'a AccountInfo<'info>) { @@ -178,6 +186,7 @@ impl<'a, 'info> LamportTransfer<'a, 'info> for AccountInfo<'info> { } } +#[cfg(not(feature = "pinocchio"))] impl<'a, 'info> CloseAccount<'a, 'info> for AccountInfo<'info> { fn close(&'a self, to: &'a AccountInfo<'info>) -> Result<(), ProgramError> { // Realloc data to zero. @@ -386,3 +395,187 @@ impl AccountValidation for spl_token::state::Account { panic!("not implemented") } } + +#[cfg(feature = "pinocchio")] +impl AccountInfoValidation for AccountInfo { + #[track_caller] + fn is_empty(&self) -> Result<&Self, ProgramError> { + if !self.data_is_empty() { + return Err(trace( + "Account already initialized", + ProgramError::AccountAlreadyInitialized, + )); + } + Ok(self) + } + #[track_caller] + fn is_executable(&self) -> Result<&Self, ProgramError> { + if !self.executable() { + return Err(trace( + "Account is not executable", + ProgramError::InvalidAccountData, + )); + } + Ok(self) + } + + #[track_caller] + fn is_program(&self, program_id: &Pubkey) -> Result<&Self, ProgramError> { + self.has_address(program_id)?.is_executable() + } + + #[track_caller] + fn is_signer(&self) -> Result<&Self, ProgramError> { + if !self.is_signer() { + return Err(trace( + "Account is not a signer", + ProgramError::MissingRequiredSignature, + )); + } + Ok(self) + } + + #[track_caller] + fn is_sysvar(&self, _sysvar_id: &Pubkey) -> Result<&Self, ProgramError> { + unimplemented!() + // Owner pubkey for sysvar accounts + // solana_pubkey::declare_id!("Sysvar1111111111111111111111111111111111111"); + // self.has_owner(&solana_program::sysvar::ID)? + // .has_address(sysvar_id) + } + + #[track_caller] + fn is_type(&self, program_id: &Pubkey) -> Result<&Self, ProgramError> { + self.has_owner(program_id)?; + if self.try_borrow_data()?[0].ne(&T::discriminator()) { + return Err(trace( + format!("Account is not of type {}", T::discriminator()).as_str(), + ProgramError::InvalidAccountData, + )); + } + Ok(self) + } + + #[track_caller] + fn is_writable(&self) -> Result<&Self, ProgramError> { + if !self.is_writable() { + return Err(trace( + "Account is not writable", + ProgramError::MissingRequiredSignature, + )); + } + Ok(self) + } + + #[track_caller] + fn has_address(&self, address: &Pubkey) -> Result<&Self, ProgramError> { + if self.key().ne(address) { + return Err(trace( + "Account has invalid address", + ProgramError::InvalidAccountData, + )); + } + Ok(self) + } + + #[track_caller] + fn has_owner(&self, owner: &Pubkey) -> Result<&Self, ProgramError> { + if self.owner().ne(owner) { + return Err(trace( + "Account has invalid owner", + ProgramError::InvalidAccountOwner, + )); + } + Ok(self) + } + + #[track_caller] + fn has_seeds(&self, seeds: &[&[u8]], program_id: &Pubkey) -> Result<&Self, ProgramError> { + let pda = pinocchio::pubkey::find_program_address(seeds, program_id); + if self.key().ne(&pda.0) { + return Err(trace( + "Account has invalid seeds", + ProgramError::InvalidSeeds, + )); + } + Ok(self) + } +} + +#[cfg(feature = "pinocchio")] +impl AsAccount for AccountInfo { + #[track_caller] + fn as_account(&self, program_id: &Pubkey) -> Result<&T, ProgramError> + where + T: AccountDeserialize + Discriminator + Pod, + { + unsafe { + // Validate account owner. + self.has_owner(program_id)?; + + // Validate account data length. + let data = self.try_borrow_data()?; + let expected_len = 8 + std::mem::size_of::(); + if data.len() != expected_len { + return Err(ProgramError::InvalidAccountData); + } + + // Deserialize account data. + T::try_from_bytes(std::slice::from_raw_parts(data.as_ptr(), expected_len)) + } + } + + #[track_caller] + fn as_account_mut(&self, program_id: &Pubkey) -> Result<&mut T, ProgramError> + where + T: AccountDeserialize + Discriminator + Pod, + { + unsafe { + // Validate account owner. + self.has_owner(program_id)?; + + // Validate account data length. + let mut data = self.try_borrow_mut_data()?; + let expected_len = 8 + std::mem::size_of::(); + if data.len() != expected_len { + return Err(ProgramError::InvalidAccountData); + } + + // Deserialize account data. + T::try_from_bytes_mut(std::slice::from_raw_parts_mut( + data.as_mut_ptr(), + expected_len, + )) + } + } +} + +#[cfg(feature = "pinocchio")] +impl LamportTransfer for AccountInfo { + #[inline(always)] + fn send(&self, lamports: u64, to: AccountInfo) { + *self + .try_borrow_mut_lamports() + .expect("Failed to borrow mut lamports") -= lamports; + *to.try_borrow_mut_lamports() + .expect("Failed to borrow mut lamports") += lamports; + } + + #[inline(always)] + fn collect(&self, _lamports: u64, _from: AccountInfo) -> Result<(), ProgramError> { + unimplemented!() + } +} + +#[cfg(feature = "pinocchio")] +impl CloseAccount for AccountInfo { + fn close(&self, to: AccountInfo) -> Result<(), ProgramError> { + // Realloc data to zero. + self.realloc(0, true)?; + + // Return rent lamports. + self.send(self.lamports(), to); + + Ok(()) + } +} diff --git a/lib/src/log.rs b/lib/src/log.rs index 5156cd5..b1c6ea6 100644 --- a/lib/src/log.rs +++ b/lib/src/log.rs @@ -1,12 +1,34 @@ +#[cfg(not(feature = "pinocchio"))] use solana_program::program_error::ProgramError; +#[cfg(feature = "pinocchio")] +use pinocchio::program_error::ProgramError; + /// Logs a message. +#[cfg(not(feature = "pinocchio"))] #[inline(always)] pub fn log(msg: String) { solana_program::log::sol_log(msg.as_str()); } /// Logs the call trace and returns the error. +#[cfg(not(feature = "pinocchio"))] +#[track_caller] +pub fn trace(msg: &str, error: ProgramError) -> ProgramError { + let caller = std::panic::Location::caller(); + log(format!("{}: {}", msg, caller)); + error +} + +/// Logs a message. +#[cfg(feature = "pinocchio")] +#[inline(always)] +pub fn log(msg: String) { + pinocchio_log::log!("{}", msg.as_str()); +} + +/// Logs the call trace and returns the error. +#[cfg(feature = "pinocchio")] #[track_caller] pub fn trace(msg: &str, error: ProgramError) -> ProgramError { let caller = std::panic::Location::caller(); diff --git a/lib/src/macros.rs b/lib/src/macros.rs index 7ec182e..0700087 100644 --- a/lib/src/macros.rs +++ b/lib/src/macros.rs @@ -24,12 +24,8 @@ macro_rules! impl_from_bytes { macro_rules! impl_instruction_from_bytes { ($struct_name:ident) => { impl $struct_name { - pub fn try_from_bytes( - data: &[u8], - ) -> Result<&Self, solana_program::program_error::ProgramError> { - bytemuck::try_from_bytes::(data).or(Err( - solana_program::program_error::ProgramError::InvalidInstructionData, - )) + pub fn try_from_bytes(data: &[u8]) -> Result<&Self, ProgramError> { + bytemuck::try_from_bytes::(data).or(Err(ProgramError::InvalidInstructionData)) } } }; @@ -48,28 +44,21 @@ macro_rules! account { impl $crate::AccountValidation for $struct_name { #[track_caller] - fn assert( - &self, - condition: F, - ) -> Result<&Self, solana_program::program_error::ProgramError> + fn assert(&self, condition: F) -> Result<&Self, ProgramError> where F: Fn(&Self) -> bool, { if !condition(self) { return Err(trace( "Account data is invalid", - solana_program::program_error::ProgramError::InvalidAccountData, + ProgramError::InvalidAccountData, )); } Ok(self) } #[track_caller] - fn assert_err( - &self, - condition: F, - err: solana_program::program_error::ProgramError, - ) -> Result<&Self, solana_program::program_error::ProgramError> + fn assert_err(&self, condition: F, err: ProgramError) -> Result<&Self, ProgramError> where F: Fn(&Self) -> bool, { @@ -80,35 +69,28 @@ macro_rules! account { } #[track_caller] - fn assert_msg( - &self, - condition: F, - msg: &str, - ) -> Result<&Self, solana_program::program_error::ProgramError> + fn assert_msg(&self, condition: F, msg: &str) -> Result<&Self, ProgramError> where F: Fn(&Self) -> bool, { if !condition(self) { return Err(trace( format!("Account data is invalid: {}", msg).as_str(), - solana_program::program_error::ProgramError::InvalidAccountData, + ProgramError::InvalidAccountData, )); } Ok(self) } #[track_caller] - fn assert_mut( - &mut self, - condition: F, - ) -> Result<&mut Self, solana_program::program_error::ProgramError> + fn assert_mut(&mut self, condition: F) -> Result<&mut Self, ProgramError> where F: Fn(&Self) -> bool, { if !condition(self) { return Err(trace( "Account data is invalid", - solana_program::program_error::ProgramError::InvalidAccountData, + ProgramError::InvalidAccountData, )); } Ok(self) @@ -118,8 +100,8 @@ macro_rules! account { fn assert_mut_err( &mut self, condition: F, - err: solana_program::program_error::ProgramError, - ) -> Result<&mut Self, solana_program::program_error::ProgramError> + err: ProgramError, + ) -> Result<&mut Self, ProgramError> where F: Fn(&Self) -> bool, { @@ -134,14 +116,14 @@ macro_rules! account { &mut self, condition: F, msg: &str, - ) -> Result<&mut Self, solana_program::program_error::ProgramError> + ) -> Result<&mut Self, ProgramError> where F: Fn(&Self) -> bool, { if !condition(self) { return Err(trace( format!("Account data is invalid: {}", msg).as_str(), - solana_program::program_error::ProgramError::InvalidAccountData, + ProgramError::InvalidAccountData, )); } Ok(self) @@ -153,9 +135,9 @@ macro_rules! account { #[macro_export] macro_rules! error { ($struct_name:ident) => { - impl From<$struct_name> for solana_program::program_error::ProgramError { + impl From<$struct_name> for ProgramError { fn from(e: $struct_name) -> Self { - solana_program::program_error::ProgramError::Custom(e as u32) + ProgramError::Custom(e as u32) } } }; diff --git a/lib/src/pinocchio_cpi.rs b/lib/src/pinocchio_cpi.rs new file mode 100644 index 0000000..f1e27bc --- /dev/null +++ b/lib/src/pinocchio_cpi.rs @@ -0,0 +1,207 @@ +use bytemuck::Pod; +use pinocchio::{ + account_info::AccountInfo, + instruction::Instruction, + pubkey::{find_program_address, Pubkey}, + signer, + sysvars::{rent::Rent, Sysvar}, + ProgramResult, +}; + +use crate::Discriminator; + +/// Creates a new account. +#[inline(always)] +pub fn create_account( + from: &AccountInfo, + to: &AccountInfo, + space: u64, + owner: &[u8; 32], +) -> ProgramResult { + let lamports = (Rent::get()?).minimum_balance(space as usize); + + pinocchio_system::instructions::CreateAccount { + from, + to, + lamports, + space, + owner, + } + .invoke()?; + + Ok(()) +} + +/// Creates a new program account. +#[inline(always)] +pub fn create_program_account( + target_account: &AccountInfo, + payer: &AccountInfo, + owner: &[u8; 32], + seeds: &[u8; SIZE], +) -> ProgramResult { + let chunks: &[&[u8]] = &split_bytes(&seeds, SIZE); + + create_program_account_with_bump::( + target_account, + payer, + owner, + seeds, + find_program_address(chunks, owner).1, + ) +} + +/// Creates a new program account with user-provided bump. +#[inline(always)] +pub fn create_program_account_with_bump( + target_account: &AccountInfo, + payer: &AccountInfo, + owner: &[u8; 32], + seeds: &[u8; SIZE], + bump: u8, +) -> ProgramResult { + // Allocate space. + allocate_account_with_bump( + target_account, + payer, + 8 + std::mem::size_of::(), + owner, + seeds, + bump, + )?; + + // Set discriminator. + let mut data = target_account.try_borrow_mut_data()?; + data[0] = T::discriminator(); + + Ok(()) +} + +/// Allocates space for a new program account. +#[inline(always)] +pub fn allocate_account( + target_account: &AccountInfo, + payer: &AccountInfo, + space: usize, + owner: &[u8; 32], + seeds: &[u8; SIZE], +) -> ProgramResult { + let chunks: &[&[u8]] = &split_bytes(&seeds, SIZE); + + allocate_account_with_bump( + target_account, + payer, + space, + owner, + seeds, + find_program_address(chunks, owner).1, + ) +} + +/// Allocates space for a new program account with user-provided bump. +#[inline(always)] +pub fn allocate_account_with_bump( + target_account: &AccountInfo, + payer: &AccountInfo, + space: usize, + owner: &[u8; 32], + seeds: &[u8; SIZE], + bump: u8, +) -> ProgramResult { + // Combine seeds + let bump: &[u8] = &[bump]; + + // Allocate space for account + let rent = Rent::get()?; + if target_account.lamports().eq(&0) { + // If balance is zero, create account + pinocchio_system::instructions::CreateAccount { + from: payer, + to: target_account, + lamports: rent.minimum_balance(space), + space: space as u64, + owner, + } + .invoke_signed(&[signer!(seeds, bump)])?; + } else { + // Otherwise, if balance is nonzero: + + // 1) transfer sufficient lamports for rent exemption + let rent_exempt_balance = rent + .minimum_balance(space) + .saturating_sub(target_account.lamports()); + if rent_exempt_balance.gt(&0) { + pinocchio_system::instructions::Transfer { + from: payer, + to: target_account, + lamports: rent_exempt_balance, + } + .invoke()?; + } + + // 2) allocate space for the account + pinocchio_system::instructions::Allocate { + account: target_account, + space: space as u64, + } + .invoke()?; + + // 3) assign our program as the owner + pinocchio_system::instructions::Assign { + account: target_account, + owner, + } + .invoke_signed(&[signer!(seeds, bump)])?; + } + + Ok(()) +} + +/// Closes an account and returns the remaining rent lamports to the provided recipient. +#[inline(always)] +pub fn close_account(account_info: &AccountInfo, recipient: &AccountInfo) -> ProgramResult { + // Realloc data to zero. + account_info.realloc(0, true)?; + + // Return rent lamports. + *recipient + .try_borrow_mut_lamports() + .expect("Failed to borrow mut lamports") += account_info.lamports(); + *account_info + .try_borrow_mut_lamports() + .expect("Failed to borrow mut lamports") = 0; + + Ok(()) +} + +/// Invokes a CPI with provided signer seeds and program id. +#[inline(always)] +pub fn invoke_signed( + instruction: &Instruction, + account_infos: &[&AccountInfo; ACCOUNTS], + program_id: &Pubkey, + seeds: &[u8; SIZE], +) -> ProgramResult { + let chunks: &[&[u8]] = &split_bytes(&seeds, SIZE); + let bump = find_program_address(chunks, program_id).1; + + invoke_signed_with_bump(instruction, account_infos, seeds, bump) +} +fn split_bytes(data: &[u8; N], chunk_size: usize) -> Vec<&[u8]> { + data.chunks(chunk_size).collect() +} + +/// Invokes a CPI with the provided signer seeds and bump. +#[inline(always)] +pub fn invoke_signed_with_bump( + instruction: &Instruction, + account_infos: &[&AccountInfo; ACCOUNTS], + seeds: &[u8; SIZE], + bump: u8, +) -> ProgramResult { + // Combine seeds + let bump: &[u8] = &[bump]; + + // Invoke CPI + pinocchio::program::invoke_signed(instruction, account_infos, &[signer!(seeds, bump)]) +} diff --git a/lib/src/traits.rs b/lib/src/traits.rs index f5eaee3..6c25037 100644 --- a/lib/src/traits.rs +++ b/lib/src/traits.rs @@ -1,6 +1,11 @@ use bytemuck::Pod; + +#[cfg(not(feature = "pinocchio"))] use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; +#[cfg(feature = "pinocchio")] +use pinocchio::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey}; + pub trait AccountDeserialize { fn try_from_bytes(data: &[u8]) -> Result<&Self, ProgramError>; fn try_from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, ProgramError>; @@ -12,20 +17,17 @@ where { fn try_from_bytes(data: &[u8]) -> Result<&Self, ProgramError> { if Self::discriminator().ne(&data[0]) { - return Err(solana_program::program_error::ProgramError::InvalidAccountData); + return Err(ProgramError::InvalidAccountData); } - bytemuck::try_from_bytes::(&data[8..]).or(Err( - solana_program::program_error::ProgramError::InvalidAccountData, - )) + bytemuck::try_from_bytes::(&data[8..]).or(Err(ProgramError::InvalidAccountData)) } fn try_from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, ProgramError> { if Self::discriminator().ne(&data[0]) { - return Err(solana_program::program_error::ProgramError::InvalidAccountData); + return Err(ProgramError::InvalidAccountData); } - bytemuck::try_from_bytes_mut::(&mut data[8..]).or(Err( - solana_program::program_error::ProgramError::InvalidAccountData, - )) + bytemuck::try_from_bytes_mut::(&mut data[8..]) + .or(Err(ProgramError::InvalidAccountData)) } } @@ -46,13 +48,11 @@ where { fn try_header_from_bytes(data: &[u8]) -> Result<(&Self, &[u8]), ProgramError> { if Self::discriminator().ne(&data[0]) { - return Err(solana_program::program_error::ProgramError::InvalidAccountData); + return Err(ProgramError::InvalidAccountData); } let (prefix, remainder) = data[8..].split_at(std::mem::size_of::()); Ok(( - bytemuck::try_from_bytes::(prefix).or(Err( - solana_program::program_error::ProgramError::InvalidAccountData, - ))?, + bytemuck::try_from_bytes::(prefix).or(Err(ProgramError::InvalidAccountData))?, remainder, )) } @@ -60,9 +60,8 @@ where fn try_header_from_bytes_mut(data: &mut [u8]) -> Result<(&mut Self, &mut [u8]), ProgramError> { let (prefix, remainder) = data[8..].split_at_mut(std::mem::size_of::()); Ok(( - bytemuck::try_from_bytes_mut::(prefix).or(Err( - solana_program::program_error::ProgramError::InvalidAccountData, - ))?, + bytemuck::try_from_bytes_mut::(prefix) + .or(Err(ProgramError::InvalidAccountData))?, remainder, )) } @@ -140,15 +139,28 @@ pub trait AsSplToken { ) -> Result; } +#[cfg(not(feature = "pinocchio"))] pub trait LamportTransfer<'a, 'info> { fn send(&'a self, lamports: u64, to: &'a AccountInfo<'info>); fn collect(&'a self, lamports: u64, from: &'a AccountInfo<'info>) -> Result<(), ProgramError>; } +#[cfg(not(feature = "pinocchio"))] pub trait CloseAccount<'a, 'info> { fn close(&'a self, to: &'a AccountInfo<'info>) -> Result<(), ProgramError>; } +#[cfg(feature = "pinocchio")] +pub trait LamportTransfer { + fn send(&self, lamports: u64, to: AccountInfo); + fn collect(&self, lamports: u64, from: AccountInfo) -> Result<(), ProgramError>; +} + +#[cfg(feature = "pinocchio")] +pub trait CloseAccount { + fn close(&self, to: AccountInfo) -> Result<(), ProgramError>; +} + pub trait Loggable { fn log(&self); fn log_return(&self); diff --git a/lib/src/utils.rs b/lib/src/utils.rs index 98e3016..d4f60bc 100644 --- a/lib/src/utils.rs +++ b/lib/src/utils.rs @@ -1,5 +1,9 @@ +#[cfg(not(feature = "pinocchio"))] use solana_program::{program_error::ProgramError, pubkey::Pubkey}; +#[cfg(feature = "pinocchio")] +use pinocchio::{program_error::ProgramError, pubkey::Pubkey}; + /// Parses an instruction from the instruction data. pub fn parse_instruction<'a, T: std::convert::TryFrom>( api_id: &'a Pubkey, @@ -7,7 +11,7 @@ pub fn parse_instruction<'a, T: std::convert::TryFrom>( data: &'a [u8], ) -> Result<(T, &'a [u8]), ProgramError> { // Validate the program id is valid. - if program_id.ne(&api_id) { + if program_id.ne(api_id) { return Err(ProgramError::IncorrectProgramId); }