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