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 — 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}