Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
301 changes: 267 additions & 34 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 12 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,18 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.137"
solana-clap-v3-utils = "^2.1"
solana-cli-config = "^2.1"
solana-program = "^2.1"
solana-address = { version = "2.0", features = ["decode", "curve25519"] }
solana-account-view = "1.0"
solana-program-error = "3.0"
solana-instruction-view = { version = "1.0.0", features = ["cpi"] }
pinocchio = { version = "0.10.1", features = ["cpi"] }
pinocchio-log = "0.5.1"
pinocchio-system = "0.5.0"
pinocchio-pubkey = "0.3.0"
pinocchio-associated-token-account = "0.3.0"
pinocchio-token-2022 = "0.2.0"
pinocchio-token = "0.5.0"
# solana-program = "^2.1"
solana-sdk = "^2.1"
spl-token = { features = ["no-entrypoint"], version = "^4" }
spl-token-2022 = { features = ["no-entrypoint"], version = "^7" }
Expand All @@ -38,4 +49,3 @@ thiserror = "1.0.57"
tokio = "1.35"
toml = "0.8.19"
quote = "1.0"

13 changes: 12 additions & 1 deletion lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,20 @@ bytemuck.workspace = true
fixed.workspace = true
num_enum.workspace = true
serde.workspace = true
solana-program.workspace = true
# solana-program.workspace = true
solana-address.workspace = true
solana-account-view.workspace = true
solana-program-error.workspace = true
solana-instruction-view.workspace = true
spl-token = { workspace = true, optional = true }
spl-token-2022 = { workspace = true, optional = true }
spl-associated-token-account = { workspace = true, optional = true }
thiserror.workspace = true

pinocchio.workspace = true
pinocchio-log.workspace = true
pinocchio-system.workspace = true
pinocchio-pubkey.workspace = true
pinocchio-associated-token-account.workspace = true
pinocchio-token.workspace = true
pinocchio-token-2022.workspace = true
23 changes: 14 additions & 9 deletions lib/src/account/close.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
use solana_program::{account_info::AccountInfo, program_error::ProgramError, system_program};
use solana_account_view::AccountView;
use solana_address::address;
use solana_program_error::ProgramError;

