solana_system_interface/instruction.rs
1//! Instructions and constructors for the system program.
2//!
3//! The system program is responsible for the creation of accounts and [nonce
4//! accounts][na]. It is responsible for transferring lamports from accounts
5//! owned by the system program, including typical user wallet accounts.
6//!
7//! [na]: https://docs.solanalabs.com/implemented-proposals/durable-tx-nonces
8//!
9//! Account creation typically involves three steps: [`allocate`] space,
10//! [`transfer`] lamports for rent, [`assign`] to its owning program. The
11//! [`create_account`] function does all three at once. All new accounts must
12//! contain enough lamports to be [rent exempt], or else the creation
13//! instruction will fail.
14//!
15//! [rent exempt]: https://solana.com/docs/core/accounts#rent-exemption
16//!
17//! The accounts created by the System program can either be user-controlled,
18//! where the secret keys are held outside the blockchain,
19//! or they can be [program derived addresses][pda],
20//! where write access to accounts is granted by an owning program.
21//!
22//! [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address
23//!
24//! Most of the functions in this module construct an [`Instruction`], that must
25//! be submitted to the runtime for execution, either via RPC, typically with
26//! [`RpcClient`], or through [cross-program invocation][cpi].
27//!
28//! When invoking through CPI, the [`invoke`] or [`invoke_signed`] instruction
29//! requires all account references to be provided explicitly as [`AccountInfo`]
30//! values. The account references required are specified in the documentation
31//! for the [`SystemInstruction`] variants for each System program instruction,
32//! and these variants are linked from the documentation for their constructors.
33//!
34//! [`RpcClient`]: https://docs.rs/solana-client/latest/solana_client/rpc_client/struct.RpcClient.html
35//! [cpi]: https://docs.rs/solana-cpi/latest/solana_cpi/index.html
36//! [`invoke`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
37//! [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html
38//! [`AccountInfo`]: https://docs.rs/solana-account-info/latest/solana_account_info/struct.AccountInfo.html
39//! [`Instruction`]:
40//! https://docs.rs/solana-instruction/latest/solana_instruction/struct.Instruction.html
41
42use solana_pubkey::Pubkey;
43#[cfg(feature = "bincode")]
44use {
45 crate::program::ID,
46 solana_instruction::{AccountMeta, Instruction},
47};
48
49// Inline some constants to avoid dependencies.
50//
51// Note: replace these inline IDs with the corresponding value from
52// `solana_sdk_ids` once the version is updated to 2.2.0.
53
54#[cfg(feature = "bincode")]
55const RECENT_BLOCKHASHES_ID: Pubkey =
56 Pubkey::from_str_const("SysvarRecentB1ockHashes11111111111111111111");
57
58#[cfg(feature = "bincode")]
59const RENT_ID: Pubkey = Pubkey::from_str_const("SysvarRent111111111111111111111111111111111");
60
61#[cfg(feature = "bincode")]
62#[cfg(test)]
63static_assertions::const_assert_eq!(solana_nonce::state::State::size(), NONCE_STATE_SIZE);
64/// The serialized size of the nonce state.
65#[cfg(feature = "bincode")]
66const NONCE_STATE_SIZE: usize = 80;
67
68/// An instruction to the system program.
69#[cfg_attr(
70 feature = "frozen-abi",
71 solana_frozen_abi_macro::frozen_abi(digest = "8M189WgLE19cw1iYDAFLNJKoAUKyqF9jsKYennJi5BfK"),
72 derive(
73 solana_frozen_abi_macro::AbiExample,
74 solana_frozen_abi_macro::AbiEnumVisitor
75 )
76)]
77#[cfg_attr(
78 feature = "serde",
79 derive(serde_derive::Deserialize, serde_derive::Serialize)
80)]
81#[derive(Clone, Debug, Eq, PartialEq)]
82pub enum SystemInstruction {
83 /// Create a new account
84 ///
85 /// # Account references
86 /// 0. `[WRITE, SIGNER]` Funding account
87 /// 1. `[WRITE, SIGNER]` New account
88 CreateAccount {
89 /// Number of lamports to transfer to the new account
90 lamports: u64,
91
92 /// Number of bytes of memory to allocate
93 space: u64,
94
95 /// Address of program that will own the new account
96 owner: Pubkey,
97 },
98
99 /// Assign account to a program
100 ///
101 /// # Account references
102 /// 0. `[WRITE, SIGNER]` Assigned account public key
103 Assign {
104 /// Owner program account
105 owner: Pubkey,
106 },
107
108 /// Transfer lamports
109 ///
110 /// # Account references
111 /// 0. `[WRITE, SIGNER]` Funding account
112 /// 1. `[WRITE]` Recipient account
113 Transfer { lamports: u64 },
114
115 /// Create a new account at an address derived from a base pubkey and a seed
116 ///
117 /// # Account references
118 /// 0. `[WRITE, SIGNER]` Funding account
119 /// 1. `[WRITE]` Created account
120 /// 2. `[SIGNER]` (optional) Base account; the account matching the base Pubkey below must be
121 /// provided as a signer, but may be the same as the funding account
122 /// and provided as account 0
123 CreateAccountWithSeed {
124 /// Base public key
125 base: Pubkey,
126
127 /// String of ASCII chars, no longer than `Pubkey::MAX_SEED_LEN`
128 seed: String,
129
130 /// Number of lamports to transfer to the new account
131 lamports: u64,
132
133 /// Number of bytes of memory to allocate
134 space: u64,
135
136 /// Owner program account address
137 owner: Pubkey,
138 },
139
140 /// Consumes a stored nonce, replacing it with a successor
141 ///
142 /// # Account references
143 /// 0. `[WRITE]` Nonce account
144 /// 1. `[]` RecentBlockhashes sysvar
145 /// 2. `[SIGNER]` Nonce authority
146 AdvanceNonceAccount,
147
148 /// Withdraw funds from a nonce account
149 ///
150 /// # Account references
151 /// 0. `[WRITE]` Nonce account
152 /// 1. `[WRITE]` Recipient account
153 /// 2. `[]` RecentBlockhashes sysvar
154 /// 3. `[]` Rent sysvar
155 /// 4. `[SIGNER]` Nonce authority
156 ///
157 /// The `u64` parameter is the lamports to withdraw, which must leave the
158 /// account balance above the rent exempt reserve or at zero.
159 WithdrawNonceAccount(u64),
160
161 /// Drive state of Uninitialized nonce account to Initialized, setting the nonce value
162 ///
163 /// # Account references
164 /// 0. `[WRITE]` Nonce account
165 /// 1. `[]` RecentBlockhashes sysvar
166 /// 2. `[]` Rent sysvar
167 ///
168 /// The `Pubkey` parameter specifies the entity authorized to execute nonce
169 /// instruction on the account
170 ///
171 /// No signatures are required to execute this instruction, enabling derived
172 /// nonce account addresses
173 InitializeNonceAccount(Pubkey),
174
175 /// Change the entity authorized to execute nonce instructions on the account
176 ///
177 /// # Account references
178 /// 0. `[WRITE]` Nonce account
179 /// 1. `[SIGNER]` Nonce authority
180 ///
181 /// The `Pubkey` parameter identifies the entity to authorize
182 AuthorizeNonceAccount(Pubkey),
183
184 /// Allocate space in a (possibly new) account without funding
185 ///
186 /// # Account references
187 /// 0. `[WRITE, SIGNER]` New account
188 Allocate {
189 /// Number of bytes of memory to allocate
190 space: u64,
191 },
192
193 /// Allocate space for and assign an account at an address
194 /// derived from a base public key and a seed
195 ///
196 /// # Account references
197 /// 0. `[WRITE]` Allocated account
198 /// 1. `[SIGNER]` Base account
199 AllocateWithSeed {
200 /// Base public key
201 base: Pubkey,
202
203 /// String of ASCII chars, no longer than `pubkey::MAX_SEED_LEN`
204 seed: String,
205
206 /// Number of bytes of memory to allocate
207 space: u64,
208
209 /// Owner program account
210 owner: Pubkey,
211 },
212
213 /// Assign account to a program based on a seed
214 ///
215 /// # Account references
216 /// 0. `[WRITE]` Assigned account
217 /// 1. `[SIGNER]` Base account
218 AssignWithSeed {
219 /// Base public key
220 base: Pubkey,
221
222 /// String of ASCII chars, no longer than `pubkey::MAX_SEED_LEN`
223 seed: String,
224
225 /// Owner program account
226 owner: Pubkey,
227 },
228
229 /// Transfer lamports from a derived address
230 ///
231 /// # Account references
232 /// 0. `[WRITE]` Funding account
233 /// 1. `[SIGNER]` Base for funding account
234 /// 2. `[WRITE]` Recipient account
235 TransferWithSeed {
236 /// Amount to transfer
237 lamports: u64,
238
239 /// Seed to use to derive the funding account address
240 from_seed: String,
241
242 /// Owner to use to derive the funding account address
243 from_owner: Pubkey,
244 },
245
246 /// One-time idempotent upgrade of legacy nonce versions in order to bump
247 /// them out of chain blockhash domain.
248 ///
249 /// # Account references
250 /// 0. `[WRITE]` Nonce account
251 UpgradeNonceAccount,
252}
253
254/// Create an account.
255///
256/// This function produces an [`Instruction`] which must be submitted in a
257/// [`Transaction`] or [invoked] to take effect, containing a serialized
258/// [`SystemInstruction::CreateAccount`].
259///
260/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html
261/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
262///
263/// Account creation typically involves three steps: [`allocate`] space,
264/// [`transfer`] lamports for rent, [`assign`] to its owning program. The
265/// [`create_account`] function does all three at once.
266///
267/// # Required signers
268///
269/// The `from_pubkey` and `to_pubkey` signers must sign the transaction.
270///
271/// # Examples
272///
273/// These examples use a single invocation of
274/// [`SystemInstruction::CreateAccount`] to create a new account, allocate some
275/// space, transfer it the minimum lamports for rent exemption, and assign it to
276/// the system program,
277///
278/// ## Example: client-side RPC
279///
280/// This example submits the instruction from an RPC client.
281/// The `payer` and `new_account` are signers.
282///
283/// ```
284/// # use solana_example_mocks::{solana_sdk, solana_rpc_client};
285/// use solana_rpc_client::rpc_client::RpcClient;
286/// use solana_sdk::{
287/// signature::{Keypair, Signer},
288/// transaction::Transaction,
289/// };
290/// use solana_system_interface::{instruction, program};
291/// use anyhow::Result;
292///
293/// fn create_account(
294/// client: &RpcClient,
295/// payer: &Keypair,
296/// new_account: &Keypair,
297/// space: u64,
298/// ) -> Result<()> {
299/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?;
300/// let instr = instruction::create_account(
301/// &payer.pubkey(),
302/// &new_account.pubkey(),
303/// rent,
304/// space,
305/// &program::ID,
306/// );
307///
308/// let blockhash = client.get_latest_blockhash()?;
309/// let tx = Transaction::new_signed_with_payer(
310/// &[instr],
311/// Some(&payer.pubkey()),
312/// &[payer, new_account],
313/// blockhash,
314/// );
315///
316/// let _sig = client.send_and_confirm_transaction(&tx)?;
317///
318/// Ok(())
319/// }
320/// # let payer = Keypair::new();
321/// # let new_account = Keypair::new();
322/// # let client = RpcClient::new(String::new());
323/// # create_account(&client, &payer, &new_account, 0);
324/// #
325/// # Ok::<(), anyhow::Error>(())
326/// ```
327///
328/// ## Example: on-chain program
329///
330/// This example submits the instruction from an on-chain Solana program. The
331/// created account is a [program derived address][pda]. The `payer` and
332/// `new_account_pda` are signers, with `new_account_pda` being signed for
333/// virtually by the program itself via [`invoke_signed`], `payer` being signed
334/// for by the client that submitted the transaction.
335///
336/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address
337/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html
338///
339/// ```
340/// use borsh::{BorshDeserialize, BorshSerialize};
341/// use solana_account_info::{next_account_info, AccountInfo};
342/// use solana_cpi::invoke_signed;
343/// use solana_program_entrypoint::entrypoint;
344/// use solana_program_error::ProgramResult;
345/// use solana_pubkey::Pubkey;
346/// use solana_system_interface::{instruction, program};
347/// use solana_sysvar::{rent::Rent, Sysvar};
348///
349/// #[derive(BorshSerialize, BorshDeserialize, Debug)]
350/// pub struct CreateAccountInstruction {
351/// /// The PDA seed used to distinguish the new account from other PDAs
352/// pub new_account_seed: [u8; 16],
353/// /// The PDA bump seed
354/// pub new_account_bump_seed: u8,
355/// /// The amount of space to allocate for `new_account_pda`
356/// pub space: u64,
357/// }
358///
359/// entrypoint!(process_instruction);
360///
361/// fn process_instruction(
362/// program_id: &Pubkey,
363/// accounts: &[AccountInfo],
364/// instruction_data: &[u8],
365/// ) -> ProgramResult {
366/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?;
367///
368/// let account_info_iter = &mut accounts.iter();
369///
370/// let payer = next_account_info(account_info_iter)?;
371/// let new_account_pda = next_account_info(account_info_iter)?;
372/// let system_account = next_account_info(account_info_iter)?;
373///
374/// assert!(payer.is_signer);
375/// assert!(payer.is_writable);
376/// // Note that `new_account_pda` is not a signer yet.
377/// // This program will sign for it via `invoke_signed`.
378/// assert!(!new_account_pda.is_signer);
379/// assert!(new_account_pda.is_writable);
380/// assert!(program::check_id(system_account.key));
381///
382/// let new_account_seed = &instr.new_account_seed;
383/// let new_account_bump_seed = instr.new_account_bump_seed;
384///
385/// let rent = Rent::get()?
386/// .minimum_balance(instr.space.try_into().expect("overflow"));
387///
388/// invoke_signed(
389/// &instruction::create_account(
390/// payer.key,
391/// new_account_pda.key,
392/// rent,
393/// instr.space,
394/// &program::ID
395/// ),
396/// &[payer.clone(), new_account_pda.clone()],
397/// &[&[
398/// payer.key.as_ref(),
399/// new_account_seed,
400/// &[new_account_bump_seed],
401/// ]],
402/// )?;
403///
404/// Ok(())
405/// }
406/// ```
407#[cfg(feature = "bincode")]
408pub fn create_account(
409 from_pubkey: &Pubkey,
410 to_pubkey: &Pubkey,
411 lamports: u64,
412 space: u64,
413 owner: &Pubkey,
414) -> Instruction {
415 let account_metas = vec![
416 AccountMeta::new(*from_pubkey, true),
417 AccountMeta::new(*to_pubkey, true),
418 ];
419 Instruction::new_with_bincode(
420 ID,
421 &SystemInstruction::CreateAccount {
422 lamports,
423 space,
424 owner: *owner,
425 },
426 account_metas,
427 )
428}
429
430// we accept `to` as a parameter so that callers do their own error handling when
431// calling create_with_seed()
432#[cfg(feature = "bincode")]
433pub fn create_account_with_seed(
434 from_pubkey: &Pubkey,
435 to_pubkey: &Pubkey, // must match create_with_seed(base, seed, owner)
436 base: &Pubkey,
437 seed: &str,
438 lamports: u64,
439 space: u64,
440 owner: &Pubkey,
441) -> Instruction {
442 let account_metas = vec![
443 AccountMeta::new(*from_pubkey, true),
444 AccountMeta::new(*to_pubkey, false),
445 AccountMeta::new_readonly(*base, true),
446 ];
447
448 Instruction::new_with_bincode(
449 ID,
450 &SystemInstruction::CreateAccountWithSeed {
451 base: *base,
452 seed: seed.to_string(),
453 lamports,
454 space,
455 owner: *owner,
456 },
457 account_metas,
458 )
459}
460
461/// Assign ownership of an account from the system program.
462///
463/// This function produces an [`Instruction`] which must be submitted in a
464/// [`Transaction`] or [invoked] to take effect, containing a serialized
465/// [`SystemInstruction::Assign`].
466///
467/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html
468/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
469///
470/// # Required signers
471///
472/// The `pubkey` signer must sign the transaction.
473///
474/// # Examples
475///
476/// These examples allocate space for an account, transfer it the minimum
477/// balance for rent exemption, and assign the account to a program.
478///
479/// ## Example: client-side RPC
480///
481/// This example submits the instructions from an RPC client.
482/// It assigns the account to a provided program account.
483/// The `payer` and `new_account` are signers.
484///
485/// ```
486/// # use solana_example_mocks::{solana_sdk, solana_rpc_client};
487/// use solana_rpc_client::rpc_client::RpcClient;
488/// use solana_pubkey::Pubkey;
489/// use solana_sdk::{
490/// signature::{Keypair, Signer},
491/// transaction::Transaction,
492/// };
493/// use solana_system_interface::instruction;
494/// use anyhow::Result;
495///
496/// fn create_account(
497/// client: &RpcClient,
498/// payer: &Keypair,
499/// new_account: &Keypair,
500/// owning_program: &Pubkey,
501/// space: u64,
502/// ) -> Result<()> {
503/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?;
504///
505/// let transfer_instr = instruction::transfer(
506/// &payer.pubkey(),
507/// &new_account.pubkey(),
508/// rent,
509/// );
510///
511/// let allocate_instr = instruction::allocate(
512/// &new_account.pubkey(),
513/// space,
514/// );
515///
516/// let assign_instr = instruction::assign(
517/// &new_account.pubkey(),
518/// owning_program,
519/// );
520///
521/// let blockhash = client.get_latest_blockhash()?;
522/// let tx = Transaction::new_signed_with_payer(
523/// &[transfer_instr, allocate_instr, assign_instr],
524/// Some(&payer.pubkey()),
525/// &[payer, new_account],
526/// blockhash,
527/// );
528///
529/// let _sig = client.send_and_confirm_transaction(&tx)?;
530///
531/// Ok(())
532/// }
533/// # let client = RpcClient::new(String::new());
534/// # let payer = Keypair::new();
535/// # let new_account = Keypair::new();
536/// # let owning_program = Pubkey::new_unique();
537/// # create_account(&client, &payer, &new_account, &owning_program, 1);
538/// #
539/// # Ok::<(), anyhow::Error>(())
540/// ```
541///
542/// ## Example: on-chain program
543///
544/// This example submits the instructions from an on-chain Solana program. The
545/// created account is a [program derived address][pda], funded by `payer`, and
546/// assigned to the running program. The `payer` and `new_account_pda` are
547/// signers, with `new_account_pda` being signed for virtually by the program
548/// itself via [`invoke_signed`], `payer` being signed for by the client that
549/// submitted the transaction.
550///
551/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address
552/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html
553///
554/// ```
555/// use borsh::{BorshDeserialize, BorshSerialize};
556/// use solana_account_info::{next_account_info, AccountInfo};
557/// use solana_cpi::invoke_signed;
558/// use solana_program_entrypoint::entrypoint;
559/// use solana_program_error::ProgramResult;
560/// use solana_pubkey::Pubkey;
561/// use solana_system_interface::{instruction, program};
562/// use solana_sysvar::{rent::Rent, Sysvar};
563///
564/// #[derive(BorshSerialize, BorshDeserialize, Debug)]
565/// pub struct CreateAccountInstruction {
566/// /// The PDA seed used to distinguish the new account from other PDAs
567/// pub new_account_seed: [u8; 16],
568/// /// The PDA bump seed
569/// pub new_account_bump_seed: u8,
570/// /// The amount of space to allocate for `new_account_pda`
571/// pub space: u64,
572/// }
573///
574/// entrypoint!(process_instruction);
575///
576/// fn process_instruction(
577/// program_id: &Pubkey,
578/// accounts: &[AccountInfo],
579/// instruction_data: &[u8],
580/// ) -> ProgramResult {
581/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?;
582///
583/// let account_info_iter = &mut accounts.iter();
584///
585/// let payer = next_account_info(account_info_iter)?;
586/// let new_account_pda = next_account_info(account_info_iter)?;
587/// let system_account = next_account_info(account_info_iter)?;
588///
589/// assert!(payer.is_signer);
590/// assert!(payer.is_writable);
591/// // Note that `new_account_pda` is not a signer yet.
592/// // This program will sign for it via `invoke_signed`.
593/// assert!(!new_account_pda.is_signer);
594/// assert!(new_account_pda.is_writable);
595/// assert!(program::check_id(system_account.key));
596///
597/// let new_account_seed = &instr.new_account_seed;
598/// let new_account_bump_seed = instr.new_account_bump_seed;
599///
600/// let rent = Rent::get()?
601/// .minimum_balance(instr.space.try_into().expect("overflow"));
602///
603/// invoke_signed(
604/// &instruction::create_account(
605/// payer.key,
606/// new_account_pda.key,
607/// rent,
608/// instr.space,
609/// &program::ID
610/// ),
611/// &[payer.clone(), new_account_pda.clone()],
612/// &[&[
613/// payer.key.as_ref(),
614/// new_account_seed,
615/// &[new_account_bump_seed],
616/// ]],
617/// )?;
618///
619/// Ok(())
620/// }
621/// ```
622#[cfg(feature = "bincode")]
623pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction {
624 let account_metas = vec![AccountMeta::new(*pubkey, true)];
625 Instruction::new_with_bincode(
626 ID,
627 &SystemInstruction::Assign { owner: *owner },
628 account_metas,
629 )
630}
631
632#[cfg(feature = "bincode")]
633pub fn assign_with_seed(
634 address: &Pubkey, // must match create_with_seed(base, seed, owner)
635 base: &Pubkey,
636 seed: &str,
637 owner: &Pubkey,
638) -> Instruction {
639 let account_metas = vec![
640 AccountMeta::new(*address, false),
641 AccountMeta::new_readonly(*base, true),
642 ];
643 Instruction::new_with_bincode(
644 ID,
645 &SystemInstruction::AssignWithSeed {
646 base: *base,
647 seed: seed.to_string(),
648 owner: *owner,
649 },
650 account_metas,
651 )
652}
653
654/// Transfer lamports from an account owned by the system program.
655///
656/// This function produces an [`Instruction`] which must be submitted in a
657/// [`Transaction`] or [invoked] to take effect, containing a serialized
658/// [`SystemInstruction::Transfer`].
659///
660/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html
661/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
662///
663/// # Required signers
664///
665/// The `from_pubkey` signer must sign the transaction.
666///
667/// # Examples
668///
669/// These examples allocate space for an account, transfer it the minimum
670/// balance for rent exemption, and assign the account to a program.
671///
672/// # Example: client-side RPC
673///
674/// This example submits the instructions from an RPC client.
675/// It assigns the account to a provided program account.
676/// The `payer` and `new_account` are signers.
677///
678/// ```
679/// # use solana_example_mocks::{solana_sdk, solana_rpc_client};
680/// use solana_rpc_client::rpc_client::RpcClient;
681/// use solana_pubkey::Pubkey;
682/// use solana_sdk::{
683/// signature::{Keypair, Signer},
684/// transaction::Transaction,
685/// };
686/// use solana_system_interface::instruction;
687/// use anyhow::Result;
688///
689/// fn create_account(
690/// client: &RpcClient,
691/// payer: &Keypair,
692/// new_account: &Keypair,
693/// owning_program: &Pubkey,
694/// space: u64,
695/// ) -> Result<()> {
696/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?;
697///
698/// let transfer_instr = instruction::transfer(
699/// &payer.pubkey(),
700/// &new_account.pubkey(),
701/// rent,
702/// );
703///
704/// let allocate_instr = instruction::allocate(
705/// &new_account.pubkey(),
706/// space,
707/// );
708///
709/// let assign_instr = instruction::assign(
710/// &new_account.pubkey(),
711/// owning_program,
712/// );
713///
714/// let blockhash = client.get_latest_blockhash()?;
715/// let tx = Transaction::new_signed_with_payer(
716/// &[transfer_instr, allocate_instr, assign_instr],
717/// Some(&payer.pubkey()),
718/// &[payer, new_account],
719/// blockhash,
720/// );
721///
722/// let _sig = client.send_and_confirm_transaction(&tx)?;
723///
724/// Ok(())
725/// }
726/// # let client = RpcClient::new(String::new());
727/// # let payer = Keypair::new();
728/// # let new_account = Keypair::new();
729/// # let owning_program = Pubkey::new_unique();
730/// # create_account(&client, &payer, &new_account, &owning_program, 1);
731/// #
732/// # Ok::<(), anyhow::Error>(())
733/// ```
734///
735/// ## Example: on-chain program
736///
737/// This example submits the instructions from an on-chain Solana program. The
738/// created account is a [program derived address][pda], funded by `payer`, and
739/// assigned to the running program. The `payer` and `new_account_pda` are
740/// signers, with `new_account_pda` being signed for virtually by the program
741/// itself via [`invoke_signed`], `payer` being signed for by the client that
742/// submitted the transaction.
743///
744/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address
745/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html
746///
747/// ```
748/// # use borsh::{BorshDeserialize, BorshSerialize};
749/// use solana_account_info::{next_account_info, AccountInfo};
750/// use solana_cpi::invoke_signed;
751/// use solana_program_entrypoint::entrypoint;
752/// use solana_program_error::ProgramResult;
753/// use solana_pubkey::Pubkey;
754/// use solana_system_interface::{instruction, program};
755/// use solana_sysvar::{rent::Rent, Sysvar};
756///
757/// #[derive(BorshSerialize, BorshDeserialize, Debug)]
758/// # #[borsh(crate = "borsh")]
759/// pub struct CreateAccountInstruction {
760/// /// The PDA seed used to distinguish the new account from other PDAs
761/// pub new_account_seed: [u8; 16],
762/// /// The PDA bump seed
763/// pub new_account_bump_seed: u8,
764/// /// The amount of space to allocate for `new_account_pda`
765/// pub space: u64,
766/// }
767///
768/// entrypoint!(process_instruction);
769///
770/// fn process_instruction(
771/// program_id: &Pubkey,
772/// accounts: &[AccountInfo],
773/// instruction_data: &[u8],
774/// ) -> ProgramResult {
775/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?;
776///
777/// let account_info_iter = &mut accounts.iter();
778///
779/// let payer = next_account_info(account_info_iter)?;
780/// let new_account_pda = next_account_info(account_info_iter)?;
781/// let system_account = next_account_info(account_info_iter)?;
782///
783/// assert!(payer.is_signer);
784/// assert!(payer.is_writable);
785/// // Note that `new_account_pda` is not a signer yet.
786/// // This program will sign for it via `invoke_signed`.
787/// assert!(!new_account_pda.is_signer);
788/// assert!(new_account_pda.is_writable);
789/// assert!(program::check_id(system_account.key));
790///
791/// let new_account_seed = &instr.new_account_seed;
792/// let new_account_bump_seed = instr.new_account_bump_seed;
793///
794/// let rent = Rent::get()?
795/// .minimum_balance(instr.space.try_into().expect("overflow"));
796///
797/// invoke_signed(
798/// &instruction::create_account(
799/// payer.key,
800/// new_account_pda.key,
801/// rent,
802/// instr.space,
803/// &program::ID
804/// ),
805/// &[payer.clone(), new_account_pda.clone()],
806/// &[&[
807/// payer.key.as_ref(),
808/// new_account_seed,
809/// &[new_account_bump_seed],
810/// ]],
811/// )?;
812///
813/// Ok(())
814/// }
815/// ```
816#[cfg(feature = "bincode")]
817pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction {
818 let account_metas = vec![
819 AccountMeta::new(*from_pubkey, true),
820 AccountMeta::new(*to_pubkey, false),
821 ];
822 Instruction::new_with_bincode(ID, &SystemInstruction::Transfer { lamports }, account_metas)
823}
824
825#[cfg(feature = "bincode")]
826pub fn transfer_with_seed(
827 from_pubkey: &Pubkey, // must match create_with_seed(base, seed, owner)
828 from_base: &Pubkey,
829 from_seed: String,
830 from_owner: &Pubkey,
831 to_pubkey: &Pubkey,
832 lamports: u64,
833) -> Instruction {
834 let account_metas = vec![
835 AccountMeta::new(*from_pubkey, false),
836 AccountMeta::new_readonly(*from_base, true),
837 AccountMeta::new(*to_pubkey, false),
838 ];
839 Instruction::new_with_bincode(
840 ID,
841 &SystemInstruction::TransferWithSeed {
842 lamports,
843 from_seed,
844 from_owner: *from_owner,
845 },
846 account_metas,
847 )
848}
849
850/// Allocate space for an account.
851///
852/// This function produces an [`Instruction`] which must be submitted in a
853/// [`Transaction`] or [invoked] to take effect, containing a serialized
854/// [`SystemInstruction::Allocate`].
855///
856/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html
857/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
858///
859/// The transaction will fail if the account already has size greater than 0,
860/// or if the requested size is greater than [`super::MAX_PERMITTED_DATA_LENGTH`].
861///
862/// # Required signers
863///
864/// The `pubkey` signer must sign the transaction.
865///
866/// # Examples
867///
868/// These examples allocate space for an account, transfer it the minimum
869/// balance for rent exemption, and assign the account to a program.
870///
871/// # Example: client-side RPC
872///
873/// This example submits the instructions from an RPC client.
874/// It assigns the account to a provided program account.
875/// The `payer` and `new_account` are signers.
876///
877/// ```
878/// # use solana_example_mocks::{solana_sdk, solana_rpc_client};
879/// use solana_rpc_client::rpc_client::RpcClient;
880/// use solana_pubkey::Pubkey;
881/// use solana_sdk::{
882/// signature::{Keypair, Signer},
883/// transaction::Transaction,
884/// };
885/// use solana_system_interface::instruction;
886/// use anyhow::Result;
887///
888/// fn create_account(
889/// client: &RpcClient,
890/// payer: &Keypair,
891/// new_account: &Keypair,
892/// owning_program: &Pubkey,
893/// space: u64,
894/// ) -> Result<()> {
895/// let rent = client.get_minimum_balance_for_rent_exemption(space.try_into()?)?;
896///
897/// let transfer_instr = instruction::transfer(
898/// &payer.pubkey(),
899/// &new_account.pubkey(),
900/// rent,
901/// );
902///
903/// let allocate_instr = instruction::allocate(
904/// &new_account.pubkey(),
905/// space,
906/// );
907///
908/// let assign_instr = instruction::assign(
909/// &new_account.pubkey(),
910/// owning_program,
911/// );
912///
913/// let blockhash = client.get_latest_blockhash()?;
914/// let tx = Transaction::new_signed_with_payer(
915/// &[transfer_instr, allocate_instr, assign_instr],
916/// Some(&payer.pubkey()),
917/// &[payer, new_account],
918/// blockhash,
919/// );
920///
921/// let _sig = client.send_and_confirm_transaction(&tx)?;
922///
923/// Ok(())
924/// }
925/// # let client = RpcClient::new(String::new());
926/// # let payer = Keypair::new();
927/// # let new_account = Keypair::new();
928/// # let owning_program = Pubkey::new_unique();
929/// # create_account(&client, &payer, &new_account, &owning_program, 1);
930/// #
931/// # Ok::<(), anyhow::Error>(())
932/// ```
933///
934/// ## Example: on-chain program
935///
936/// This example submits the instructions from an on-chain Solana program. The
937/// created account is a [program derived address][pda], funded by `payer`, and
938/// assigned to the running program. The `payer` and `new_account_pda` are
939/// signers, with `new_account_pda` being signed for virtually by the program
940/// itself via [`invoke_signed`], `payer` being signed for by the client that
941/// submitted the transaction.
942///
943/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address
944/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html
945///
946/// ```
947/// use borsh::{BorshDeserialize, BorshSerialize};
948/// use solana_account_info::{next_account_info, AccountInfo};
949/// use solana_cpi::invoke_signed;
950/// use solana_program_entrypoint::entrypoint;
951/// use solana_program_error::ProgramResult;
952/// use solana_pubkey::Pubkey;
953/// use solana_system_interface::{instruction, program};
954/// use solana_sysvar::{rent::Rent, Sysvar};
955///
956/// #[derive(BorshSerialize, BorshDeserialize, Debug)]
957/// pub struct CreateAccountInstruction {
958/// /// The PDA seed used to distinguish the new account from other PDAs
959/// pub new_account_seed: [u8; 16],
960/// /// The PDA bump seed
961/// pub new_account_bump_seed: u8,
962/// /// The amount of space to allocate for `new_account_pda`
963/// pub space: u64,
964/// }
965///
966/// entrypoint!(process_instruction);
967///
968/// fn process_instruction(
969/// program_id: &Pubkey,
970/// accounts: &[AccountInfo],
971/// instruction_data: &[u8],
972/// ) -> ProgramResult {
973/// let instr = CreateAccountInstruction::deserialize(&mut &instruction_data[..])?;
974///
975/// let account_info_iter = &mut accounts.iter();
976///
977/// let payer = next_account_info(account_info_iter)?;
978/// let new_account_pda = next_account_info(account_info_iter)?;
979/// let system_account = next_account_info(account_info_iter)?;
980///
981/// assert!(payer.is_signer);
982/// assert!(payer.is_writable);
983/// // Note that `new_account_pda` is not a signer yet.
984/// // This program will sign for it via `invoke_signed`.
985/// assert!(!new_account_pda.is_signer);
986/// assert!(new_account_pda.is_writable);
987/// assert!(program::check_id(system_account.key));
988///
989/// let new_account_seed = &instr.new_account_seed;
990/// let new_account_bump_seed = instr.new_account_bump_seed;
991///
992/// let rent = Rent::get()?
993/// .minimum_balance(instr.space.try_into().expect("overflow"));
994///
995/// invoke_signed(
996/// &instruction::create_account(
997/// payer.key,
998/// new_account_pda.key,
999/// rent,
1000/// instr.space,
1001/// &program::ID
1002/// ),
1003/// &[payer.clone(), new_account_pda.clone()],
1004/// &[&[
1005/// payer.key.as_ref(),
1006/// new_account_seed,
1007/// &[new_account_bump_seed],
1008/// ]],
1009/// )?;
1010///
1011/// Ok(())
1012/// }
1013/// ```
1014#[cfg(feature = "bincode")]
1015pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction {
1016 let account_metas = vec![AccountMeta::new(*pubkey, true)];
1017 Instruction::new_with_bincode(ID, &SystemInstruction::Allocate { space }, account_metas)
1018}
1019
1020#[cfg(feature = "bincode")]
1021pub fn allocate_with_seed(
1022 address: &Pubkey, // must match create_with_seed(base, seed, owner)
1023 base: &Pubkey,
1024 seed: &str,
1025 space: u64,
1026 owner: &Pubkey,
1027) -> Instruction {
1028 let account_metas = vec![
1029 AccountMeta::new(*address, false),
1030 AccountMeta::new_readonly(*base, true),
1031 ];
1032 Instruction::new_with_bincode(
1033 ID,
1034 &SystemInstruction::AllocateWithSeed {
1035 base: *base,
1036 seed: seed.to_string(),
1037 space,
1038 owner: *owner,
1039 },
1040 account_metas,
1041 )
1042}
1043
1044/// Transfer lamports from an account owned by the system program to multiple accounts.
1045///
1046/// This function produces a vector of [`Instruction`]s which must be submitted
1047/// in a [`Transaction`] or [invoked] to take effect, containing serialized
1048/// [`SystemInstruction::Transfer`]s.
1049///
1050/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html
1051/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
1052///
1053/// # Required signers
1054///
1055/// The `from_pubkey` signer must sign the transaction.
1056///
1057/// # Examples
1058///
1059/// ## Example: client-side RPC
1060///
1061/// This example performs multiple transfers in a single transaction.
1062///
1063/// ```
1064/// # use solana_example_mocks::{solana_sdk, solana_rpc_client};
1065/// use solana_rpc_client::rpc_client::RpcClient;
1066/// use solana_pubkey::Pubkey;
1067/// use solana_sdk::{
1068/// signature::{Keypair, Signer},
1069/// transaction::Transaction,
1070/// };
1071/// use solana_system_interface::instruction;
1072/// use anyhow::Result;
1073///
1074/// fn transfer_lamports_to_many(
1075/// client: &RpcClient,
1076/// from: &Keypair,
1077/// to_and_amount: &[(Pubkey, u64)],
1078/// ) -> Result<()> {
1079/// let instrs = instruction::transfer_many(&from.pubkey(), to_and_amount);
1080///
1081/// let blockhash = client.get_latest_blockhash()?;
1082/// let tx = Transaction::new_signed_with_payer(
1083/// &instrs,
1084/// Some(&from.pubkey()),
1085/// &[from],
1086/// blockhash,
1087/// );
1088///
1089/// let _sig = client.send_and_confirm_transaction(&tx)?;
1090///
1091/// Ok(())
1092/// }
1093/// # let from = Keypair::new();
1094/// # let to_and_amount = vec![
1095/// # (Pubkey::new_unique(), 1_000),
1096/// # (Pubkey::new_unique(), 2_000),
1097/// # (Pubkey::new_unique(), 3_000),
1098/// # ];
1099/// # let client = RpcClient::new(String::new());
1100/// # transfer_lamports_to_many(&client, &from, &to_and_amount);
1101/// #
1102/// # Ok::<(), anyhow::Error>(())
1103/// ```
1104///
1105/// ## Example: on-chain program
1106///
1107/// This example makes multiple transfers out of a "bank" account,
1108/// a [program derived address][pda] owned by the calling program.
1109/// This example submits the instructions from an on-chain Solana program. The
1110/// created account is a [program derived address][pda], and it is assigned to
1111/// the running program. The `payer` and `new_account_pda` are signers, with
1112/// `new_account_pda` being signed for virtually by the program itself via
1113/// [`invoke_signed`], `payer` being signed for by the client that submitted the
1114/// transaction.
1115///
1116/// [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address
1117/// [`invoke_signed`]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke_signed.html
1118///
1119/// ```
1120/// # use borsh::{BorshDeserialize, BorshSerialize};
1121/// use solana_account_info::{next_account_info, next_account_infos, AccountInfo};
1122/// use solana_cpi::invoke_signed;
1123/// use solana_program_entrypoint::entrypoint;
1124/// use solana_program_error::ProgramResult;
1125/// use solana_pubkey::Pubkey;
1126/// use solana_system_interface::{instruction, program};
1127///
1128/// /// # Accounts
1129/// ///
1130/// /// - 0: bank_pda - writable
1131/// /// - 1: system_program - executable
1132/// /// - *: to - writable
1133/// # #[derive(BorshSerialize, BorshDeserialize, Debug)]
1134/// # #[borsh(crate = "borsh")]
1135/// pub struct TransferLamportsToManyInstruction {
1136/// pub bank_pda_bump_seed: u8,
1137/// pub amount_list: Vec<u64>,
1138/// }
1139///
1140/// entrypoint!(process_instruction);
1141///
1142/// fn process_instruction(
1143/// program_id: &Pubkey,
1144/// accounts: &[AccountInfo],
1145/// instruction_data: &[u8],
1146/// ) -> ProgramResult {
1147/// let instr = TransferLamportsToManyInstruction::deserialize(&mut &instruction_data[..])?;
1148///
1149/// let account_info_iter = &mut accounts.iter();
1150///
1151/// let bank_pda = next_account_info(account_info_iter)?;
1152/// let bank_pda_bump_seed = instr.bank_pda_bump_seed;
1153/// let system_account = next_account_info(account_info_iter)?;
1154///
1155/// assert!(program::check_id(system_account.key));
1156///
1157/// let to_accounts = next_account_infos(account_info_iter, account_info_iter.len())?;
1158///
1159/// for to_account in to_accounts {
1160/// assert!(to_account.is_writable);
1161/// // ... do other verification ...
1162/// }
1163///
1164/// let to_and_amount = to_accounts
1165/// .iter()
1166/// .zip(instr.amount_list.iter())
1167/// .map(|(to, amount)| (*to.key, *amount))
1168/// .collect::<Vec<(Pubkey, u64)>>();
1169///
1170/// let instrs = instruction::transfer_many(bank_pda.key, to_and_amount.as_ref());
1171///
1172/// for instr in instrs {
1173/// invoke_signed(&instr, accounts, &[&[b"bank", &[bank_pda_bump_seed]]])?;
1174/// }
1175///
1176/// Ok(())
1177/// }
1178/// ```
1179#[cfg(feature = "bincode")]
1180pub fn transfer_many(from_pubkey: &Pubkey, to_lamports: &[(Pubkey, u64)]) -> Vec<Instruction> {
1181 to_lamports
1182 .iter()
1183 .map(|(to_pubkey, lamports)| transfer(from_pubkey, to_pubkey, *lamports))
1184 .collect()
1185}
1186
1187#[cfg(feature = "bincode")]
1188pub fn create_nonce_account_with_seed(
1189 from_pubkey: &Pubkey,
1190 nonce_pubkey: &Pubkey,
1191 base: &Pubkey,
1192 seed: &str,
1193 authority: &Pubkey,
1194 lamports: u64,
1195) -> Vec<Instruction> {
1196 vec![
1197 create_account_with_seed(
1198 from_pubkey,
1199 nonce_pubkey,
1200 base,
1201 seed,
1202 lamports,
1203 NONCE_STATE_SIZE as u64,
1204 &ID,
1205 ),
1206 Instruction::new_with_bincode(
1207 ID,
1208 &SystemInstruction::InitializeNonceAccount(*authority),
1209 vec![
1210 AccountMeta::new(*nonce_pubkey, false),
1211 #[allow(deprecated)]
1212 AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false),
1213 AccountMeta::new_readonly(RENT_ID, false),
1214 ],
1215 ),
1216 ]
1217}
1218
1219/// Create an account containing a durable transaction nonce.
1220///
1221/// This function produces a vector of [`Instruction`]s which must be submitted
1222/// in a [`Transaction`] or [invoked] to take effect, containing a serialized
1223/// [`SystemInstruction::CreateAccount`] and
1224/// [`SystemInstruction::InitializeNonceAccount`].
1225///
1226/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html
1227/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
1228///
1229/// A [durable transaction nonce][dtn] is a special account that enables
1230/// execution of transactions that have been signed in the past.
1231///
1232/// Standard Solana transactions include a [recent blockhash][rbh] (sometimes
1233/// referred to as a _[nonce]_). During execution the Solana runtime verifies
1234/// the recent blockhash is approximately less than two minutes old, and that in
1235/// those two minutes no other identical transaction with the same blockhash has
1236/// been executed. These checks prevent accidental replay of transactions.
1237/// Consequently, it is not possible to sign a transaction, wait more than two
1238/// minutes, then successfully execute that transaction.
1239///
1240/// [dtn]: https://docs.solanalabs.com/implemented-proposals/durable-tx-nonces
1241/// [rbh]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#structfield.recent_blockhash
1242/// [nonce]: https://en.wikipedia.org/wiki/Cryptographic_nonce
1243///
1244/// Durable transaction nonces are an alternative to the standard recent
1245/// blockhash nonce. They are stored in accounts on chain, and every time they
1246/// are used their value is changed to a new value for their next use. The
1247/// runtime verifies that each durable nonce value is only used once, and there
1248/// are no restrictions on how "old" the nonce is. Because they are stored on
1249/// chain and require additional instructions to use, transacting with durable
1250/// transaction nonces is more expensive than with standard transactions.
1251///
1252/// The value of the durable nonce is itself a blockhash and is accessible via
1253/// the [`blockhash`] field of [`nonce::state::Data`], which is deserialized
1254/// from the nonce account data.
1255///
1256/// [`blockhash`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#structfield.recent_blockhash
1257/// [`nonce::state::Data`]: https://docs.rs/solana-nonce/latest/solana_nonce/state/struct.Data.html
1258///
1259/// The basic durable transaction nonce lifecycle is
1260///
1261/// 1) Create the nonce account with the `create_nonce_account` instruction.
1262/// 2) Submit specially-formed transactions that include the
1263/// [`advance_nonce_account`] instruction.
1264/// 3) Destroy the nonce account by withdrawing its lamports with the
1265/// [`withdraw_nonce_account`] instruction.
1266///
1267/// Nonce accounts have an associated _authority_ account, which is stored in
1268/// their account data, and can be changed with the [`authorize_nonce_account`]
1269/// instruction. The authority must sign transactions that include the
1270/// `advance_nonce_account`, `authorize_nonce_account` and
1271/// `withdraw_nonce_account` instructions.
1272///
1273/// Nonce accounts are owned by the system program.
1274///
1275/// This constructor creates a [`SystemInstruction::CreateAccount`] instruction
1276/// and a [`SystemInstruction::InitializeNonceAccount`] instruction.
1277///
1278/// # Required signers
1279///
1280/// The `from_pubkey` and `nonce_pubkey` signers must sign the transaction.
1281///
1282/// # Examples
1283///
1284/// Create a nonce account from an off-chain client:
1285///
1286/// ```
1287/// # use solana_example_mocks::solana_keypair;
1288/// # use solana_example_mocks::solana_signer;
1289/// # use solana_example_mocks::solana_rpc_client;
1290/// # use solana_example_mocks::solana_transaction;
1291/// use solana_keypair::Keypair;
1292/// use solana_nonce::state::State;
1293/// use solana_rpc_client::rpc_client::RpcClient;
1294/// use solana_signer::Signer;
1295/// use solana_system_interface::instruction;
1296/// use solana_transaction::Transaction;
1297/// use anyhow::Result;
1298///
1299/// fn submit_create_nonce_account_tx(
1300/// client: &RpcClient,
1301/// payer: &Keypair,
1302/// ) -> Result<()> {
1303///
1304/// let nonce_account = Keypair::new();
1305///
1306/// let nonce_rent = client.get_minimum_balance_for_rent_exemption(State::size())?;
1307/// let instr = instruction::create_nonce_account(
1308/// &payer.pubkey(),
1309/// &nonce_account.pubkey(),
1310/// &payer.pubkey(), // Make the fee payer the nonce account authority
1311/// nonce_rent,
1312/// );
1313///
1314/// let mut tx = Transaction::new_with_payer(&instr, Some(&payer.pubkey()));
1315///
1316/// let blockhash = client.get_latest_blockhash()?;
1317/// tx.try_sign(&[&nonce_account, payer], blockhash)?;
1318///
1319/// client.send_and_confirm_transaction(&tx)?;
1320///
1321/// Ok(())
1322/// }
1323/// #
1324/// # let client = RpcClient::new(String::new());
1325/// # let payer = Keypair::new();
1326/// # submit_create_nonce_account_tx(&client, &payer)?;
1327/// #
1328/// # Ok::<(), anyhow::Error>(())
1329/// ```
1330#[cfg(feature = "bincode")]
1331pub fn create_nonce_account(
1332 from_pubkey: &Pubkey,
1333 nonce_pubkey: &Pubkey,
1334 authority: &Pubkey,
1335 lamports: u64,
1336) -> Vec<Instruction> {
1337 vec![
1338 create_account(
1339 from_pubkey,
1340 nonce_pubkey,
1341 lamports,
1342 NONCE_STATE_SIZE as u64,
1343 &ID,
1344 ),
1345 Instruction::new_with_bincode(
1346 ID,
1347 &SystemInstruction::InitializeNonceAccount(*authority),
1348 vec![
1349 AccountMeta::new(*nonce_pubkey, false),
1350 #[allow(deprecated)]
1351 AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false),
1352 AccountMeta::new_readonly(RENT_ID, false),
1353 ],
1354 ),
1355 ]
1356}
1357
1358/// Advance the value of a durable transaction nonce.
1359///
1360/// This function produces an [`Instruction`] which must be submitted in a
1361/// [`Transaction`] or [invoked] to take effect, containing a serialized
1362/// [`SystemInstruction::AdvanceNonceAccount`].
1363///
1364/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html
1365/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
1366///
1367/// Every transaction that relies on a durable transaction nonce must contain a
1368/// [`SystemInstruction::AdvanceNonceAccount`] instruction as the first
1369/// instruction in the [`Message`], as created by this function. When included
1370/// in the first position, the Solana runtime recognizes the transaction as one
1371/// that relies on a durable transaction nonce and processes it accordingly. The
1372/// [`Message::new_with_nonce`] function can be used to construct a `Message` in
1373/// the correct format without calling `advance_nonce_account` directly.
1374///
1375/// When constructing a transaction that includes an `AdvanceNonceInstruction`
1376/// the [`recent_blockhash`] must be treated differently — instead of
1377/// setting it to a recent blockhash, the value of the nonce must be retrieved
1378/// and deserialized from the nonce account, and that value specified as the
1379/// "recent blockhash". A nonce account can be deserialized with the
1380/// [`solana_rpc_client_nonce_utils::data_from_account`][dfa] function.
1381///
1382/// For further description of durable transaction nonces see
1383/// [`create_nonce_account`].
1384///
1385/// [`Message`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html
1386/// [`Message::new_with_nonce`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#method.new_with_nonce
1387/// [`recent_blockhash`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#structfield.recent_blockhash
1388/// [dfa]: https://docs.rs/solana-rpc-client-nonce-utils/latest/solana_rpc_client_nonce_utils/fn.data_from_account.html
1389///
1390/// # Required signers
1391///
1392/// The `authorized_pubkey` signer must sign the transaction.
1393///
1394/// # Examples
1395///
1396/// Create and sign a transaction with a durable nonce:
1397///
1398/// ```
1399/// # use solana_example_mocks::solana_sdk;
1400/// # use solana_example_mocks::solana_rpc_client;
1401/// # use solana_example_mocks::solana_rpc_client_nonce_utils;
1402/// # use solana_sdk::account::Account;
1403/// use solana_rpc_client::rpc_client::RpcClient;
1404/// use solana_pubkey::Pubkey;
1405/// use solana_sdk::{
1406/// message::Message,
1407/// signature::{Keypair, Signer},
1408/// transaction::Transaction,
1409/// };
1410/// use solana_system_interface::instruction;
1411/// use std::path::Path;
1412/// use anyhow::Result;
1413///
1414/// fn create_transfer_tx_with_nonce(
1415/// client: &RpcClient,
1416/// nonce_account_pubkey: &Pubkey,
1417/// payer: &Keypair,
1418/// receiver: &Pubkey,
1419/// amount: u64,
1420/// tx_path: &Path,
1421/// ) -> Result<()> {
1422///
1423/// let instr_transfer = instruction::transfer(
1424/// &payer.pubkey(),
1425/// receiver,
1426/// amount,
1427/// );
1428///
1429/// // In this example, `payer` is `nonce_account_pubkey`'s authority
1430/// let instr_advance_nonce_account = instruction::advance_nonce_account(
1431/// nonce_account_pubkey,
1432/// &payer.pubkey(),
1433/// );
1434///
1435/// // The `advance_nonce_account` instruction must be the first issued in
1436/// // the transaction.
1437/// let message = Message::new(
1438/// &[
1439/// instr_advance_nonce_account,
1440/// instr_transfer
1441/// ],
1442/// Some(&payer.pubkey()),
1443/// );
1444///
1445/// let mut tx = Transaction::new_unsigned(message);
1446///
1447/// // Sign the tx with nonce_account's `blockhash` instead of the
1448/// // network's latest blockhash.
1449/// # client.set_get_account_response(*nonce_account_pubkey, Account {
1450/// # lamports: 1,
1451/// # data: vec![0],
1452/// # owner: solana_sdk::system_program::ID,
1453/// # executable: false,
1454/// # });
1455/// let nonce_account = client.get_account(nonce_account_pubkey)?;
1456/// let nonce_data = solana_rpc_client_nonce_utils::data_from_account(&nonce_account)?;
1457/// let blockhash = nonce_data.blockhash();
1458///
1459/// tx.try_sign(&[payer], blockhash)?;
1460///
1461/// // Save the signed transaction locally for later submission.
1462/// save_tx_to_file(&tx_path, &tx)?;
1463///
1464/// Ok(())
1465/// }
1466/// #
1467/// # fn save_tx_to_file(path: &Path, tx: &Transaction) -> Result<()> {
1468/// # Ok(())
1469/// # }
1470/// #
1471/// # let client = RpcClient::new(String::new());
1472/// # let nonce_account_pubkey = Pubkey::new_unique();
1473/// # let payer = Keypair::new();
1474/// # let receiver = Pubkey::new_unique();
1475/// # create_transfer_tx_with_nonce(&client, &nonce_account_pubkey, &payer, &receiver, 1024, Path::new("new_tx"))?;
1476/// #
1477/// # Ok::<(), anyhow::Error>(())
1478/// ```
1479#[cfg(feature = "bincode")]
1480pub fn advance_nonce_account(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction {
1481 let account_metas = vec![
1482 AccountMeta::new(*nonce_pubkey, false),
1483 #[allow(deprecated)]
1484 AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false),
1485 AccountMeta::new_readonly(*authorized_pubkey, true),
1486 ];
1487 Instruction::new_with_bincode(ID, &SystemInstruction::AdvanceNonceAccount, account_metas)
1488}
1489
1490/// Withdraw lamports from a durable transaction nonce account.
1491///
1492/// This function produces an [`Instruction`] which must be submitted in a
1493/// [`Transaction`] or [invoked] to take effect, containing a serialized
1494/// [`SystemInstruction::WithdrawNonceAccount`].
1495///
1496/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html
1497/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
1498///
1499/// Withdrawing the entire balance of a nonce account will cause the runtime to
1500/// destroy it upon successful completion of the transaction.
1501///
1502/// Otherwise, nonce accounts must maintain a balance greater than or equal to
1503/// the minimum required for [rent exemption]. If the result of this instruction
1504/// would leave the nonce account with a balance less than required for rent
1505/// exemption, but also greater than zero, then the transaction will fail.
1506///
1507/// [rent exemption]: https://solana.com/docs/core/accounts#rent-exemption
1508///
1509/// This constructor creates a [`SystemInstruction::WithdrawNonceAccount`]
1510/// instruction.
1511///
1512/// # Required signers
1513///
1514/// The `authorized_pubkey` signer must sign the transaction.
1515///
1516/// # Examples
1517///
1518/// ```
1519/// # use solana_example_mocks::solana_sdk;
1520/// # use solana_example_mocks::solana_rpc_client;
1521/// use solana_rpc_client::rpc_client::RpcClient;
1522/// use solana_pubkey::Pubkey;
1523/// use solana_sdk::{
1524/// signature::{Keypair, Signer},
1525/// transaction::Transaction,
1526/// };
1527/// use solana_system_interface::instruction;
1528/// use anyhow::Result;
1529///
1530/// fn submit_withdraw_nonce_account_tx(
1531/// client: &RpcClient,
1532/// nonce_account_pubkey: &Pubkey,
1533/// authorized_account: &Keypair,
1534/// ) -> Result<()> {
1535///
1536/// let nonce_balance = client.get_balance(nonce_account_pubkey)?;
1537///
1538/// let instr = instruction::withdraw_nonce_account(
1539/// nonce_account_pubkey,
1540/// &authorized_account.pubkey(),
1541/// &authorized_account.pubkey(),
1542/// nonce_balance,
1543/// );
1544///
1545/// let mut tx = Transaction::new_with_payer(&[instr], Some(&authorized_account.pubkey()));
1546///
1547/// let blockhash = client.get_latest_blockhash()?;
1548/// tx.try_sign(&[authorized_account], blockhash)?;
1549///
1550/// client.send_and_confirm_transaction(&tx)?;
1551///
1552/// Ok(())
1553/// }
1554/// #
1555/// # let client = RpcClient::new(String::new());
1556/// # let nonce_account_pubkey = Pubkey::new_unique();
1557/// # let payer = Keypair::new();
1558/// # submit_withdraw_nonce_account_tx(&client, &nonce_account_pubkey, &payer)?;
1559/// #
1560/// # Ok::<(), anyhow::Error>(())
1561/// ```
1562#[cfg(feature = "bincode")]
1563pub fn withdraw_nonce_account(
1564 nonce_pubkey: &Pubkey,
1565 authorized_pubkey: &Pubkey,
1566 to_pubkey: &Pubkey,
1567 lamports: u64,
1568) -> Instruction {
1569 let account_metas = vec![
1570 AccountMeta::new(*nonce_pubkey, false),
1571 AccountMeta::new(*to_pubkey, false),
1572 #[allow(deprecated)]
1573 AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false),
1574 AccountMeta::new_readonly(RENT_ID, false),
1575 AccountMeta::new_readonly(*authorized_pubkey, true),
1576 ];
1577 Instruction::new_with_bincode(
1578 ID,
1579 &SystemInstruction::WithdrawNonceAccount(lamports),
1580 account_metas,
1581 )
1582}
1583
1584/// Change the authority of a durable transaction nonce account.
1585///
1586/// This function produces an [`Instruction`] which must be submitted in a
1587/// [`Transaction`] or [invoked] to take effect, containing a serialized
1588/// [`SystemInstruction::AuthorizeNonceAccount`].
1589///
1590/// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html
1591/// [invoked]: https://docs.rs/solana-cpi/latest/solana_cpi/fn.invoke.html
1592///
1593/// This constructor creates a [`SystemInstruction::AuthorizeNonceAccount`]
1594/// instruction.
1595///
1596/// # Required signers
1597///
1598/// The `authorized_pubkey` signer must sign the transaction.
1599///
1600/// # Examples
1601///
1602/// ```
1603/// # use solana_example_mocks::solana_sdk;
1604/// # use solana_example_mocks::solana_rpc_client;
1605/// use solana_rpc_client::rpc_client::RpcClient;
1606/// use solana_pubkey::Pubkey;
1607/// use solana_sdk::{
1608/// signature::{Keypair, Signer},
1609/// transaction::Transaction,
1610/// };
1611/// use solana_system_interface::instruction;
1612/// use anyhow::Result;
1613///
1614/// fn authorize_nonce_account_tx(
1615/// client: &RpcClient,
1616/// nonce_account_pubkey: &Pubkey,
1617/// authorized_account: &Keypair,
1618/// new_authority_pubkey: &Pubkey,
1619/// ) -> Result<()> {
1620///
1621/// let instr = instruction::authorize_nonce_account(
1622/// nonce_account_pubkey,
1623/// &authorized_account.pubkey(),
1624/// new_authority_pubkey,
1625/// );
1626///
1627/// let mut tx = Transaction::new_with_payer(&[instr], Some(&authorized_account.pubkey()));
1628///
1629/// let blockhash = client.get_latest_blockhash()?;
1630/// tx.try_sign(&[authorized_account], blockhash)?;
1631///
1632/// client.send_and_confirm_transaction(&tx)?;
1633///
1634/// Ok(())
1635/// }
1636/// #
1637/// # let client = RpcClient::new(String::new());
1638/// # let nonce_account_pubkey = Pubkey::new_unique();
1639/// # let payer = Keypair::new();
1640/// # let new_authority_pubkey = Pubkey::new_unique();
1641/// # authorize_nonce_account_tx(&client, &nonce_account_pubkey, &payer, &new_authority_pubkey)?;
1642/// #
1643/// # Ok::<(), anyhow::Error>(())
1644/// ```
1645#[cfg(feature = "bincode")]
1646pub fn authorize_nonce_account(
1647 nonce_pubkey: &Pubkey,
1648 authorized_pubkey: &Pubkey,
1649 new_authority: &Pubkey,
1650) -> Instruction {
1651 let account_metas = vec![
1652 AccountMeta::new(*nonce_pubkey, false),
1653 AccountMeta::new_readonly(*authorized_pubkey, true),
1654 ];
1655 Instruction::new_with_bincode(
1656 ID,
1657 &SystemInstruction::AuthorizeNonceAccount(*new_authority),
1658 account_metas,
1659 )
1660}
1661
1662/// One-time idempotent upgrade of legacy nonce versions in order to bump
1663/// them out of chain blockhash domain.
1664#[cfg(feature = "bincode")]
1665pub fn upgrade_nonce_account(nonce_pubkey: Pubkey) -> Instruction {
1666 let account_metas = vec![AccountMeta::new(nonce_pubkey, /*is_signer:*/ false)];
1667 Instruction::new_with_bincode(ID, &SystemInstruction::UpgradeNonceAccount, account_metas)
1668}
1669
1670#[cfg(feature = "bincode")]
1671#[cfg(test)]
1672mod tests {
1673 use {super::*, solana_sysvar_id::SysvarId};
1674
1675 fn get_keys(instruction: &Instruction) -> Vec<Pubkey> {
1676 instruction.accounts.iter().map(|x| x.pubkey).collect()
1677 }
1678
1679 #[allow(deprecated)]
1680 #[test]
1681 fn test_constants() {
1682 // Ensure that the constants are in sync with the solana program.
1683 assert_eq!(
1684 RECENT_BLOCKHASHES_ID,
1685 solana_sysvar::recent_blockhashes::RecentBlockhashes::id(),
1686 );
1687
1688 // Ensure that the constants are in sync with the solana rent.
1689 assert_eq!(RENT_ID, solana_sysvar::rent::Rent::id());
1690 }
1691
1692 #[test]
1693 fn test_move_many() {
1694 let alice_pubkey = Pubkey::new_unique();
1695 let bob_pubkey = Pubkey::new_unique();
1696 let carol_pubkey = Pubkey::new_unique();
1697 let to_lamports = vec![(bob_pubkey, 1), (carol_pubkey, 2)];
1698
1699 let instructions = transfer_many(&alice_pubkey, &to_lamports);
1700 assert_eq!(instructions.len(), 2);
1701 assert_eq!(get_keys(&instructions[0]), vec![alice_pubkey, bob_pubkey]);
1702 assert_eq!(get_keys(&instructions[1]), vec![alice_pubkey, carol_pubkey]);
1703 }
1704
1705 #[test]
1706 fn test_create_nonce_account() {
1707 let from_pubkey = Pubkey::new_unique();
1708 let nonce_pubkey = Pubkey::new_unique();
1709 let authorized = nonce_pubkey;
1710 let ixs = create_nonce_account(&from_pubkey, &nonce_pubkey, &authorized, 42);
1711 assert_eq!(ixs.len(), 2);
1712 let ix = &ixs[0];
1713 assert_eq!(ix.program_id, crate::program::ID);
1714 let pubkeys: Vec<_> = ix.accounts.iter().map(|am| am.pubkey).collect();
1715 assert!(pubkeys.contains(&from_pubkey));
1716 assert!(pubkeys.contains(&nonce_pubkey));
1717 }
1718}