1pub mod pda;
7
8use pinocchio::{error::ProgramError, AccountView, Address, ProgramResult};
9
10#[cfg(feature = "programs")]
11use crate::programs;
12
13const SYSTEM_PROGRAM_ID: Address = Address::new_from_array([0u8; 32]);
17
18#[inline(always)]
20pub fn check_signer(account: &AccountView) -> ProgramResult {
21 if !account.is_signer() {
22 return Err(ProgramError::MissingRequiredSignature);
23 }
24 Ok(())
25}
26
27#[inline(always)]
29pub fn check_writable(account: &AccountView) -> ProgramResult {
30 if !account.is_writable() {
31 return Err(ProgramError::InvalidArgument);
32 }
33 Ok(())
34}
35
36#[inline(always)]
38pub fn check_owner(account: &AccountView, program_id: &Address) -> ProgramResult {
39 if !account.owned_by(program_id) {
40 return Err(ProgramError::IncorrectProgramId);
41 }
42 Ok(())
43}
44
45#[inline(always)]
47pub fn check_pda(account: &AccountView, expected: &Address) -> ProgramResult {
48 if *account.address() != *expected {
49 return Err(ProgramError::InvalidSeeds);
50 }
51 Ok(())
52}
53
54#[inline(always)]
56pub fn check_system_program(account: &AccountView) -> ProgramResult {
57 if *account.address() != SYSTEM_PROGRAM_ID {
58 return Err(ProgramError::IncorrectProgramId);
59 }
60 Ok(())
61}
62
63#[inline(always)]
65pub fn check_uninitialized(account: &AccountView) -> ProgramResult {
66 if !account.is_data_empty() {
67 return Err(ProgramError::AccountAlreadyInitialized);
68 }
69 Ok(())
70}
71
72#[inline(always)]
74pub fn check_executable(account: &AccountView) -> ProgramResult {
75 if !account.executable() {
76 return Err(ProgramError::IncorrectProgramId);
77 }
78 Ok(())
79}
80
81#[inline(always)]
85pub fn check_size(data: &[u8], min_len: usize) -> ProgramResult {
86 if data.len() < min_len {
87 return Err(ProgramError::AccountDataTooSmall);
88 }
89 Ok(())
90}
91
92#[inline(always)]
94pub fn check_discriminator(data: &[u8], expected: u8) -> ProgramResult {
95 if data.is_empty() || data[0] != expected {
96 return Err(ProgramError::InvalidAccountData);
97 }
98 Ok(())
99}
100
101#[inline(always)]
103pub fn check_account(
104 account: &AccountView,
105 program_id: &Address,
106 discriminator: u8,
107 min_len: usize,
108) -> ProgramResult {
109 check_owner(account, program_id)?;
110 let data = account.try_borrow()?;
111 check_size(&data, min_len)?;
112 check_discriminator(&data, discriminator)?;
113 Ok(())
114}
115
116#[inline(always)]
118pub fn check_version(data: &[u8], min_version: u8) -> ProgramResult {
119 if data.len() < 2 {
120 return Err(ProgramError::AccountDataTooSmall);
121 }
122 if data[1] < min_version {
123 return Err(ProgramError::InvalidAccountData);
124 }
125 Ok(())
126}
127
128#[inline(always)]
132pub fn check_keys_eq(a: &Address, b: &Address) -> ProgramResult {
133 if *a != *b {
134 return Err(ProgramError::InvalidArgument);
135 }
136 Ok(())
137}
138
139#[inline(always)]
143pub fn check_has_one(stored: &Address, account: &AccountView) -> ProgramResult {
144 if stored != account.address() {
145 return Err(ProgramError::InvalidArgument);
146 }
147 Ok(())
148}
149
150#[inline(always)]
156pub fn rent_exempt_min(data_len: usize) -> u64 {
157 (128u64 + data_len as u64).saturating_mul(6960)
158}
159
160#[inline(always)]
162pub fn check_rent_exempt(account: &AccountView) -> ProgramResult {
163 let data = account.try_borrow()?;
164 let min = rent_exempt_min(data.len());
165 drop(data);
166 if account.lamports() < min {
167 return Err(ProgramError::InsufficientFunds);
168 }
169 Ok(())
170}
171
172#[inline(always)]
174pub fn check_lamports_gte(account: &AccountView, min_lamports: u64) -> ProgramResult {
175 if account.lamports() < min_lamports {
176 return Err(ProgramError::InsufficientFunds);
177 }
178 Ok(())
179}
180
181#[inline(always)]
183pub fn check_closed(account: &AccountView) -> ProgramResult {
184 if account.lamports() != 0 || !account.is_data_empty() {
185 return Err(ProgramError::InvalidAccountData);
186 }
187 Ok(())
188}
189
190#[inline(always)]
194pub fn check_instruction_data_len(data: &[u8], expected_len: usize) -> ProgramResult {
195 if data.len() != expected_len {
196 return Err(ProgramError::InvalidInstructionData);
197 }
198 Ok(())
199}
200
201#[inline(always)]
203pub fn check_instruction_data_min(data: &[u8], min_len: usize) -> ProgramResult {
204 if data.len() < min_len {
205 return Err(ProgramError::InvalidInstructionData);
206 }
207 Ok(())
208}
209
210#[inline(always)]
214pub fn check_accounts_unique_2(a: &AccountView, b: &AccountView) -> ProgramResult {
215 if a.address() == b.address() {
216 return Err(ProgramError::InvalidArgument);
217 }
218 Ok(())
219}
220
221#[inline(always)]
223pub fn check_accounts_unique_3(
224 a: &AccountView,
225 b: &AccountView,
226 c: &AccountView,
227) -> ProgramResult {
228 if a.address() == b.address()
229 || a.address() == c.address()
230 || b.address() == c.address()
231 {
232 return Err(ProgramError::InvalidArgument);
233 }
234 Ok(())
235}
236
237#[inline(always)]
239pub fn check_accounts_unique_4(
240 a: &AccountView,
241 b: &AccountView,
242 c: &AccountView,
243 d: &AccountView,
244) -> ProgramResult {
245 if a.address() == b.address()
246 || a.address() == c.address()
247 || a.address() == d.address()
248 || b.address() == c.address()
249 || b.address() == d.address()
250 || c.address() == d.address()
251 {
252 return Err(ProgramError::InvalidArgument);
253 }
254 Ok(())
255}
256
257#[inline(always)]
263pub fn assert_pda(
264 account: &AccountView,
265 seeds: &[&[u8]],
266 program_id: &Address,
267) -> Result<u8, ProgramError> {
268 #[cfg(target_os = "solana")]
269 {
270 let (derived, bump) = Address::find_program_address(seeds, program_id);
271 if derived != *account.address() {
272 return Err(ProgramError::InvalidSeeds);
273 }
274 Ok(bump)
275 }
276 #[cfg(not(target_os = "solana"))]
277 {
278 let _ = (account, seeds, program_id);
279 Err(ProgramError::InvalidSeeds)
280 }
281}
282
283#[inline(always)]
285pub fn assert_pda_with_bump(
286 account: &AccountView,
287 seeds: &[&[u8]],
288 bump: u8,
289 program_id: &Address,
290) -> ProgramResult {
291 #[cfg(target_os = "solana")]
292 {
293 let bump_bytes = [bump];
294 let n = seeds.len();
295 let mut all_seeds: [&[u8]; 17] = [&[]; 17];
296 let mut i = 0;
297 while i < n {
298 all_seeds[i] = seeds[i];
299 i += 1;
300 }
301 all_seeds[n] = &bump_bytes;
302
303 let derived = Address::create_program_address(&all_seeds[..n + 1], program_id)
304 .map_err(|_| ProgramError::InvalidSeeds)?;
305 if derived != *account.address() {
306 return Err(ProgramError::InvalidSeeds);
307 }
308 Ok(())
309 }
310 #[cfg(not(target_os = "solana"))]
311 {
312 let _ = (account, seeds, bump, program_id);
313 Err(ProgramError::InvalidSeeds)
314 }
315}
316
317#[inline(always)]
319pub fn assert_pda_external(
320 account: &AccountView,
321 seeds: &[&[u8]],
322 program_id: &Address,
323) -> Result<u8, ProgramError> {
324 assert_pda(account, seeds, program_id)
325}
326
327#[cfg(feature = "programs")]
329#[inline(always)]
330pub fn assert_token_program(account: &AccountView) -> ProgramResult {
331 if *account.address() != programs::TOKEN && *account.address() != programs::TOKEN_2022 {
332 return Err(ProgramError::IncorrectProgramId);
333 }
334 Ok(())
335}
336
337#[inline(always)]
339pub fn assert_address(account: &AccountView, expected: &Address) -> ProgramResult {
340 if *account.address() != *expected {
341 return Err(ProgramError::InvalidArgument);
342 }
343 Ok(())
344}
345
346#[inline(always)]
348pub fn assert_program(account: &AccountView, expected_program: &Address) -> ProgramResult {
349 if *account.address() != *expected_program {
350 return Err(ProgramError::IncorrectProgramId);
351 }
352 if !account.executable() {
353 return Err(ProgramError::IncorrectProgramId);
354 }
355 Ok(())
356}
357
358#[inline(always)]
360pub fn assert_not_initialized(account: &AccountView) -> ProgramResult {
361 if account.lamports() != 0 {
362 return Err(ProgramError::AccountAlreadyInitialized);
363 }
364 Ok(())
365}
366
367#[inline(always)]
379pub fn check_program_allowed(
380 account: &AccountView,
381 allowed: &[Address],
382) -> ProgramResult {
383 let owner = unsafe { account.owner() };
384 let mut i = 0;
385 while i < allowed.len() {
386 if *owner == allowed[i] {
387 return Ok(());
388 }
389 i += 1;
390 }
391 Err(ProgramError::IncorrectProgramId)
392}