pub trait CloseAccount<'info> {
fn close(&self, to: &AccountInfo<'info>) -> Result<(), ProgramError>;
pub trait CloseAccount {
fn close(&self, to: &AccountView) -> Result<(), ProgramError>;
}

impl<'info> CloseAccount<'info> for AccountInfo<'info> {
fn close(&self, to: &AccountInfo<'info>) -> Result<(), ProgramError> {
impl CloseAccount for AccountView {
fn close(&self, to: &AccountView) -> Result<(), ProgramError> {
// Return rent lamports.
**to.lamports.borrow_mut() += self.lamports();
**self.lamports.borrow_mut() = 0;
to.set_lamports(to.lamports() + self.lamports());
self.set_lamports(0);

// Assign system program as the owner
self.assign(&system_program::ID);
unsafe {
// self.assign(&system_program::ID);
self.assign(&address!("11111111111111111111111111111111"));
}

// Realloc data to zero.
self.realloc(0, true)?;
self.resize(0)?;

Ok(())
}
Expand Down
236 changes: 91 additions & 145 deletions lib/src/account/cpi.rs
Original file line number Diff line number Diff line change
@@ -1,221 +1,167 @@
use bytemuck::Pod;
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction, pubkey::Pubkey,
rent::Rent, sysvar::Sysvar,

use pinocchio::cpi;
use pinocchio::sysvars::rent::Rent;
use pinocchio::sysvars::Sysvar;
use pinocchio::{
cpi::{Seed, Signer},
instruction::InstructionView,
AccountView, Address, ProgramResult,
};

use crate::{CloseAccount, Discriminator};
// use crate::{CloseAccount};
use crate::Discriminator;

/// Invokes a CPI with provided signer seeds and program id.
#[inline(always)]
pub fn invoke_signed<'info>(
instruction: &Instruction,
account_infos: &[AccountInfo<'info>],
program_id: &Pubkey,
seeds: &[&[u8]],
pub fn invoke_signed<const N: usize, const ACCOUNTS: usize>(
instruction: &InstructionView,
account_views: &[&AccountView; ACCOUNTS],
seeds: &[Seed; N],
) -> ProgramResult {
let bump = Pubkey::find_program_address(seeds, program_id).1;
invoke_signed_with_bump(instruction, account_infos, seeds, bump)
}
let signers_seeds = Signer::from(seeds);

/// Invokes a CPI with the provided signer seeds and bump.
#[inline(always)]
pub fn invoke_signed_with_bump<'info>(
instruction: &Instruction,
account_infos: &[AccountInfo<'info>],
seeds: &[&[u8]],
bump: u8,
) -> ProgramResult {
// Combine seeds
let bump: &[u8] = &[bump];
let mut combined_seeds = Vec::with_capacity(seeds.len() + 1);
combined_seeds.extend_from_slice(seeds);
combined_seeds.push(bump);
let seeds = combined_seeds.as_slice();

// Invoke CPI
solana_program::program::invoke_signed(instruction, account_infos, &[seeds])
cpi::invoke_signed(instruction, account_views, &[signers_seeds])
}

/// Creates a new account.
#[inline(always)]
pub fn create_account<'a, 'info>(
from_pubkey: &'a AccountInfo<'info>,
to_pubkey: &'a AccountInfo<'info>,
system_program: &'a AccountInfo<'info>,
from: &'a AccountView,
to: &'a AccountView,
space: usize,
owner: &Pubkey,
owner: &Address,
) -> ProgramResult {
let lamports_required = (Rent::get()?).minimum_balance(space);

solana_program::program::invoke(
&solana_program::system_instruction::create_account(
from_pubkey.key,
to_pubkey.key,
lamports_required,
space as u64,
owner,
),
&[
from_pubkey.clone(),
to_pubkey.clone(),
system_program.clone(),
],
)?;
let rent = Rent::get()?;

let lamports_required = rent.try_minimum_balance(space)?;

pinocchio_system::instructions::CreateAccount {
from,
to,
lamports: lamports_required,
space: space as u64,
owner,
}
.invoke()?;

Ok(())
}

/// Creates a new program account.
#[inline(always)]
pub fn create_program_account<'a, 'info, T: Discriminator + Pod>(
target_account: &'a AccountInfo<'info>,
system_program: &'a AccountInfo<'info>,
payer: &'a AccountInfo<'info>,
owner: &Pubkey,
seeds: &[&[u8]],
pub fn create_program_account<'a, 'info, T: Discriminator + Pod, const N: usize>(
target_account: &'a AccountView,
payer: &'a AccountView,
owner: &Address,
seeds: &[Seed; N],
) -> ProgramResult {
create_program_account_with_bump::<T>(
target_account,
system_program,
payer,
owner,
seeds,
Pubkey::find_program_address(seeds, owner).1,
)
create_program_account_with_bump::<T, N>(target_account, payer, owner, seeds)
}

/// Creates a new program account with user-provided bump.
#[inline(always)]
pub fn create_program_account_with_bump<'a, 'info, T: Discriminator + Pod>(
target_account: &'a AccountInfo<'info>,
system_program: &'a AccountInfo<'info>,
payer: &'a AccountInfo<'info>,
owner: &Pubkey,
seeds: &[&[u8]],
bump: u8,
pub fn create_program_account_with_bump<'a, 'info, T: Discriminator + Pod, const N: usize>(
target_account: &'a AccountView,
payer: &'a AccountView,
owner: &Address,
seeds: &[Seed; N],
) -> ProgramResult {
// Allocate space.
allocate_account_with_bump(
target_account,
system_program,
payer,
8 + std::mem::size_of::<T>(),
owner,
seeds,
bump,
)?;

// Set discriminator.
let mut data = target_account.data.borrow_mut();
data[0] = T::discriminator();
let mut data = target_account.try_borrow_mut()?;
data.copy_from_slice(&T::discriminator().to_le_bytes());

Ok(())
}

/// Allocates space for a new program account.
#[inline(always)]
pub fn allocate_account<'a, 'info>(
target_account: &'a AccountInfo<'info>,
system_program: &'a AccountInfo<'info>,
payer: &'a AccountInfo<'info>,
pub fn allocate_account<'a, 'info, const N: usize>(
target_account: &'a AccountView,
payer: &'a AccountView,
space: usize,
owner: &Pubkey,
seeds: &[&[u8]],
owner: &Address,
seeds: &[Seed; N],
) -> ProgramResult {
allocate_account_with_bump(
target_account,
system_program,
payer,
space,
owner,
seeds,
Pubkey::find_program_address(seeds, owner).1,
)
allocate_account_with_bump(target_account, payer, space, owner, seeds)
}

/// Allocates space for a new program account with user-provided bump.
#[inline(always)]
pub fn allocate_account_with_bump<'a, 'info>(
target_account: &'a AccountInfo<'info>,
system_program: &'a AccountInfo<'info>,
payer: &'a AccountInfo<'info>,
pub fn allocate_account_with_bump<'a, 'info, const N: usize>(
target_account: &'a AccountView,
payer: &'a AccountView,
space: usize,
owner: &Pubkey,
seeds: &[&[u8]],
bump: u8,
owner: &Address,
seeds: &[Seed; N],
) -> ProgramResult {
// Combine seeds
let bump: &[u8] = &[bump];
let mut combined_seeds = Vec::with_capacity(seeds.len() + 1);
combined_seeds.extend_from_slice(seeds);
combined_seeds.push(bump);
let seeds = combined_seeds.as_slice();
let signer_seeds = Signer::from(seeds);

// Allocate space for account
let rent = Rent::get()?;

if target_account.lamports().eq(&0) {
// If balance is zero, create account
solana_program::program::invoke_signed(
&solana_program::system_instruction::create_account(
payer.key,
target_account.key,
rent.minimum_balance(space),
space as u64,
owner,
),
&[
payer.clone(),
target_account.clone(),
system_program.clone(),
],
&[seeds],
)?;

pinocchio_system::instructions::CreateAccount {
from: payer,
to: target_account,
lamports: rent.try_minimum_balance(space)?,
space: space as u64,
owner,
}
.invoke_signed(&[signer_seeds.clone()])?;
} else {
// Otherwise, if balance is nonzero:

// 1) transfer sufficient lamports for rent exemption
let rent_exempt_balance = rent
.minimum_balance(space)
.try_minimum_balance(space)?
.saturating_sub(target_account.lamports());
if rent_exempt_balance.gt(&0) {
solana_program::program::invoke(
&solana_program::system_instruction::transfer(
payer.key,
target_account.key,
rent_exempt_balance,
),
&[
payer.clone(),
target_account.clone(),
system_program.clone(),
],
)?;
pinocchio_system::instructions::Transfer {
from: payer,
to: target_account,
lamports: rent_exempt_balance,
}
.invoke()?;
}

// 2) allocate space for the account
solana_program::program::invoke_signed(
&solana_program::system_instruction::allocate(target_account.key, space as u64),
&[target_account.clone(), system_program.clone()],
&[seeds],
)?;

pinocchio_system::instructions::Allocate {
account: target_account,
space: space as u64,
}
.invoke_signed(&[signer_seeds.clone()])?;

// 3) assign our program as the owner
solana_program::program::invoke_signed(
&solana_program::system_instruction::assign(target_account.key, owner),
&[target_account.clone(), system_program.clone()],
&[seeds],
)?;
pinocchio_system::instructions::Assign {
account: target_account,
owner,
}
.invoke_signed(&[signer_seeds])?;
}

Ok(())
}

/// Closes an account and returns the remaining rent lamports to the provided recipient.
#[inline(always)]
pub fn close_account<'info>(
account_info: &AccountInfo<'info>,
recipient: &AccountInfo<'info>,
) -> ProgramResult {
account_info.close(recipient)
pub fn close_account(account_info: &AccountView, recipient: &AccountView) -> ProgramResult {
let lamports = account_info.lamports();

account_info.set_lamports(lamports - lamports);
recipient.set_lamports(recipient.lamports() + lamports);

account_info.close()
}
Loading