1use crate::instructions::program_ids::SYSTEM_PROGRAM_ID;
2use crate::types::{AccountMeta, Instruction, Pubkey};
3use borsh::{BorshDeserialize, BorshSerialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
7pub enum SystemInstruction {
8 CreateAccount {
12 lamports: u64,
14 space: u64,
16 owner: Pubkey,
18 },
19
20 Assign {
23 owner: Pubkey,
25 },
26
27 Transfer {
31 lamports: u64,
33 },
34
35 CreateAccountWithSeed {
40 base: Pubkey,
42 seed: String,
44 lamports: u64,
46 space: u64,
48 owner: Pubkey,
50 },
51
52 AdvanceNonceAccount {
57 authorized: Pubkey,
59 },
60
61 WithdrawNonceAccount {
67 lamports: u64,
69 },
70
71 InitializeNonceAccount {
75 authorized: Pubkey,
77 },
78
79 AuthorizeNonceAccount {
83 authorized: Pubkey,
85 },
86
87 Allocate {
90 space: u64,
92 },
93
94 AllocateWithSeed {
98 base: Pubkey,
100 seed: String,
102 space: u64,
104 owner: Pubkey,
106 },
107
108 AssignWithSeed {
112 base: Pubkey,
114 seed: String,
116 owner: Pubkey,
118 },
119
120 TransferWithSeed {
125 lamports: u64,
127 seed: String,
129 owner: Pubkey,
131 },
132}
133
134impl SystemInstruction {
135 pub fn size(&self) -> usize {
137 match self {
138 Self::CreateAccount { .. } => 52, Self::Assign { .. } => 36, Self::Transfer { .. } => 12, Self::CreateAccountWithSeed { seed, .. } => 116 + seed.len(), Self::AdvanceNonceAccount { .. } => 36, Self::WithdrawNonceAccount { .. } => 12, Self::InitializeNonceAccount { .. } => 36, Self::AuthorizeNonceAccount { .. } => 36, Self::Allocate { .. } => 12, Self::AllocateWithSeed { seed, .. } => 84 + seed.len(), Self::AssignWithSeed { seed, .. } => 72 + seed.len(), Self::TransferWithSeed { seed, .. } => 48 + seed.len(), }
151 }
152
153 pub fn serialize(&self) -> Vec<u8> {
155 let mut data = Vec::with_capacity(self.size());
156 match self {
157 Self::CreateAccount {
158 lamports,
159 space,
160 owner,
161 } => {
162 data.extend_from_slice(&[0, 0, 0, 0]); data.extend_from_slice(&lamports.to_le_bytes());
164 data.extend_from_slice(&space.to_le_bytes());
165 data.extend_from_slice(owner.as_bytes());
166 }
167 Self::Assign { owner } => {
168 data.extend_from_slice(&[1, 0, 0, 0]); data.extend_from_slice(owner.as_bytes());
170 }
171 Self::Transfer { lamports } => {
172 data.extend_from_slice(&[2, 0, 0, 0]); data.extend_from_slice(&lamports.to_le_bytes());
174 }
175 Self::CreateAccountWithSeed {
176 base,
177 seed,
178 lamports,
179 space,
180 owner,
181 } => {
182 data.extend_from_slice(&[3, 0, 0, 0]); data.extend_from_slice(base.as_bytes());
184 let seed_bytes = seed.as_bytes();
185 data.extend_from_slice(&(seed_bytes.len() as u32).to_le_bytes());
186 data.extend_from_slice(seed_bytes);
187 data.extend_from_slice(&lamports.to_le_bytes());
188 data.extend_from_slice(&space.to_le_bytes());
189 data.extend_from_slice(owner.as_bytes());
190 }
191 Self::AdvanceNonceAccount { authorized } => {
192 data.extend_from_slice(&[4, 0, 0, 0]); data.extend_from_slice(authorized.as_bytes());
194 }
195 Self::WithdrawNonceAccount { lamports } => {
196 data.extend_from_slice(&[5, 0, 0, 0]); data.extend_from_slice(&lamports.to_le_bytes());
198 }
199 Self::InitializeNonceAccount { authorized } => {
200 data.extend_from_slice(&[6, 0, 0, 0]); data.extend_from_slice(authorized.as_bytes());
202 }
203 Self::AuthorizeNonceAccount { authorized } => {
204 data.extend_from_slice(&[7, 0, 0, 0]); data.extend_from_slice(authorized.as_bytes());
206 }
207 Self::Allocate { space } => {
208 data.extend_from_slice(&[8, 0, 0, 0]); data.extend_from_slice(&space.to_le_bytes());
210 }
211 Self::AllocateWithSeed {
212 base,
213 seed,
214 space,
215 owner,
216 } => {
217 data.extend_from_slice(&[9, 0, 0, 0]); data.extend_from_slice(base.as_bytes());
219 let seed_bytes = seed.as_bytes();
220 data.extend_from_slice(&(seed_bytes.len() as u32).to_le_bytes());
221 data.extend_from_slice(seed_bytes);
222 data.extend_from_slice(&space.to_le_bytes());
223 data.extend_from_slice(owner.as_bytes());
224 }
225 Self::AssignWithSeed { base, seed, owner } => {
226 data.extend_from_slice(&[10, 0, 0, 0]); data.extend_from_slice(base.as_bytes());
228 let seed_bytes = seed.as_bytes();
229 data.extend_from_slice(&(seed_bytes.len() as u32).to_le_bytes());
230 data.extend_from_slice(seed_bytes);
231 data.extend_from_slice(owner.as_bytes());
232 }
233 Self::TransferWithSeed {
234 lamports,
235 seed,
236 owner,
237 } => {
238 data.extend_from_slice(&[11, 0, 0, 0]); data.extend_from_slice(&lamports.to_le_bytes());
240 let seed_bytes = seed.as_bytes();
241 data.extend_from_slice(&(seed_bytes.len() as u32).to_le_bytes());
242 data.extend_from_slice(seed_bytes);
243 data.extend_from_slice(owner.as_bytes());
244 }
245 }
246 data
247 }
248}
249
250pub fn create_account(
254 from_pubkey: &Pubkey,
255 to_pubkey: &Pubkey,
256 lamports: u64,
257 space: u64,
258 owner: &Pubkey,
259) -> Instruction {
260 let account_metas = vec![
261 AccountMeta {
262 pubkey: *from_pubkey,
263 is_signer: true,
264 is_writable: true,
265 },
266 AccountMeta {
267 pubkey: *to_pubkey,
268 is_signer: true,
269 is_writable: true,
270 },
271 ];
272
273 let instruction = SystemInstruction::CreateAccount {
274 lamports,
275 space,
276 owner: *owner,
277 };
278
279 Instruction {
280 program_id: Pubkey::from_base58(SYSTEM_PROGRAM_ID).unwrap(),
281 accounts: account_metas,
282 data: instruction.serialize(),
283 }
284}
285
286pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction {
288 let account_metas = vec![AccountMeta {
289 pubkey: *pubkey,
290 is_signer: true,
291 is_writable: true,
292 }];
293
294 let instruction = SystemInstruction::Assign { owner: *owner };
295
296 Instruction {
297 program_id: Pubkey::from_base58(SYSTEM_PROGRAM_ID).unwrap(),
298 accounts: account_metas,
299 data: instruction.serialize(),
300 }
301}
302
303pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction {
305 let account_metas = vec![
306 AccountMeta {
307 pubkey: *from_pubkey,
308 is_signer: true,
309 is_writable: true,
310 },
311 AccountMeta {
312 pubkey: *to_pubkey,
313 is_signer: false,
314 is_writable: true,
315 },
316 ];
317
318 let instruction = SystemInstruction::Transfer { lamports };
319
320 Instruction {
321 program_id: Pubkey::from_base58(SYSTEM_PROGRAM_ID).unwrap(),
322 accounts: account_metas,
323 data: instruction.serialize(),
324 }
325}
326
327pub fn advance_nonce_account(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction {
329 let account_metas = vec![
330 AccountMeta {
331 pubkey: *nonce_pubkey,
332 is_signer: false,
333 is_writable: true,
334 },
335 AccountMeta {
337 pubkey: Pubkey::from_base58("SysvarRecentB1ockHashes11111111111111111111").unwrap(),
338 is_signer: false,
339 is_writable: false,
340 },
341 AccountMeta {
342 pubkey: *authorized_pubkey,
343 is_signer: true,
344 is_writable: false,
345 },
346 ];
347
348 let instruction = SystemInstruction::AdvanceNonceAccount {
349 authorized: *authorized_pubkey,
350 };
351
352 Instruction {
353 program_id: Pubkey::from_base58(SYSTEM_PROGRAM_ID).unwrap(),
354 accounts: account_metas,
355 data: instruction.serialize(),
356 }
357}
358
359pub fn withdraw_nonce_account(
361 nonce_pubkey: &Pubkey,
362 authorized_pubkey: &Pubkey,
363 to_pubkey: &Pubkey,
364 lamports: u64,
365) -> Instruction {
366 let account_metas = vec![
367 AccountMeta {
368 pubkey: *nonce_pubkey,
369 is_signer: false,
370 is_writable: true,
371 },
372 AccountMeta {
373 pubkey: *to_pubkey,
374 is_signer: false,
375 is_writable: true,
376 },
377 AccountMeta {
379 pubkey: Pubkey::from_base58("SysvarRecentB1ockHashes11111111111111111111").unwrap(),
380 is_signer: false,
381 is_writable: false,
382 },
383 AccountMeta {
385 pubkey: Pubkey::from_base58("SysvarRent111111111111111111111111111111111").unwrap(),
386 is_signer: false,
387 is_writable: false,
388 },
389 AccountMeta {
390 pubkey: *authorized_pubkey,
391 is_signer: true,
392 is_writable: false,
393 },
394 ];
395
396 let instruction = SystemInstruction::WithdrawNonceAccount { lamports };
397
398 Instruction {
399 program_id: Pubkey::from_base58(SYSTEM_PROGRAM_ID).unwrap(),
400 accounts: account_metas,
401 data: instruction.serialize(),
402 }
403}
404
405pub fn create_nonce_account(
407 from_pubkey: &Pubkey,
408 nonce_pubkey: &Pubkey,
409 authority_pubkey: &Pubkey,
410 lamports: u64,
411) -> Vec<Instruction> {
412 vec![
413 create_account(
415 from_pubkey,
416 nonce_pubkey,
417 lamports,
418 80, &Pubkey::from_base58(SYSTEM_PROGRAM_ID).unwrap(),
420 ),
421 initialize_nonce_account(nonce_pubkey, authority_pubkey),
423 ]
424}
425
426pub fn initialize_nonce_account(nonce_pubkey: &Pubkey, authority_pubkey: &Pubkey) -> Instruction {
428 let account_metas = vec![
429 AccountMeta {
430 pubkey: *nonce_pubkey,
431 is_signer: false,
432 is_writable: true,
433 },
434 AccountMeta {
436 pubkey: Pubkey::from_base58("SysvarRecentB1ockHashes11111111111111111111").unwrap(),
437 is_signer: false,
438 is_writable: false,
439 },
440 AccountMeta {
442 pubkey: Pubkey::from_base58("SysvarRent111111111111111111111111111111111").unwrap(),
443 is_signer: false,
444 is_writable: false,
445 },
446 ];
447
448 let instruction = SystemInstruction::InitializeNonceAccount {
449 authorized: *authority_pubkey,
450 };
451
452 Instruction {
453 program_id: Pubkey::from_base58(SYSTEM_PROGRAM_ID).unwrap(),
454 accounts: account_metas,
455 data: instruction.serialize(),
456 }
457}
458
459pub fn authorize_nonce_account(
461 nonce_pubkey: &Pubkey,
462 authority_pubkey: &Pubkey,
463 new_authority_pubkey: &Pubkey,
464) -> Instruction {
465 let account_metas = vec![
466 AccountMeta {
467 pubkey: *nonce_pubkey,
468 is_signer: false,
469 is_writable: true,
470 },
471 AccountMeta {
472 pubkey: *authority_pubkey,
473 is_signer: true,
474 is_writable: false,
475 },
476 ];
477
478 let instruction = SystemInstruction::AuthorizeNonceAccount {
479 authorized: *new_authority_pubkey,
480 };
481
482 Instruction {
483 program_id: Pubkey::from_base58(SYSTEM_PROGRAM_ID).unwrap(),
484 accounts: account_metas,
485 data: instruction.serialize(),
486 }
487}
488
489#[cfg(test)]
490mod tests {
491 use super::*;
492 use crate::Pubkey;
493
494 fn from_pubkey() -> Pubkey {
495 Pubkey::from_base58("7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z").unwrap()
496 }
497
498 fn to_pubkey() -> Pubkey {
499 Pubkey::from_base58("DShWnroshVbeUp28oopA3Pu7oFPDBtC1DBmPECXXAQ9n").unwrap()
500 }
501
502 fn owner_pubkey() -> Pubkey {
503 Pubkey::from_base58("Hozo7TadHq6PMMiGLGNvgk79Hvj5VTAM7Ny2bamQ2m8q").unwrap()
504 }
505
506 #[test]
507 fn test_sys_create_account() {
508 let from = from_pubkey();
509 let to = to_pubkey();
510 let owner = owner_pubkey();
511 let lamports = 10_000_000_000; let space = 165; let instruction = create_account(&from, &to, lamports, space, &owner);
515
516 assert_eq!(
518 instruction.program_id,
519 Pubkey::from_base58(SYSTEM_PROGRAM_ID).unwrap()
520 );
521 assert_eq!(instruction.accounts.len(), 2);
522
523 assert_eq!(instruction.accounts[0].pubkey, from);
525 assert!(instruction.accounts[0].is_signer);
526 assert!(instruction.accounts[0].is_writable);
527
528 assert_eq!(instruction.accounts[1].pubkey, to);
530 assert!(instruction.accounts[1].is_signer);
531 assert!(instruction.accounts[1].is_writable);
532
533 let data = instruction.data.clone();
535
536 assert_eq!(data[0], 0);
538
539 assert_eq!(
544 data.len(),
545 SystemInstruction::CreateAccount {
546 lamports,
547 space,
548 owner,
549 }
550 .size()
551 );
552
553 assert_eq!(&data[data.len() - 32..], owner.as_bytes());
555 }
556
557 #[test]
558 fn test_short_vec_encode() {
559 let from = from_pubkey();
566 let to = to_pubkey();
567 let owner = owner_pubkey();
568
569 let accounts = vec![
571 AccountMeta {
572 pubkey: from,
573 is_signer: true,
574 is_writable: true,
575 },
576 AccountMeta {
577 pubkey: to,
578 is_signer: false,
579 is_writable: true,
580 },
581 AccountMeta {
582 pubkey: owner,
583 is_signer: false,
584 is_writable: false,
585 },
586 ];
587
588 let instruction = Instruction {
590 program_id: Pubkey::from_base58(SYSTEM_PROGRAM_ID).unwrap(),
591 accounts,
592 data: vec![0, 1, 2, 3], };
594
595 assert_eq!(instruction.accounts.len(), 3);
601 }
602}