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