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