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