Skip to content

Commit 34ce5b2

Browse files
committed
use Sysvar rent account
1 parent d84197b commit 34ce5b2

File tree

4 files changed

+232
-160
lines changed

4 files changed

+232
-160
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use pinocchio::{
2+
account_info::AccountInfo,
3+
instruction::{AccountMeta, Instruction, Signer},
4+
program::invoke_signed,
5+
pubkey::Pubkey,
6+
sysvars::{rent::Rent, Sysvar},
7+
ProgramResult,
8+
program_error::ProgramError,
9+
};
10+
11+
/// Create a new account.
12+
///
13+
/// ### Accounts:
14+
/// 0. `[WRITE, SIGNER]` Funding account
15+
/// 1. `[WRITE, SIGNER]` New account
16+
pub struct CreateAccountChecked<'a> {
17+
/// Funding account.
18+
pub from: &'a AccountInfo,
19+
20+
/// New account.
21+
pub to: &'a AccountInfo,
22+
23+
/// Number of bytes of memory to allocate.
24+
pub space: u64,
25+
26+
/// Address of program that will own the new account.
27+
pub owner: &'a Pubkey,
28+
29+
/// Sysvar rent account.
30+
pub sysvar_rent_account: &'a AccountInfo,
31+
}
32+
33+
impl CreateAccountChecked<'_> {
34+
#[inline(always)]
35+
pub fn invoke(&self) -> ProgramResult {
36+
self.invoke_signed(&[])
37+
}
38+
39+
#[inline(always)]
40+
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
41+
// getting lamports from rent
42+
let rent = Rent::from_account_info(self.sysvar_rent_account)?;
43+
let lamports = rent.minimum_balance(self.space as usize);
44+
45+
// checking if the funding account has enough lamports
46+
if self.from.lamports() < lamports {
47+
return Err(ProgramError::InsufficientFunds);
48+
}
49+
50+
// checking if the new account is already initialized
51+
if !self.to.data_is_empty() {
52+
return Err(ProgramError::InvalidAccountData);
53+
}
54+
55+
// account metadata
56+
let account_metas: [AccountMeta; 2] = [
57+
AccountMeta::writable_signer(self.from.key()),
58+
AccountMeta::writable_signer(self.to.key()),
59+
];
60+
61+
// instruction data
62+
// - [0..4 ]: instruction discriminator
63+
// - [4..12 ]: lamports
64+
// - [12..20]: account space
65+
// - [20..52]: owner pubkey
66+
let mut instruction_data = [0; 52];
67+
// create account instruction has a '0' discriminator
68+
instruction_data[4..12].copy_from_slice(&lamports.to_le_bytes());
69+
instruction_data[12..20].copy_from_slice(&self.space.to_le_bytes());
70+
instruction_data[20..52].copy_from_slice(self.owner.as_ref());
71+
72+
let instruction = Instruction {
73+
program_id: &crate::ID,
74+
accounts: &account_metas,
75+
data: &instruction_data,
76+
};
77+
78+
invoke_signed(&instruction, &[self.from, self.to], signers)
79+
}
80+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use pinocchio::{
2+
account_info::AccountInfo,
3+
instruction::{AccountMeta, Instruction, Signer},
4+
program::invoke_signed,
5+
pubkey::{self, Pubkey, MAX_SEED_LEN},
6+
sysvars::{rent::Rent, Sysvar},
7+
ProgramResult,
8+
program_error::ProgramError,
9+
};
10+
11+
/// Create a new account at an address derived from a base pubkey and a seed.
12+
///
13+
/// ### Accounts:
14+
/// 0. `[WRITE, SIGNER]` Funding account
15+
/// 1. `[WRITE]` Created account
16+
/// 2. `[SIGNER]` (optional) Base account; the account matching the base Pubkey below must be
17+
/// provided as a signer, but may be the same as the funding account
18+
pub struct CreateAccountWithSeedChecked<'a, 'b, 'c> {
19+
/// Funding account.
20+
pub from: &'a AccountInfo,
21+
22+
/// New account.
23+
pub to: &'a AccountInfo,
24+
25+
/// Base account.
26+
///
27+
/// The account matching the base Pubkey below must be provided as
28+
/// a signer, but may be the same as the funding account and provided
29+
/// as account 0.
30+
pub base: Option<&'a AccountInfo>,
31+
32+
/// String of ASCII chars, no longer than `Pubkey::MAX_SEED_LEN`.
33+
pub seed: &'b str,
34+
35+
/// Number of bytes of memory to allocate.
36+
pub space: u64,
37+
38+
/// Address of program that will own the new account.
39+
pub owner: &'c Pubkey,
40+
41+
/// Sysvar rent account.
42+
pub sysvar_rent_account: &'a AccountInfo,
43+
}
44+
45+
impl CreateAccountWithSeed<'_, '_, '_> {
46+
#[inline(always)]
47+
pub fn invoke(&self) -> ProgramResult {
48+
self.invoke_signed(&[])
49+
}
50+
51+
#[inline(always)]
52+
pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult {
53+
// checking if the seed is valid
54+
if self.seed.len() > MAX_SEED_LEN {
55+
return Err(ProgramError::InvalidInstructionData);
56+
}
57+
58+
// getting lamports from rent
59+
let rent = Rent::from_account_info(self.sysvar_rent_account)?;
60+
let lamports = rent.minimum_balance(self.space as usize);
61+
62+
// checking if the funding account has enough lamports
63+
if self.from.lamports() < lamports {
64+
return Err(ProgramError::InsufficientFunds);
65+
}
66+
67+
// checking if the new account is already initialized
68+
if !self.to.data_is_empty() {
69+
return Err(ProgramError::InvalidAccountData);
70+
}
71+
72+
// account metadata
73+
let account_metas: [AccountMeta; 3] = [
74+
AccountMeta::writable_signer(self.from.key()),
75+
AccountMeta::writable(self.to.key()),
76+
AccountMeta::readonly_signer(self.base.unwrap_or(self.from).key()),
77+
];
78+
79+
// instruction data
80+
// - [0..4 ]: instruction discriminator
81+
// - [4..36 ]: base pubkey
82+
// - [36..44]: seed length
83+
// - [44.. ]: seed (max 32)
84+
// - [.. +8]: lamports
85+
// - [.. +8]: account space
86+
// - [.. +32]: owner pubkey
87+
let mut instruction_data = [0; 120];
88+
instruction_data[0] = 3;
89+
instruction_data[4..36].copy_from_slice(self.base.unwrap_or(self.from).key());
90+
instruction_data[36..44].copy_from_slice(&u64::to_le_bytes(self.seed.len() as u64));
91+
92+
let offset = 44 + self.seed.len();
93+
instruction_data[44..offset].copy_from_slice(self.seed.as_bytes());
94+
instruction_data[offset..offset + 8].copy_from_slice(&lamports.to_le_bytes());
95+
instruction_data[offset + 8..offset + 16].copy_from_slice(&self.space.to_le_bytes());
96+
instruction_data[offset + 16..offset + 48].copy_from_slice(self.owner.as_ref());
97+
98+
let instruction = Instruction {
99+
program_id: &crate::ID,
100+
accounts: &account_metas,
101+
data: &instruction_data[..offset + 48],
102+
};
103+
104+
invoke_signed(
105+
&instruction,
106+
&[self.from, self.to, self.base.unwrap_or(self.from)],
107+
signers,
108+
)
109+
}
110+
}

programs/system/src/instructions/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ mod assign;
55
mod assign_with_seed;
66
mod authorize_nonce_account;
77
mod create_account;
8+
mod create_account_checked;
89
mod create_account_with_seed;
10+
mod create_account_with_seed_checked;
911
mod initialize_nonce_account;
1012
mod transfer;
1113
mod transfer_with_seed;
@@ -19,7 +21,9 @@ pub use assign::*;
1921
pub use assign_with_seed::*;
2022
pub use authorize_nonce_account::*;
2123
pub use create_account::*;
24+
pub use create_account_checked::*;
2225
pub use create_account_with_seed::*;
26+
pub use create_account_with_seed_checked::*;
2327
pub use initialize_nonce_account::*;
2428
pub use transfer::*;
2529
pub use transfer_with_seed::*;

0 commit comments

Comments
 (0)