1use crate::account_view::AccountView;
13use crate::address::Address;
14use crate::error::ProgramError;
15use crate::instruction::{CpiAccount, Signer};
16use crate::ProgramResult;
17
18pub const SYSTEM_PROGRAM_ID: Address = Address::new_from_array([
20 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,
21]);
22
23pub const RECENT_BLOCKHASHES_ID: Address =
26 crate::address!("SysvarRecentB1ockHashes11111111111111111111");
27
28pub const RENT_SYSVAR_ID: Address = crate::address!("SysvarRent111111111111111111111111111111111");
30
31pub const MAX_SEED_LEN: usize = 32;
34
35const CREATE_ACCOUNT_META: (usize, usize) = (0b11, 0b11);
37const CREATE_ACCOUNT_ALLOW_PREFUND_META: (usize, usize) = (0b11, 0b11);
41const CREATE_ACCOUNT_ALLOW_PREFUND_UNFUNDED_META: (usize, usize) = (0b1, 0b1);
42const TRANSFER_META: (usize, usize) = (0b11, 0b01);
43const ASSIGN_META: (usize, usize) = (0b1, 0b1);
44const ALLOCATE_META: (usize, usize) = (0b1, 0b1);
45const CREATE_ACCOUNT_WITH_SEED_META: (usize, usize) = (0b011, 0b101);
46const ALLOCATE_WITH_SEED_META: (usize, usize) = (0b01, 0b10);
47const ASSIGN_WITH_SEED_META: (usize, usize) = (0b01, 0b10);
48const TRANSFER_WITH_SEED_META: (usize, usize) = (0b101, 0b010);
49const ADVANCE_NONCE_META: (usize, usize) = (0b001, 0b100);
50const WITHDRAW_NONCE_META: (usize, usize) = (0b00011, 0b10000);
51const INITIALIZE_NONCE_META: (usize, usize) = (0b001, 0);
52const AUTHORIZE_NONCE_META: (usize, usize) = (0b01, 0b10);
53const UPGRADE_NONCE_META: (usize, usize) = (0b1, 0);
54
55pub struct CreateAccount<'a, 'b> {
59 pub from: &'a AccountView<'a>,
60 pub to: &'a AccountView<'a>,
61 pub lamports: u64,
62 pub space: u64,
63 pub owner: &'b Address,
64}
65
66impl CreateAccount<'_, '_> {
67 #[inline]
69 pub fn invoke(&self) -> ProgramResult {
70 self.invoke_signed(&[])
71 }
72
73 #[inline]
75 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
76 let mut data = [0u8; 52];
78 data[4..12].copy_from_slice(&self.lamports.to_le_bytes());
80 data[12..20].copy_from_slice(&self.space.to_le_bytes());
81 data[20..52].copy_from_slice(self.owner.as_array());
82
83 let accounts = [CpiAccount::from(self.from), CpiAccount::from(self.to)];
84
85 invoke_system(
86 &data,
87 &accounts,
88 CREATE_ACCOUNT_META.0,
89 CREATE_ACCOUNT_META.1,
90 signers,
91 )
92 }
93}
94
95pub struct CreateAccountAllowPrefund<'a, 'b> {
112 pub to: &'a AccountView<'a>,
113 pub funding: Option<(&'a AccountView<'a>, u64)>,
116 pub space: u64,
117 pub owner: &'b Address,
118}
119
120impl CreateAccountAllowPrefund<'_, '_> {
121 #[inline]
123 pub fn invoke(&self) -> ProgramResult {
124 self.invoke_signed(&[])
125 }
126
127 #[inline]
129 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
130 let (from, lamports) = match self.funding {
131 Some((from, lamports)) => (Some(from), lamports),
132 None => (None, 0),
133 };
134 let mut data = [0u8; 52];
136 data[0..4].copy_from_slice(&13u32.to_le_bytes());
137 data[4..12].copy_from_slice(&lamports.to_le_bytes());
138 data[12..20].copy_from_slice(&self.space.to_le_bytes());
139 data[20..52].copy_from_slice(self.owner.as_array());
140
141 match from {
142 Some(from) => {
143 let accounts = [CpiAccount::from(self.to), CpiAccount::from(from)];
144 invoke_system(
145 &data,
146 &accounts,
147 CREATE_ACCOUNT_ALLOW_PREFUND_META.0,
148 CREATE_ACCOUNT_ALLOW_PREFUND_META.1,
149 signers,
150 )
151 }
152 None => {
153 let accounts = [CpiAccount::from(self.to)];
154 invoke_system(
155 &data,
156 &accounts,
157 CREATE_ACCOUNT_ALLOW_PREFUND_UNFUNDED_META.0,
158 CREATE_ACCOUNT_ALLOW_PREFUND_UNFUNDED_META.1,
159 signers,
160 )
161 }
162 }
163 }
164}
165
166pub struct Transfer<'a> {
170 pub from: &'a AccountView<'a>,
171 pub to: &'a AccountView<'a>,
172 pub lamports: u64,
173}
174
175impl Transfer<'_> {
176 #[inline]
178 pub fn invoke(&self) -> ProgramResult {
179 self.invoke_signed(&[])
180 }
181
182 #[inline]
184 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
185 let mut data = [0u8; 12];
187 data[0] = 2;
188 data[4..12].copy_from_slice(&self.lamports.to_le_bytes());
189
190 let accounts = [CpiAccount::from(self.from), CpiAccount::from(self.to)];
191
192 invoke_system(&data, &accounts, TRANSFER_META.0, TRANSFER_META.1, signers)
193 }
194}
195
196pub struct Assign<'a, 'b> {
200 pub account: &'a AccountView<'a>,
201 pub owner: &'b Address,
202}
203
204impl Assign<'_, '_> {
205 #[inline]
207 pub fn invoke(&self) -> ProgramResult {
208 self.invoke_signed(&[])
209 }
210
211 #[inline]
213 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
214 let mut data = [0u8; 36];
216 data[0] = 1;
217 data[4..36].copy_from_slice(self.owner.as_array());
218
219 let accounts = [CpiAccount::from(self.account)];
220
221 invoke_system(&data, &accounts, ASSIGN_META.0, ASSIGN_META.1, signers)
222 }
223}
224
225pub struct Allocate<'a> {
229 pub account: &'a AccountView<'a>,
230 pub space: u64,
231}
232
233impl Allocate<'_> {
234 #[inline]
236 pub fn invoke(&self) -> ProgramResult {
237 self.invoke_signed(&[])
238 }
239
240 #[inline]
242 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
243 let mut data = [0u8; 12];
245 data[0] = 8;
246 data[4..12].copy_from_slice(&self.space.to_le_bytes());
247
248 let accounts = [CpiAccount::from(self.account)];
249
250 invoke_system(&data, &accounts, ALLOCATE_META.0, ALLOCATE_META.1, signers)
251 }
252}
253
254pub struct CreateAccountWithSeed<'a, 'b> {
265 pub from: &'a AccountView<'a>,
266 pub to: &'a AccountView<'a>,
267 pub base: &'a AccountView<'a>,
268 pub seed: &'b [u8],
269 pub lamports: u64,
270 pub space: u64,
271 pub owner: &'b Address,
272}
273
274impl CreateAccountWithSeed<'_, '_> {
275 #[inline]
277 pub fn invoke(&self) -> ProgramResult {
278 self.invoke_signed(&[])
279 }
280
281 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
283 if self.seed.len() > MAX_SEED_LEN {
284 return Err(ProgramError::MaxSeedLengthExceeded);
285 }
286 let mut data = [0u8; 4 + 32 + 8 + MAX_SEED_LEN + 8 + 8 + 32];
289 data[0] = 3;
290 let mut n = 4;
291 data[n..n + 32].copy_from_slice(self.base.address().as_array());
292 n += 32;
293 data[n..n + 8].copy_from_slice(&(self.seed.len() as u64).to_le_bytes());
294 n += 8;
295 data[n..n + self.seed.len()].copy_from_slice(self.seed);
296 n += self.seed.len();
297 data[n..n + 8].copy_from_slice(&self.lamports.to_le_bytes());
298 n += 8;
299 data[n..n + 8].copy_from_slice(&self.space.to_le_bytes());
300 n += 8;
301 data[n..n + 32].copy_from_slice(self.owner.as_array());
302 n += 32;
303
304 let accounts = [
305 CpiAccount::from(self.from),
306 CpiAccount::from(self.to),
307 CpiAccount::from(self.base),
308 ];
309 invoke_system(
310 &data[..n],
311 &accounts,
312 CREATE_ACCOUNT_WITH_SEED_META.0,
313 CREATE_ACCOUNT_WITH_SEED_META.1,
314 signers,
315 )
316 }
317}
318
319pub struct AllocateWithSeed<'a, 'b> {
321 pub account: &'a AccountView<'a>,
322 pub base: &'a AccountView<'a>,
323 pub seed: &'b [u8],
324 pub space: u64,
325 pub owner: &'b Address,
326}
327
328impl AllocateWithSeed<'_, '_> {
329 #[inline]
331 pub fn invoke(&self) -> ProgramResult {
332 self.invoke_signed(&[])
333 }
334
335 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
337 if self.seed.len() > MAX_SEED_LEN {
338 return Err(ProgramError::MaxSeedLengthExceeded);
339 }
340 let mut data = [0u8; 4 + 32 + 8 + MAX_SEED_LEN + 8 + 32];
342 data[0] = 9;
343 let mut n = 4;
344 data[n..n + 32].copy_from_slice(self.base.address().as_array());
345 n += 32;
346 data[n..n + 8].copy_from_slice(&(self.seed.len() as u64).to_le_bytes());
347 n += 8;
348 data[n..n + self.seed.len()].copy_from_slice(self.seed);
349 n += self.seed.len();
350 data[n..n + 8].copy_from_slice(&self.space.to_le_bytes());
351 n += 8;
352 data[n..n + 32].copy_from_slice(self.owner.as_array());
353 n += 32;
354
355 let accounts = [CpiAccount::from(self.account), CpiAccount::from(self.base)];
356 invoke_system(
357 &data[..n],
358 &accounts,
359 ALLOCATE_WITH_SEED_META.0,
360 ALLOCATE_WITH_SEED_META.1,
361 signers,
362 )
363 }
364}
365
366pub struct AssignWithSeed<'a, 'b> {
368 pub account: &'a AccountView<'a>,
369 pub base: &'a AccountView<'a>,
370 pub seed: &'b [u8],
371 pub owner: &'b Address,
372}
373
374impl AssignWithSeed<'_, '_> {
375 #[inline]
377 pub fn invoke(&self) -> ProgramResult {
378 self.invoke_signed(&[])
379 }
380
381 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
383 if self.seed.len() > MAX_SEED_LEN {
384 return Err(ProgramError::MaxSeedLengthExceeded);
385 }
386 let mut data = [0u8; 4 + 32 + 8 + MAX_SEED_LEN + 32];
388 data[0] = 10;
389 let mut n = 4;
390 data[n..n + 32].copy_from_slice(self.base.address().as_array());
391 n += 32;
392 data[n..n + 8].copy_from_slice(&(self.seed.len() as u64).to_le_bytes());
393 n += 8;
394 data[n..n + self.seed.len()].copy_from_slice(self.seed);
395 n += self.seed.len();
396 data[n..n + 32].copy_from_slice(self.owner.as_array());
397 n += 32;
398
399 let accounts = [CpiAccount::from(self.account), CpiAccount::from(self.base)];
400 invoke_system(
401 &data[..n],
402 &accounts,
403 ASSIGN_WITH_SEED_META.0,
404 ASSIGN_WITH_SEED_META.1,
405 signers,
406 )
407 }
408}
409
410pub struct TransferWithSeed<'a, 'b> {
415 pub from: &'a AccountView<'a>,
416 pub base: &'a AccountView<'a>,
417 pub to: &'a AccountView<'a>,
418 pub lamports: u64,
419 pub from_seed: &'b [u8],
420 pub from_owner: &'b Address,
421}
422
423impl TransferWithSeed<'_, '_> {
424 #[inline]
426 pub fn invoke(&self) -> ProgramResult {
427 self.invoke_signed(&[])
428 }
429
430 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
432 if self.from_seed.len() > MAX_SEED_LEN {
433 return Err(ProgramError::MaxSeedLengthExceeded);
434 }
435 let mut data = [0u8; 4 + 8 + 8 + MAX_SEED_LEN + 32];
437 data[0] = 11;
438 let mut n = 4;
439 data[n..n + 8].copy_from_slice(&self.lamports.to_le_bytes());
440 n += 8;
441 data[n..n + 8].copy_from_slice(&(self.from_seed.len() as u64).to_le_bytes());
442 n += 8;
443 data[n..n + self.from_seed.len()].copy_from_slice(self.from_seed);
444 n += self.from_seed.len();
445 data[n..n + 32].copy_from_slice(self.from_owner.as_array());
446 n += 32;
447
448 let accounts = [
449 CpiAccount::from(self.from),
450 CpiAccount::from(self.base),
451 CpiAccount::from(self.to),
452 ];
453 invoke_system(
454 &data[..n],
455 &accounts,
456 TRANSFER_WITH_SEED_META.0,
457 TRANSFER_WITH_SEED_META.1,
458 signers,
459 )
460 }
461}
462
463pub struct AdvanceNonceAccount<'a> {
471 pub nonce: &'a AccountView<'a>,
472 pub recent_blockhashes: &'a AccountView<'a>,
473 pub authority: &'a AccountView<'a>,
474}
475
476impl AdvanceNonceAccount<'_> {
477 #[inline]
479 pub fn invoke(&self) -> ProgramResult {
480 self.invoke_signed(&[])
481 }
482
483 #[inline]
485 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
486 let data = [4u8, 0, 0, 0];
487 let accounts = [
488 CpiAccount::from(self.nonce),
489 CpiAccount::from(self.recent_blockhashes),
490 CpiAccount::from(self.authority),
491 ];
492 invoke_system(
493 &data,
494 &accounts,
495 ADVANCE_NONCE_META.0,
496 ADVANCE_NONCE_META.1,
497 signers,
498 )
499 }
500}
501
502pub struct WithdrawNonceAccount<'a> {
507 pub nonce: &'a AccountView<'a>,
508 pub to: &'a AccountView<'a>,
509 pub recent_blockhashes: &'a AccountView<'a>,
510 pub rent: &'a AccountView<'a>,
511 pub authority: &'a AccountView<'a>,
512 pub lamports: u64,
513}
514
515impl WithdrawNonceAccount<'_> {
516 #[inline]
518 pub fn invoke(&self) -> ProgramResult {
519 self.invoke_signed(&[])
520 }
521
522 #[inline]
524 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
525 let mut data = [0u8; 12];
526 data[0] = 5;
527 data[4..12].copy_from_slice(&self.lamports.to_le_bytes());
528 let accounts = [
529 CpiAccount::from(self.nonce),
530 CpiAccount::from(self.to),
531 CpiAccount::from(self.recent_blockhashes),
532 CpiAccount::from(self.rent),
533 CpiAccount::from(self.authority),
534 ];
535 invoke_system(
536 &data,
537 &accounts,
538 WITHDRAW_NONCE_META.0,
539 WITHDRAW_NONCE_META.1,
540 signers,
541 )
542 }
543}
544
545pub struct InitializeNonceAccount<'a, 'b> {
549 pub nonce: &'a AccountView<'a>,
550 pub recent_blockhashes: &'a AccountView<'a>,
551 pub rent: &'a AccountView<'a>,
552 pub authority: &'b Address,
553}
554
555impl InitializeNonceAccount<'_, '_> {
556 #[inline]
558 pub fn invoke(&self) -> ProgramResult {
559 self.invoke_signed(&[])
560 }
561
562 #[inline]
564 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
565 let mut data = [0u8; 36];
566 data[0] = 6;
567 data[4..36].copy_from_slice(self.authority.as_array());
568 let accounts = [
569 CpiAccount::from(self.nonce),
570 CpiAccount::from(self.recent_blockhashes),
571 CpiAccount::from(self.rent),
572 ];
573 invoke_system(
574 &data,
575 &accounts,
576 INITIALIZE_NONCE_META.0,
577 INITIALIZE_NONCE_META.1,
578 signers,
579 )
580 }
581}
582
583pub struct AuthorizeNonceAccount<'a, 'b> {
587 pub nonce: &'a AccountView<'a>,
588 pub authority: &'a AccountView<'a>,
589 pub new_authority: &'b Address,
590}
591
592impl AuthorizeNonceAccount<'_, '_> {
593 #[inline]
595 pub fn invoke(&self) -> ProgramResult {
596 self.invoke_signed(&[])
597 }
598
599 #[inline]
601 pub fn invoke_signed(&self, signers: &[Signer<'_, '_>]) -> ProgramResult {
602 let mut data = [0u8; 36];
603 data[0] = 7;
604 data[4..36].copy_from_slice(self.new_authority.as_array());
605 let accounts = [
606 CpiAccount::from(self.nonce),
607 CpiAccount::from(self.authority),
608 ];
609 invoke_system(
610 &data,
611 &accounts,
612 AUTHORIZE_NONCE_META.0,
613 AUTHORIZE_NONCE_META.1,
614 signers,
615 )
616 }
617}
618
619pub struct UpgradeNonceAccount<'a> {
623 pub nonce: &'a AccountView<'a>,
624}
625
626impl UpgradeNonceAccount<'_> {
627 #[inline]
629 pub fn invoke(&self) -> ProgramResult {
630 let data = [12u8, 0, 0, 0];
631 let accounts = [CpiAccount::from(self.nonce)];
632 invoke_system(
633 &data,
634 &accounts,
635 UPGRADE_NONCE_META.0,
636 UPGRADE_NONCE_META.1,
637 &[],
638 )
639 }
640}
641
642#[inline]
646fn invoke_system<'a, const ACCOUNTS: usize>(
647 data: &[u8],
648 accounts: &[CpiAccount<'a>; ACCOUNTS],
649 writable_mask: usize,
650 signer_mask: usize,
651 signers: &[Signer<'_, '_>],
652) -> ProgramResult {
653 crate::cpi::invoke_specialized_signed(
654 &SYSTEM_PROGRAM_ID,
655 data,
656 accounts,
657 writable_mask,
658 signer_mask,
659 signers,
660 )
661}
662
663pub mod instructions {
666 pub use super::{
667 AdvanceNonceAccount, Allocate, AllocateWithSeed, Assign, AssignWithSeed,
668 AuthorizeNonceAccount, CreateAccount, CreateAccountAllowPrefund, CreateAccountWithSeed,
669 InitializeNonceAccount, Transfer, TransferWithSeed, UpgradeNonceAccount,
670 WithdrawNonceAccount,
671 };
672}
673
674pub const NONCE_ACCOUNT_LEN: usize = 80;
689
690pub const NONCE_VERSION_CURRENT: u32 = 1;
692
693pub const NONCE_STATE_INITIALIZED: u32 = 1;
695
696#[derive(Clone, Copy, Debug)]
698pub struct NonceState<'a> {
699 data: &'a [u8],
700}
701
702impl<'a> NonceState<'a> {
703 #[inline]
708 pub fn from_account_data(data: &'a [u8]) -> Result<Self, ProgramError> {
709 if data.len() < NONCE_ACCOUNT_LEN {
710 return Err(ProgramError::AccountDataTooSmall);
711 }
712 let version = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
713 let state = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
714 if version != NONCE_VERSION_CURRENT || state != NONCE_STATE_INITIALIZED {
715 return Err(ProgramError::InvalidAccountData);
716 }
717 Ok(Self { data })
718 }
719
720 #[inline]
722 pub fn authority(&self) -> &'a Address {
723 unsafe { &*(self.data.as_ptr().add(8) as *const Address) }
726 }
727
728 #[inline]
730 pub fn durable_nonce(&self) -> &'a [u8; 32] {
731 unsafe { &*(self.data.as_ptr().add(40) as *const [u8; 32]) }
733 }
734
735 #[inline]
737 pub fn lamports_per_signature(&self) -> u64 {
738 let b = &self.data[72..80];
739 u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])
740 }
741}