use crate::account_view::AccountView;
use crate::address::Address;
use crate::error::ProgramError;
use crate::instruction::{CpiAccount, Signer};
use crate::ProgramResult;
pub const SYSTEM_PROGRAM_ID: Address = Address::new_from_array([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]);
pub const RECENT_BLOCKHASHES_ID: Address =
crate::address!("SysvarRecentB1ockHashes11111111111111111111");
pub const RENT_SYSVAR_ID: Address = crate::address!("SysvarRent111111111111111111111111111111111");
pub const MAX_SEED_LEN: usize = 32;
const CREATE_ACCOUNT_META: (usize, usize) = (0b11, 0b11);
const CREATE_ACCOUNT_ALLOW_PREFUND_META: (usize, usize) = (0b11, 0b11);
const CREATE_ACCOUNT_ALLOW_PREFUND_UNFUNDED_META: (usize, usize) = (0b1, 0b1);
const TRANSFER_META: (usize, usize) = (0b11, 0b01);
const ASSIGN_META: (usize, usize) = (0b1, 0b1);
const ALLOCATE_META: (usize, usize) = (0b1, 0b1);
const CREATE_ACCOUNT_WITH_SEED_META: (usize, usize) = (0b011, 0b101);
const ALLOCATE_WITH_SEED_META: (usize, usize) = (0b01, 0b10);
const ASSIGN_WITH_SEED_META: (usize, usize) = (0b01, 0b10);
const TRANSFER_WITH_SEED_META: (usize, usize) = (0b101, 0b010);
const ADVANCE_NONCE_META: (usize, usize) = (0b001, 0b100);
const WITHDRAW_NONCE_META: (usize, usize) = (0b00011, 0b10000);
const INITIALIZE_NONCE_META: (usize, usize) = (0b001, 0);
const AUTHORIZE_NONCE_META: (usize, usize) = (0b01, 0b10);
const UPGRADE_NONCE_META: (usize, usize) = (0b1, 0);
pub struct CreateAccount<'a, 'b> {
pub from: &'a AccountView<'a>,
pub to: &'a AccountView<'a>,
pub lamports: u64,
pub space: u64,
pub owner: &'b Address,
}
impl CreateAccount<'_, '_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline]
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
let mut data = [0u8; 52];
data[4..12].copy_from_slice(&self.lamports.to_le_bytes());
data[12..20].copy_from_slice(&self.space.to_le_bytes());
data[20..52].copy_from_slice(self.owner.as_array());
let accounts = [CpiAccount::from(self.from), CpiAccount::from(self.to)];
invoke_system(
&data,
&accounts,
CREATE_ACCOUNT_META.0,
CREATE_ACCOUNT_META.1,
signers,
)
}
}
pub struct CreateAccountAllowPrefund<'a, 'b> {
pub to: &'a AccountView<'a>,
pub funding: Option<(&'a AccountView<'a>, u64)>,
pub space: u64,
pub owner: &'b Address,
}
impl CreateAccountAllowPrefund<'_, '_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline]
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
let (from, lamports) = match self.funding {
Some((from, lamports)) => (Some(from), lamports),
None => (None, 0),
};
let mut data = [0u8; 52];
data[0..4].copy_from_slice(&13u32.to_le_bytes());
data[4..12].copy_from_slice(&lamports.to_le_bytes());
data[12..20].copy_from_slice(&self.space.to_le_bytes());
data[20..52].copy_from_slice(self.owner.as_array());
match from {
Some(from) => {
let accounts = [CpiAccount::from(self.to), CpiAccount::from(from)];
invoke_system(
&data,
&accounts,
CREATE_ACCOUNT_ALLOW_PREFUND_META.0,
CREATE_ACCOUNT_ALLOW_PREFUND_META.1,
signers,
)
}
None => {
let accounts = [CpiAccount::from(self.to)];
invoke_system(
&data,
&accounts,
CREATE_ACCOUNT_ALLOW_PREFUND_UNFUNDED_META.0,
CREATE_ACCOUNT_ALLOW_PREFUND_UNFUNDED_META.1,
signers,
)
}
}
}
}
pub struct Transfer<'a> {
pub from: &'a AccountView<'a>,
pub to: &'a AccountView<'a>,
pub lamports: u64,
}
impl Transfer<'_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline]
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
let mut data = [0u8; 12];
data[0] = 2;
data[4..12].copy_from_slice(&self.lamports.to_le_bytes());
let accounts = [CpiAccount::from(self.from), CpiAccount::from(self.to)];
invoke_system(&data, &accounts, TRANSFER_META.0, TRANSFER_META.1, signers)
}
}
pub struct Assign<'a, 'b> {
pub account: &'a AccountView<'a>,
pub owner: &'b Address,
}
impl Assign<'_, '_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline]
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
let mut data = [0u8; 36];
data[0] = 1;
data[4..36].copy_from_slice(self.owner.as_array());
let accounts = [CpiAccount::from(self.account)];
invoke_system(&data, &accounts, ASSIGN_META.0, ASSIGN_META.1, signers)
}
}
pub struct Allocate<'a> {
pub account: &'a AccountView<'a>,
pub space: u64,
}
impl Allocate<'_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline]
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
let mut data = [0u8; 12];
data[0] = 8;
data[4..12].copy_from_slice(&self.space.to_le_bytes());
let accounts = [CpiAccount::from(self.account)];
invoke_system(&data, &accounts, ALLOCATE_META.0, ALLOCATE_META.1, signers)
}
}
pub struct CreateAccountWithSeed<'a, 'b> {
pub from: &'a AccountView<'a>,
pub to: &'a AccountView<'a>,
pub base: &'a AccountView<'a>,
pub seed: &'b [u8],
pub lamports: u64,
pub space: u64,
pub owner: &'b Address,
}
impl CreateAccountWithSeed<'_, '_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
if self.seed.len() > MAX_SEED_LEN {
return Err(ProgramError::MaxSeedLengthExceeded);
}
let mut data = [0u8; 4 + 32 + 8 + MAX_SEED_LEN + 8 + 8 + 32];
data[0] = 3;
let mut n = 4;
data[n..n + 32].copy_from_slice(self.base.address().as_array());
n += 32;
data[n..n + 8].copy_from_slice(&(self.seed.len() as u64).to_le_bytes());
n += 8;
data[n..n + self.seed.len()].copy_from_slice(self.seed);
n += self.seed.len();
data[n..n + 8].copy_from_slice(&self.lamports.to_le_bytes());
n += 8;
data[n..n + 8].copy_from_slice(&self.space.to_le_bytes());
n += 8;
data[n..n + 32].copy_from_slice(self.owner.as_array());
n += 32;
let accounts = [
CpiAccount::from(self.from),
CpiAccount::from(self.to),
CpiAccount::from(self.base),
];
invoke_system(
&data[..n],
&accounts,
CREATE_ACCOUNT_WITH_SEED_META.0,
CREATE_ACCOUNT_WITH_SEED_META.1,
signers,
)
}
}
pub struct AllocateWithSeed<'a, 'b> {
pub account: &'a AccountView<'a>,
pub base: &'a AccountView<'a>,
pub seed: &'b [u8],
pub space: u64,
pub owner: &'b Address,
}
impl AllocateWithSeed<'_, '_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
if self.seed.len() > MAX_SEED_LEN {
return Err(ProgramError::MaxSeedLengthExceeded);
}
let mut data = [0u8; 4 + 32 + 8 + MAX_SEED_LEN + 8 + 32];
data[0] = 9;
let mut n = 4;
data[n..n + 32].copy_from_slice(self.base.address().as_array());
n += 32;
data[n..n + 8].copy_from_slice(&(self.seed.len() as u64).to_le_bytes());
n += 8;
data[n..n + self.seed.len()].copy_from_slice(self.seed);
n += self.seed.len();
data[n..n + 8].copy_from_slice(&self.space.to_le_bytes());
n += 8;
data[n..n + 32].copy_from_slice(self.owner.as_array());
n += 32;
let accounts = [CpiAccount::from(self.account), CpiAccount::from(self.base)];
invoke_system(
&data[..n],
&accounts,
ALLOCATE_WITH_SEED_META.0,
ALLOCATE_WITH_SEED_META.1,
signers,
)
}
}
pub struct AssignWithSeed<'a, 'b> {
pub account: &'a AccountView<'a>,
pub base: &'a AccountView<'a>,
pub seed: &'b [u8],
pub owner: &'b Address,
}
impl AssignWithSeed<'_, '_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
if self.seed.len() > MAX_SEED_LEN {
return Err(ProgramError::MaxSeedLengthExceeded);
}
let mut data = [0u8; 4 + 32 + 8 + MAX_SEED_LEN + 32];
data[0] = 10;
let mut n = 4;
data[n..n + 32].copy_from_slice(self.base.address().as_array());
n += 32;
data[n..n + 8].copy_from_slice(&(self.seed.len() as u64).to_le_bytes());
n += 8;
data[n..n + self.seed.len()].copy_from_slice(self.seed);
n += self.seed.len();
data[n..n + 32].copy_from_slice(self.owner.as_array());
n += 32;
let accounts = [CpiAccount::from(self.account), CpiAccount::from(self.base)];
invoke_system(
&data[..n],
&accounts,
ASSIGN_WITH_SEED_META.0,
ASSIGN_WITH_SEED_META.1,
signers,
)
}
}
pub struct TransferWithSeed<'a, 'b> {
pub from: &'a AccountView<'a>,
pub base: &'a AccountView<'a>,
pub to: &'a AccountView<'a>,
pub lamports: u64,
pub from_seed: &'b [u8],
pub from_owner: &'b Address,
}
impl TransferWithSeed<'_, '_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
if self.from_seed.len() > MAX_SEED_LEN {
return Err(ProgramError::MaxSeedLengthExceeded);
}
let mut data = [0u8; 4 + 8 + 8 + MAX_SEED_LEN + 32];
data[0] = 11;
let mut n = 4;
data[n..n + 8].copy_from_slice(&self.lamports.to_le_bytes());
n += 8;
data[n..n + 8].copy_from_slice(&(self.from_seed.len() as u64).to_le_bytes());
n += 8;
data[n..n + self.from_seed.len()].copy_from_slice(self.from_seed);
n += self.from_seed.len();
data[n..n + 32].copy_from_slice(self.from_owner.as_array());
n += 32;
let accounts = [
CpiAccount::from(self.from),
CpiAccount::from(self.base),
CpiAccount::from(self.to),
];
invoke_system(
&data[..n],
&accounts,
TRANSFER_WITH_SEED_META.0,
TRANSFER_WITH_SEED_META.1,
signers,
)
}
}
pub struct AdvanceNonceAccount<'a> {
pub nonce: &'a AccountView<'a>,
pub recent_blockhashes: &'a AccountView<'a>,
pub authority: &'a AccountView<'a>,
}
impl AdvanceNonceAccount<'_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline]
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
let data = [4u8, 0, 0, 0];
let accounts = [
CpiAccount::from(self.nonce),
CpiAccount::from(self.recent_blockhashes),
CpiAccount::from(self.authority),
];
invoke_system(
&data,
&accounts,
ADVANCE_NONCE_META.0,
ADVANCE_NONCE_META.1,
signers,
)
}
}
pub struct WithdrawNonceAccount<'a> {
pub nonce: &'a AccountView<'a>,
pub to: &'a AccountView<'a>,
pub recent_blockhashes: &'a AccountView<'a>,
pub rent: &'a AccountView<'a>,
pub authority: &'a AccountView<'a>,
pub lamports: u64,
}
impl WithdrawNonceAccount<'_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline]
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
let mut data = [0u8; 12];
data[0] = 5;
data[4..12].copy_from_slice(&self.lamports.to_le_bytes());
let accounts = [
CpiAccount::from(self.nonce),
CpiAccount::from(self.to),
CpiAccount::from(self.recent_blockhashes),
CpiAccount::from(self.rent),
CpiAccount::from(self.authority),
];
invoke_system(
&data,
&accounts,
WITHDRAW_NONCE_META.0,
WITHDRAW_NONCE_META.1,
signers,
)
}
}
pub struct InitializeNonceAccount<'a, 'b> {
pub nonce: &'a AccountView<'a>,
pub recent_blockhashes: &'a AccountView<'a>,
pub rent: &'a AccountView<'a>,
pub authority: &'b Address,
}
impl InitializeNonceAccount<'_, '_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline]
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
let mut data = [0u8; 36];
data[0] = 6;
data[4..36].copy_from_slice(self.authority.as_array());
let accounts = [
CpiAccount::from(self.nonce),
CpiAccount::from(self.recent_blockhashes),
CpiAccount::from(self.rent),
];
invoke_system(
&data,
&accounts,
INITIALIZE_NONCE_META.0,
INITIALIZE_NONCE_META.1,
signers,
)
}
}
pub struct AuthorizeNonceAccount<'a, 'b> {
pub nonce: &'a AccountView<'a>,
pub authority: &'a AccountView<'a>,
pub new_authority: &'b Address,
}
impl AuthorizeNonceAccount<'_, '_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
self.invoke_signed(&[])
}
#[inline]
pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
let mut data = [0u8; 36];
data[0] = 7;
data[4..36].copy_from_slice(self.new_authority.as_array());
let accounts = [
CpiAccount::from(self.nonce),
CpiAccount::from(self.authority),
];
invoke_system(
&data,
&accounts,
AUTHORIZE_NONCE_META.0,
AUTHORIZE_NONCE_META.1,
signers,
)
}
}
pub struct UpgradeNonceAccount<'a> {
pub nonce: &'a AccountView<'a>,
}
impl UpgradeNonceAccount<'_> {
#[inline]
pub fn invoke(&self) -> ProgramResult {
let data = [12u8, 0, 0, 0];
let accounts = [CpiAccount::from(self.nonce)];
invoke_system(
&data,
&accounts,
UPGRADE_NONCE_META.0,
UPGRADE_NONCE_META.1,
&[],
)
}
}
#[inline]
fn invoke_system<'a, const ACCOUNTS: usize>(
data: &[u8],
accounts: &[CpiAccount<'a>; ACCOUNTS],
writable_mask: usize,
signer_mask: usize,
signers: &[Signer<'_, '_>],
) -> ProgramResult {
crate::cpi::invoke_specialized_signed(
&SYSTEM_PROGRAM_ID,
data,
accounts,
writable_mask,
signer_mask,
signers,
)
}
pub mod instructions {
pub use super::{
AdvanceNonceAccount, Allocate, AllocateWithSeed, Assign, AssignWithSeed,
AuthorizeNonceAccount, CreateAccount, CreateAccountAllowPrefund, CreateAccountWithSeed,
InitializeNonceAccount, Transfer, TransferWithSeed, UpgradeNonceAccount,
WithdrawNonceAccount,
};
}
pub const NONCE_ACCOUNT_LEN: usize = 80;
pub const NONCE_VERSION_CURRENT: u32 = 1;
pub const NONCE_STATE_INITIALIZED: u32 = 1;
#[derive(Clone, Copy, Debug)]
pub struct NonceState<'a> {
data: &'a [u8],
}
impl<'a> NonceState<'a> {
#[inline]
pub fn from_account_data(data: &'a [u8]) -> Result<Self, ProgramError> {
if data.len() < NONCE_ACCOUNT_LEN {
return Err(ProgramError::AccountDataTooSmall);
}
let version = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
let state = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
if version != NONCE_VERSION_CURRENT || state != NONCE_STATE_INITIALIZED {
return Err(ProgramError::InvalidAccountData);
}
Ok(Self { data })
}
#[inline]
pub fn authority(&self) -> &'a Address {
unsafe { &*(self.data.as_ptr().add(8) as *const Address) }
}
#[inline]
pub fn durable_nonce(&self) -> &'a [u8; 32] {
unsafe { &*(self.data.as_ptr().add(40) as *const [u8; 32]) }
}
#[inline]
pub fn lamports_per_signature(&self) -> u64 {
let b = &self.data[72..80];
u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])
}
}