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