gemachain_program/
system_instruction.rs

1#[allow(deprecated)]
2use crate::sysvar::recent_blockhashes;
3use crate::{
4    decode_error::DecodeError,
5    instruction::{AccountMeta, Instruction, InstructionError},
6    nonce,
7    pubkey::Pubkey,
8    system_program,
9    sysvar::rent,
10};
11use num_derive::{FromPrimitive, ToPrimitive};
12use thiserror::Error;
13
14#[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)]
15pub enum SystemError {
16    #[error("an account with the same address already exists")]
17    AccountAlreadyInUse,
18    #[error("account does not have enough GEMA to perform the operation")]
19    ResultWithNegativeCarats,
20    #[error("cannot assign account to this program id")]
21    InvalidProgramId,
22    #[error("cannot allocate account data of this length")]
23    InvalidAccountDataLength,
24    #[error("length of requested seed is too long")]
25    MaxSeedLengthExceeded,
26    #[error("provided address does not match addressed derived from seed")]
27    AddressWithSeedMismatch,
28    #[error("advancing stored nonce requires a populated RecentBlockhashes sysvar")]
29    NonceNoRecentBlockhashes,
30    #[error("stored nonce is still in recent_blockhashes")]
31    NonceBlockhashNotExpired,
32    #[error("specified nonce does not match stored nonce")]
33    NonceUnexpectedBlockhashValue,
34}
35
36impl<T> DecodeError<T> for SystemError {
37    fn type_of() -> &'static str {
38        "SystemError"
39    }
40}
41
42#[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
43pub enum NonceError {
44    #[error("recent blockhash list is empty")]
45    NoRecentBlockhashes,
46    #[error("stored nonce is still in recent_blockhashes")]
47    NotExpired,
48    #[error("specified nonce does not match stored nonce")]
49    UnexpectedValue,
50    #[error("cannot handle request in current account state")]
51    BadAccountState,
52}
53
54impl<E> DecodeError<E> for NonceError {
55    fn type_of() -> &'static str {
56        "NonceError"
57    }
58}
59
60#[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
61enum NonceErrorAdapter {
62    #[error("recent blockhash list is empty")]
63    NoRecentBlockhashes,
64    #[error("stored nonce is still in recent_blockhashes")]
65    NotExpired,
66    #[error("specified nonce does not match stored nonce")]
67    UnexpectedValue,
68    #[error("cannot handle request in current account state")]
69    BadAccountState,
70}
71
72impl<E> DecodeError<E> for NonceErrorAdapter {
73    fn type_of() -> &'static str {
74        "NonceErrorAdapter"
75    }
76}
77
78impl From<NonceErrorAdapter> for NonceError {
79    fn from(e: NonceErrorAdapter) -> Self {
80        match e {
81            NonceErrorAdapter::NoRecentBlockhashes => NonceError::NoRecentBlockhashes,
82            NonceErrorAdapter::NotExpired => NonceError::NotExpired,
83            NonceErrorAdapter::UnexpectedValue => NonceError::UnexpectedValue,
84            NonceErrorAdapter::BadAccountState => NonceError::BadAccountState,
85        }
86    }
87}
88
89pub fn nonce_to_instruction_error(error: NonceError, use_system_variant: bool) -> InstructionError {
90    if use_system_variant {
91        match error {
92            NonceError::NoRecentBlockhashes => SystemError::NonceNoRecentBlockhashes.into(),
93            NonceError::NotExpired => SystemError::NonceBlockhashNotExpired.into(),
94            NonceError::UnexpectedValue => SystemError::NonceUnexpectedBlockhashValue.into(),
95            NonceError::BadAccountState => InstructionError::InvalidAccountData,
96        }
97    } else {
98        match error {
99            NonceError::NoRecentBlockhashes => NonceErrorAdapter::NoRecentBlockhashes.into(),
100            NonceError::NotExpired => NonceErrorAdapter::NotExpired.into(),
101            NonceError::UnexpectedValue => NonceErrorAdapter::UnexpectedValue.into(),
102            NonceError::BadAccountState => NonceErrorAdapter::BadAccountState.into(),
103        }
104    }
105}
106
107pub fn instruction_to_nonce_error(
108    error: &InstructionError,
109    use_system_variant: bool,
110) -> Option<NonceError> {
111    if use_system_variant {
112        match error {
113            InstructionError::Custom(discriminant) => {
114                match SystemError::decode_custom_error_to_enum(*discriminant) {
115                    Some(SystemError::NonceNoRecentBlockhashes) => {
116                        Some(NonceError::NoRecentBlockhashes)
117                    }
118                    Some(SystemError::NonceBlockhashNotExpired) => Some(NonceError::NotExpired),
119                    Some(SystemError::NonceUnexpectedBlockhashValue) => {
120                        Some(NonceError::UnexpectedValue)
121                    }
122                    _ => None,
123                }
124            }
125            InstructionError::InvalidAccountData => Some(NonceError::BadAccountState),
126            _ => None,
127        }
128    } else if let InstructionError::Custom(discriminant) = error {
129        let maybe: Option<NonceErrorAdapter> =
130            NonceErrorAdapter::decode_custom_error_to_enum(*discriminant);
131        maybe.map(NonceError::from)
132    } else {
133        None
134    }
135}
136
137/// maximum permitted size of data: 10 MB
138pub const MAX_PERMITTED_DATA_LENGTH: u64 = 10 * 1024 * 1024;
139
140#[frozen_abi(digest = "2xnDcizcPKKR7b624FeuuPd1zj5bmnkmVsBWgoKPTh4w")]
141#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, AbiExample, AbiEnumVisitor)]
142pub enum SystemInstruction {
143    /// Create a new account
144    ///
145    /// # Account references
146    ///   0. `[WRITE, SIGNER]` Funding account
147    ///   1. `[WRITE, SIGNER]` New account
148    CreateAccount {
149        /// Number of carats to transfer to the new account
150        carats: u64,
151
152        /// Number of bytes of memory to allocate
153        space: u64,
154
155        /// Address of program that will own the new account
156        owner: Pubkey,
157    },
158
159    /// Assign account to a program
160    ///
161    /// # Account references
162    ///   0. `[WRITE, SIGNER]` Assigned account public key
163    Assign {
164        /// Owner program account
165        owner: Pubkey,
166    },
167
168    /// Transfer carats
169    ///
170    /// # Account references
171    ///   0. `[WRITE, SIGNER]` Funding account
172    ///   1. `[WRITE]` Recipient account
173    Transfer { carats: u64 },
174
175    /// Create a new account at an address derived from a base pubkey and a seed
176    ///
177    /// # Account references
178    ///   0. `[WRITE, SIGNER]` Funding account
179    ///   1. `[WRITE]` Created account
180    ///   2. `[SIGNER]` (optional) Base account; the account matching the base Pubkey below must be
181    ///                          provided as a signer, but may be the same as the funding account
182    ///                          and provided as account 0
183    CreateAccountWithSeed {
184        /// Base public key
185        base: Pubkey,
186
187        /// String of ASCII chars, no longer than `Pubkey::MAX_SEED_LEN`
188        seed: String,
189
190        /// Number of carats to transfer to the new account
191        carats: u64,
192
193        /// Number of bytes of memory to allocate
194        space: u64,
195
196        /// Owner program account address
197        owner: Pubkey,
198    },
199
200    /// Consumes a stored nonce, replacing it with a successor
201    ///
202    /// # Account references
203    ///   0. `[WRITE]` Nonce account
204    ///   1. `[]` RecentBlockhashes sysvar
205    ///   2. `[SIGNER]` Nonce authority
206    AdvanceNonceAccount,
207
208    /// Withdraw funds from a nonce account
209    ///
210    /// # Account references
211    ///   0. `[WRITE]` Nonce account
212    ///   1. `[WRITE]` Recipient account
213    ///   2. `[]` RecentBlockhashes sysvar
214    ///   3. `[]` Rent sysvar
215    ///   4. `[SIGNER]` Nonce authority
216    ///
217    /// The `u64` parameter is the carats to withdraw, which must leave the
218    /// account balance above the rent exempt reserve or at zero.
219    WithdrawNonceAccount(u64),
220
221    /// Drive state of Uninitalized nonce account to Initialized, setting the nonce value
222    ///
223    /// # Account references
224    ///   0. `[WRITE]` Nonce account
225    ///   1. `[]` RecentBlockhashes sysvar
226    ///   2. `[]` Rent sysvar
227    ///
228    /// The `Pubkey` parameter specifies the entity authorized to execute nonce
229    /// instruction on the account
230    ///
231    /// No signatures are required to execute this instruction, enabling derived
232    /// nonce account addresses
233    InitializeNonceAccount(Pubkey),
234
235    /// Change the entity authorized to execute nonce instructions on the account
236    ///
237    /// # Account references
238    ///   0. `[WRITE]` Nonce account
239    ///   1. `[SIGNER]` Nonce authority
240    ///
241    /// The `Pubkey` parameter identifies the entity to authorize
242    AuthorizeNonceAccount(Pubkey),
243
244    /// Allocate space in a (possibly new) account without funding
245    ///
246    /// # Account references
247    ///   0. `[WRITE, SIGNER]` New account
248    Allocate {
249        /// Number of bytes of memory to allocate
250        space: u64,
251    },
252
253    /// Allocate space for and assign an account at an address
254    ///    derived from a base public key and a seed
255    ///
256    /// # Account references
257    ///   0. `[WRITE]` Allocated account
258    ///   1. `[SIGNER]` Base account
259    AllocateWithSeed {
260        /// Base public key
261        base: Pubkey,
262
263        /// String of ASCII chars, no longer than `pubkey::MAX_SEED_LEN`
264        seed: String,
265
266        /// Number of bytes of memory to allocate
267        space: u64,
268
269        /// Owner program account
270        owner: Pubkey,
271    },
272
273    /// Assign account to a program based on a seed
274    ///
275    /// # Account references
276    ///   0. `[WRITE]` Assigned account
277    ///   1. `[SIGNER]` Base account
278    AssignWithSeed {
279        /// Base public key
280        base: Pubkey,
281
282        /// String of ASCII chars, no longer than `pubkey::MAX_SEED_LEN`
283        seed: String,
284
285        /// Owner program account
286        owner: Pubkey,
287    },
288
289    /// Transfer carats from a derived address
290    ///
291    /// # Account references
292    ///   0. `[WRITE]` Funding account
293    ///   1. `[SIGNER]` Base for funding account
294    ///   2. `[WRITE]` Recipient account
295    TransferWithSeed {
296        /// Amount to transfer
297        carats: u64,
298
299        /// Seed to use to derive the funding account address
300        from_seed: String,
301
302        /// Owner to use to derive the funding account address
303        from_owner: Pubkey,
304    },
305}
306
307pub fn create_account(
308    from_pubkey: &Pubkey,
309    to_pubkey: &Pubkey,
310    carats: u64,
311    space: u64,
312    owner: &Pubkey,
313) -> Instruction {
314    let account_metas = vec![
315        AccountMeta::new(*from_pubkey, true),
316        AccountMeta::new(*to_pubkey, true),
317    ];
318    Instruction::new_with_bincode(
319        system_program::id(),
320        &SystemInstruction::CreateAccount {
321            carats,
322            space,
323            owner: *owner,
324        },
325        account_metas,
326    )
327}
328
329// we accept `to` as a parameter so that callers do their own error handling when
330//   calling create_with_seed()
331pub fn create_account_with_seed(
332    from_pubkey: &Pubkey,
333    to_pubkey: &Pubkey, // must match create_with_seed(base, seed, owner)
334    base: &Pubkey,
335    seed: &str,
336    carats: u64,
337    space: u64,
338    owner: &Pubkey,
339) -> Instruction {
340    let account_metas = vec![
341        AccountMeta::new(*from_pubkey, true),
342        AccountMeta::new(*to_pubkey, false),
343        AccountMeta::new_readonly(*base, true),
344    ];
345
346    Instruction::new_with_bincode(
347        system_program::id(),
348        &SystemInstruction::CreateAccountWithSeed {
349            base: *base,
350            seed: seed.to_string(),
351            carats,
352            space,
353            owner: *owner,
354        },
355        account_metas,
356    )
357}
358
359pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction {
360    let account_metas = vec![AccountMeta::new(*pubkey, true)];
361    Instruction::new_with_bincode(
362        system_program::id(),
363        &SystemInstruction::Assign { owner: *owner },
364        account_metas,
365    )
366}
367
368pub fn assign_with_seed(
369    address: &Pubkey, // must match create_with_seed(base, seed, owner)
370    base: &Pubkey,
371    seed: &str,
372    owner: &Pubkey,
373) -> Instruction {
374    let account_metas = vec![
375        AccountMeta::new(*address, false),
376        AccountMeta::new_readonly(*base, true),
377    ];
378    Instruction::new_with_bincode(
379        system_program::id(),
380        &SystemInstruction::AssignWithSeed {
381            base: *base,
382            seed: seed.to_string(),
383            owner: *owner,
384        },
385        account_metas,
386    )
387}
388
389pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, carats: u64) -> Instruction {
390    let account_metas = vec![
391        AccountMeta::new(*from_pubkey, true),
392        AccountMeta::new(*to_pubkey, false),
393    ];
394    Instruction::new_with_bincode(
395        system_program::id(),
396        &SystemInstruction::Transfer { carats },
397        account_metas,
398    )
399}
400
401pub fn transfer_with_seed(
402    from_pubkey: &Pubkey, // must match create_with_seed(base, seed, owner)
403    from_base: &Pubkey,
404    from_seed: String,
405    from_owner: &Pubkey,
406    to_pubkey: &Pubkey,
407    carats: u64,
408) -> Instruction {
409    let account_metas = vec![
410        AccountMeta::new(*from_pubkey, false),
411        AccountMeta::new_readonly(*from_base, true),
412        AccountMeta::new(*to_pubkey, false),
413    ];
414    Instruction::new_with_bincode(
415        system_program::id(),
416        &SystemInstruction::TransferWithSeed {
417            carats,
418            from_seed,
419            from_owner: *from_owner,
420        },
421        account_metas,
422    )
423}
424
425pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction {
426    let account_metas = vec![AccountMeta::new(*pubkey, true)];
427    Instruction::new_with_bincode(
428        system_program::id(),
429        &SystemInstruction::Allocate { space },
430        account_metas,
431    )
432}
433
434pub fn allocate_with_seed(
435    address: &Pubkey, // must match create_with_seed(base, seed, owner)
436    base: &Pubkey,
437    seed: &str,
438    space: u64,
439    owner: &Pubkey,
440) -> Instruction {
441    let account_metas = vec![
442        AccountMeta::new(*address, false),
443        AccountMeta::new_readonly(*base, true),
444    ];
445    Instruction::new_with_bincode(
446        system_program::id(),
447        &SystemInstruction::AllocateWithSeed {
448            base: *base,
449            seed: seed.to_string(),
450            space,
451            owner: *owner,
452        },
453        account_metas,
454    )
455}
456
457/// Create and sign new SystemInstruction::Transfer transaction to many destinations
458pub fn transfer_many(from_pubkey: &Pubkey, to_carats: &[(Pubkey, u64)]) -> Vec<Instruction> {
459    to_carats
460        .iter()
461        .map(|(to_pubkey, carats)| transfer(from_pubkey, to_pubkey, *carats))
462        .collect()
463}
464
465pub fn create_nonce_account_with_seed(
466    from_pubkey: &Pubkey,
467    nonce_pubkey: &Pubkey,
468    base: &Pubkey,
469    seed: &str,
470    authority: &Pubkey,
471    carats: u64,
472) -> Vec<Instruction> {
473    vec![
474        create_account_with_seed(
475            from_pubkey,
476            nonce_pubkey,
477            base,
478            seed,
479            carats,
480            nonce::State::size() as u64,
481            &system_program::id(),
482        ),
483        Instruction::new_with_bincode(
484            system_program::id(),
485            &SystemInstruction::InitializeNonceAccount(*authority),
486            vec![
487                AccountMeta::new(*nonce_pubkey, false),
488                #[allow(deprecated)]
489                AccountMeta::new_readonly(recent_blockhashes::id(), false),
490                AccountMeta::new_readonly(rent::id(), false),
491            ],
492        ),
493    ]
494}
495
496pub fn create_nonce_account(
497    from_pubkey: &Pubkey,
498    nonce_pubkey: &Pubkey,
499    authority: &Pubkey,
500    carats: u64,
501) -> Vec<Instruction> {
502    vec![
503        create_account(
504            from_pubkey,
505            nonce_pubkey,
506            carats,
507            nonce::State::size() as u64,
508            &system_program::id(),
509        ),
510        Instruction::new_with_bincode(
511            system_program::id(),
512            &SystemInstruction::InitializeNonceAccount(*authority),
513            vec![
514                AccountMeta::new(*nonce_pubkey, false),
515                #[allow(deprecated)]
516                AccountMeta::new_readonly(recent_blockhashes::id(), false),
517                AccountMeta::new_readonly(rent::id(), false),
518            ],
519        ),
520    ]
521}
522
523pub fn advance_nonce_account(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction {
524    let account_metas = vec![
525        AccountMeta::new(*nonce_pubkey, false),
526        #[allow(deprecated)]
527        AccountMeta::new_readonly(recent_blockhashes::id(), false),
528        AccountMeta::new_readonly(*authorized_pubkey, true),
529    ];
530    Instruction::new_with_bincode(
531        system_program::id(),
532        &SystemInstruction::AdvanceNonceAccount,
533        account_metas,
534    )
535}
536
537pub fn withdraw_nonce_account(
538    nonce_pubkey: &Pubkey,
539    authorized_pubkey: &Pubkey,
540    to_pubkey: &Pubkey,
541    carats: u64,
542) -> Instruction {
543    let account_metas = vec![
544        AccountMeta::new(*nonce_pubkey, false),
545        AccountMeta::new(*to_pubkey, false),
546        #[allow(deprecated)]
547        AccountMeta::new_readonly(recent_blockhashes::id(), false),
548        AccountMeta::new_readonly(rent::id(), false),
549        AccountMeta::new_readonly(*authorized_pubkey, true),
550    ];
551    Instruction::new_with_bincode(
552        system_program::id(),
553        &SystemInstruction::WithdrawNonceAccount(carats),
554        account_metas,
555    )
556}
557
558pub fn authorize_nonce_account(
559    nonce_pubkey: &Pubkey,
560    authorized_pubkey: &Pubkey,
561    new_authority: &Pubkey,
562) -> Instruction {
563    let account_metas = vec![
564        AccountMeta::new(*nonce_pubkey, false),
565        AccountMeta::new_readonly(*authorized_pubkey, true),
566    ];
567    Instruction::new_with_bincode(
568        system_program::id(),
569        &SystemInstruction::AuthorizeNonceAccount(*new_authority),
570        account_metas,
571    )
572}
573
574#[cfg(test)]
575mod tests {
576    use super::*;
577    use crate::instruction::{Instruction, InstructionError};
578    use num_traits::ToPrimitive;
579
580    fn get_keys(instruction: &Instruction) -> Vec<Pubkey> {
581        instruction.accounts.iter().map(|x| x.pubkey).collect()
582    }
583
584    #[test]
585    fn test_move_many() {
586        let alice_pubkey = Pubkey::new_unique();
587        let bob_pubkey = Pubkey::new_unique();
588        let carol_pubkey = Pubkey::new_unique();
589        let to_carats = vec![(bob_pubkey, 1), (carol_pubkey, 2)];
590
591        let instructions = transfer_many(&alice_pubkey, &to_carats);
592        assert_eq!(instructions.len(), 2);
593        assert_eq!(get_keys(&instructions[0]), vec![alice_pubkey, bob_pubkey]);
594        assert_eq!(get_keys(&instructions[1]), vec![alice_pubkey, carol_pubkey]);
595    }
596
597    #[test]
598    fn test_create_nonce_account() {
599        let from_pubkey = Pubkey::new_unique();
600        let nonce_pubkey = Pubkey::new_unique();
601        let authorized = nonce_pubkey;
602        let ixs = create_nonce_account(&from_pubkey, &nonce_pubkey, &authorized, 42);
603        assert_eq!(ixs.len(), 2);
604        let ix = &ixs[0];
605        assert_eq!(ix.program_id, system_program::id());
606        let pubkeys: Vec<_> = ix.accounts.iter().map(|am| am.pubkey).collect();
607        assert!(pubkeys.contains(&from_pubkey));
608        assert!(pubkeys.contains(&nonce_pubkey));
609    }
610
611    #[test]
612    fn test_nonce_error_decode() {
613        use num_traits::FromPrimitive;
614        fn pretty_err<T>(err: InstructionError) -> String
615        where
616            T: 'static + std::error::Error + DecodeError<T> + FromPrimitive,
617        {
618            if let InstructionError::Custom(code) = err {
619                let specific_error: T = T::decode_custom_error_to_enum(code).unwrap();
620                format!(
621                    "{:?}: {}::{:?} - {}",
622                    err,
623                    T::type_of(),
624                    specific_error,
625                    specific_error,
626                )
627            } else {
628                "".to_string()
629            }
630        }
631        assert_eq!(
632            "Custom(0): NonceError::NoRecentBlockhashes - recent blockhash list is empty",
633            pretty_err::<NonceError>(NonceError::NoRecentBlockhashes.into())
634        );
635        assert_eq!(
636            "Custom(1): NonceError::NotExpired - stored nonce is still in recent_blockhashes",
637            pretty_err::<NonceError>(NonceError::NotExpired.into())
638        );
639        assert_eq!(
640            "Custom(2): NonceError::UnexpectedValue - specified nonce does not match stored nonce",
641            pretty_err::<NonceError>(NonceError::UnexpectedValue.into())
642        );
643        assert_eq!(
644            "Custom(3): NonceError::BadAccountState - cannot handle request in current account state",
645            pretty_err::<NonceError>(NonceError::BadAccountState.into())
646        );
647    }
648
649    #[test]
650    fn test_nonce_to_instruction_error() {
651        assert_eq!(
652            nonce_to_instruction_error(NonceError::NoRecentBlockhashes, false),
653            NonceError::NoRecentBlockhashes.into(),
654        );
655        assert_eq!(
656            nonce_to_instruction_error(NonceError::NotExpired, false),
657            NonceError::NotExpired.into(),
658        );
659        assert_eq!(
660            nonce_to_instruction_error(NonceError::UnexpectedValue, false),
661            NonceError::UnexpectedValue.into(),
662        );
663        assert_eq!(
664            nonce_to_instruction_error(NonceError::BadAccountState, false),
665            NonceError::BadAccountState.into(),
666        );
667        assert_eq!(
668            nonce_to_instruction_error(NonceError::NoRecentBlockhashes, true),
669            SystemError::NonceNoRecentBlockhashes.into(),
670        );
671        assert_eq!(
672            nonce_to_instruction_error(NonceError::NotExpired, true),
673            SystemError::NonceBlockhashNotExpired.into(),
674        );
675        assert_eq!(
676            nonce_to_instruction_error(NonceError::UnexpectedValue, true),
677            SystemError::NonceUnexpectedBlockhashValue.into(),
678        );
679        assert_eq!(
680            nonce_to_instruction_error(NonceError::BadAccountState, true),
681            InstructionError::InvalidAccountData,
682        );
683    }
684
685    #[test]
686    fn test_instruction_to_nonce_error() {
687        assert_eq!(
688            instruction_to_nonce_error(
689                &InstructionError::Custom(NonceErrorAdapter::NoRecentBlockhashes.to_u32().unwrap(),),
690                false,
691            ),
692            Some(NonceError::NoRecentBlockhashes),
693        );
694        assert_eq!(
695            instruction_to_nonce_error(
696                &InstructionError::Custom(NonceErrorAdapter::NotExpired.to_u32().unwrap(),),
697                false,
698            ),
699            Some(NonceError::NotExpired),
700        );
701        assert_eq!(
702            instruction_to_nonce_error(
703                &InstructionError::Custom(NonceErrorAdapter::UnexpectedValue.to_u32().unwrap(),),
704                false,
705            ),
706            Some(NonceError::UnexpectedValue),
707        );
708        assert_eq!(
709            instruction_to_nonce_error(
710                &InstructionError::Custom(NonceErrorAdapter::BadAccountState.to_u32().unwrap(),),
711                false,
712            ),
713            Some(NonceError::BadAccountState),
714        );
715        assert_eq!(
716            instruction_to_nonce_error(&InstructionError::Custom(u32::MAX), false),
717            None,
718        );
719        assert_eq!(
720            instruction_to_nonce_error(
721                &InstructionError::Custom(SystemError::NonceNoRecentBlockhashes.to_u32().unwrap(),),
722                true,
723            ),
724            Some(NonceError::NoRecentBlockhashes),
725        );
726        assert_eq!(
727            instruction_to_nonce_error(
728                &InstructionError::Custom(SystemError::NonceBlockhashNotExpired.to_u32().unwrap(),),
729                true,
730            ),
731            Some(NonceError::NotExpired),
732        );
733        assert_eq!(
734            instruction_to_nonce_error(
735                &InstructionError::Custom(
736                    SystemError::NonceUnexpectedBlockhashValue.to_u32().unwrap(),
737                ),
738                true,
739            ),
740            Some(NonceError::UnexpectedValue),
741        );
742        assert_eq!(
743            instruction_to_nonce_error(&InstructionError::InvalidAccountData, true),
744            Some(NonceError::BadAccountState),
745        );
746        assert_eq!(
747            instruction_to_nonce_error(&InstructionError::Custom(u32::MAX), true),
748            None,
749        );
750    }
751
752    #[test]
753    fn test_nonce_error_adapter_compat() {
754        assert_eq!(
755            NonceError::NoRecentBlockhashes.to_u32(),
756            NonceErrorAdapter::NoRecentBlockhashes.to_u32(),
757        );
758        assert_eq!(
759            NonceError::NotExpired.to_u32(),
760            NonceErrorAdapter::NotExpired.to_u32(),
761        );
762        assert_eq!(
763            NonceError::UnexpectedValue.to_u32(),
764            NonceErrorAdapter::UnexpectedValue.to_u32(),
765        );
766        assert_eq!(
767            NonceError::BadAccountState.to_u32(),
768            NonceErrorAdapter::BadAccountState.to_u32(),
769        );
770    }
771}