Armani Ferrante 3 سال پیش
والد
کامیت
f98b3a7138

+ 0 - 1
lang/attribute/account/src/lib.rs

@@ -211,7 +211,6 @@ pub fn account(
                 #[automatically_derived]
                 impl #impl_gen anchor_lang::AccountSerialize for #account_name #type_gen #where_clause {
                     fn try_serialize<W: std::io::Write>(&self, writer: &mut W) -> std::result::Result<(), ProgramError> {
-                        writer.write_all(&#discriminator).map_err(|_| anchor_lang::__private::ErrorCode::AccountDidNotSerialize)?;
                         AnchorSerialize::serialize(
                             self,
                             writer

+ 34 - 7
lang/attribute/event/src/lib.rs

@@ -18,13 +18,42 @@ pub fn event(
 
     let discriminator: proc_macro2::TokenStream = {
         let discriminator_preimage = format!("event:{}", event_name);
-        let mut discriminator = [0u8; 8];
-        discriminator.copy_from_slice(
-            &anchor_syn::hash::hash(discriminator_preimage.as_bytes()).to_bytes()[..8],
-        );
+
+        #[cfg(feature = "deprecated-layout")]
+        let discriminator = {
+            let mut discriminator = [0u8; 8];
+            discriminator.copy_from_slice(
+                &anchor_syn::hash::hash(discriminator_preimage.as_bytes()).to_bytes()[..8],
+            );
+            discriminator
+        };
+        #[cfg(not(feature = "deprecated-layout"))]
+        let discriminator = {
+            let mut discriminator = [0u8; 4];
+            discriminator.copy_from_slice(
+                &anchor_syn::hash::hash(discriminator_preimage.as_bytes()).to_bytes()[..4],
+            );
+            discriminator
+        };
         format!("{:?}", discriminator).parse().unwrap()
     };
 
+    let discriminator_trait_impl = {
+        if cfg!(feature = "deprecated_layout") {
+            quote! {
+                fn discriminator() -> [u8; 8] {
+                    #discriminator
+                }
+            }
+        } else {
+            quote! {
+                fn discriminator() -> [u8; 4] {
+                    #discriminator
+                }
+            }
+        }
+    };
+
     proc_macro::TokenStream::from(quote! {
         #[derive(anchor_lang::__private::EventIndex, AnchorSerialize, AnchorDeserialize)]
         #event_strct
@@ -38,9 +67,7 @@ pub fn event(
         }
 
         impl anchor_lang::Discriminator for #event_name {
-            fn discriminator() -> [u8; 8] {
-                #discriminator
-            }
+            #discriminator_trait_impl
         }
     })
 }

+ 4 - 1
lang/src/accounts/account.rs

@@ -332,7 +332,10 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsEx
         if &T::owner() == program_id {
             let info = self.to_account_info();
             let mut data = info.try_borrow_mut_data()?;
-            let dst: &mut [u8] = &mut data;
+
+            // Chop off the header.
+            let dst: &mut [u8] = &mut data[8..];
+
             let mut cursor = std::io::Cursor::new(dst);
             self.account.try_serialize(&mut cursor)?;
         }

+ 1 - 47
lang/src/accounts/account_loader.rs

@@ -13,7 +13,6 @@ use solana_program::program_error::ProgramError;
 use solana_program::pubkey::Pubkey;
 use std::cell::{Ref, RefMut};
 use std::fmt;
-use std::io::Write;
 use std::marker::PhantomData;
 use std::mem;
 use std::ops::DerefMut;
@@ -23,8 +22,6 @@ use std::ops::DerefMut;
 /// Note that using accounts in this way is distinctly different from using,
 /// for example, the [`Account`](./struct.Account.html). Namely,
 /// one must call
-/// - `load_init` after initializing an account (this will ignore the missing
-/// account discriminator that gets added only after the user's instruction code)
 /// - `load` when the account is not mutable
 /// - `load_mut` when the account is mutable
 ///
@@ -191,41 +188,6 @@ impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> {
             bytemuck::from_bytes_mut(&mut data.deref_mut()[8..mem::size_of::<T>() + 8])
         }))
     }
-
-    /// Returns a `RefMut` to the account data structure for reading or writing.
-    /// Should only be called once, when the account is being initialized.
-    pub fn load_init(&self) -> Result<RefMut<T>, ProgramError> {
-        // AccountInfo api allows you to borrow mut even if the account isn't
-        // writable, so add this check for a better dev experience.
-        if !self.acc_info.is_writable {
-            return Err(ErrorCode::AccountNotMutable.into());
-        }
-
-        let data = self.acc_info.try_borrow_mut_data()?;
-
-        // The discriminator should be zero, since we're initializing.
-        #[cfg(feature = "deprecated-layout")]
-        let mut disc_bytes = [0u8; 8];
-        #[cfg(feature = "deprecated-layout")]
-        disc_bytes.copy_from_slice(&data[..8]);
-        #[cfg(feature = "deprecated-layout")]
-        let discriminator = u64::from_le_bytes(disc_bytes);
-
-        #[cfg(not(feature = "deprecated-layout"))]
-        let mut disc_bytes = [0u8; 4];
-        #[cfg(not(feature = "deprecated-layout"))]
-        disc_bytes.copy_from_slice(&data[2..6]);
-        #[cfg(not(feature = "deprecated-layout"))]
-        let discriminator = u32::from_le_bytes(disc_bytes);
-
-        if discriminator != 0 {
-            return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
-        }
-
-        Ok(RefMut::map(data, |data| {
-            bytemuck::from_bytes_mut(&mut data.deref_mut()[8..mem::size_of::<T>() + 8])
-        }))
-    }
 }
 
 impl<'info, T: ZeroCopy + Owner> Accounts<'info> for AccountLoader<'info, T> {
@@ -248,15 +210,7 @@ impl<'info, T: ZeroCopy + Owner> Accounts<'info> for AccountLoader<'info, T> {
 impl<'info, T: ZeroCopy + Owner> AccountsExit<'info> for AccountLoader<'info, T> {
     // The account *cannot* be loaded when this is called.
     fn exit(&self, _program_id: &Pubkey) -> ProgramResult {
-        let mut data = self.acc_info.try_borrow_mut_data()?;
-
-        #[cfg(feature = "deprecated-layout")]
-        let dst: &mut [u8] = &mut data;
-        #[cfg(not(feature = "deprecated-layout"))]
-        let dst: &mut [u8] = &mut data[2..];
-
-        let mut cursor = std::io::Cursor::new(dst);
-        cursor.write_all(&T::discriminator()).unwrap();
+        // No-op.
         Ok(())
     }
 }

+ 3 - 46
lang/src/accounts/loader.rs

@@ -10,7 +10,6 @@ use solana_program::program_error::ProgramError;
 use solana_program::pubkey::Pubkey;
 use std::cell::{Ref, RefMut};
 use std::fmt;
-use std::io::Write;
 use std::marker::PhantomData;
 use std::ops::DerefMut;
 
@@ -69,6 +68,8 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
         let disc_bytes = array_ref![data, 2, 4];
 
         if disc_bytes != &T::discriminator() {
+            crate::solana_program::msg!("DISC BYTES: {:?}", disc_bytes);
+            crate::solana_program::msg!("DISC BYTES 2: {:?}", data);
             return Err(ErrorCode::AccountDiscriminatorMismatch.into());
         }
 
@@ -129,42 +130,6 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
             bytemuck::from_bytes_mut(&mut data.deref_mut()[8..])
         }))
     }
-
-    /// Returns a `RefMut` to the account data structure for reading or writing.
-    /// Should only be called once, when the account is being initialized.
-    #[allow(deprecated)]
-    pub fn load_init(&self) -> Result<RefMut<T>, ProgramError> {
-        // AccountInfo api allows you to borrow mut even if the account isn't
-        // writable, so add this check for a better dev experience.
-        if !self.acc_info.is_writable {
-            return Err(ErrorCode::AccountNotMutable.into());
-        }
-
-        let data = self.acc_info.try_borrow_mut_data()?;
-
-        // The discriminator should be zero, since we're initializing.
-        #[cfg(feature = "deprecated-layout")]
-        let mut disc_bytes = [0u8; 8];
-        #[cfg(feature = "deprecated-layout")]
-        disc_bytes.copy_from_slice(&data[..8]);
-        #[cfg(feature = "deprecated-layout")]
-        let discriminator = u64::from_le_bytes(disc_bytes);
-
-        #[cfg(not(feature = "deprecated-layout"))]
-        let mut disc_bytes = [0u8; 4];
-        #[cfg(not(feature = "deprecated-layout"))]
-        disc_bytes.copy_from_slice(&data[2..6]);
-        #[cfg(not(feature = "deprecated-layout"))]
-        let discriminator = u32::from_le_bytes(disc_bytes);
-
-        if discriminator != 0 {
-            return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
-        }
-
-        Ok(RefMut::map(data, |data| {
-            bytemuck::from_bytes_mut(&mut data.deref_mut()[8..])
-        }))
-    }
 }
 
 #[allow(deprecated)]
@@ -189,15 +154,7 @@ impl<'info, T: ZeroCopy> Accounts<'info> for Loader<'info, T> {
 impl<'info, T: ZeroCopy> AccountsExit<'info> for Loader<'info, T> {
     // The account *cannot* be loaded when this is called.
     fn exit(&self, _program_id: &Pubkey) -> ProgramResult {
-        let mut data = self.acc_info.try_borrow_mut_data()?;
-
-        #[cfg(feature = "deprecated-layout")]
-        let dst: &mut [u8] = &mut data;
-        #[cfg(not(feature = "deprecated-layout"))]
-        let dst: &mut [u8] = &mut data[2..];
-
-        let mut cursor = std::io::Cursor::new(dst);
-        cursor.write_all(&T::discriminator()).unwrap();
+        // No-op.
         Ok(())
     }
 }

+ 4 - 1
lang/src/accounts/program_account.rs

@@ -100,7 +100,10 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info
     fn exit(&self, _program_id: &Pubkey) -> ProgramResult {
         let info = self.to_account_info();
         let mut data = info.try_borrow_mut_data()?;
-        let dst: &mut [u8] = &mut data;
+
+        // Chop off the header.
+        let dst: &mut [u8] = &mut data[8..];
+
         let mut cursor = std::io::Cursor::new(dst);
         self.inner.account.try_serialize(&mut cursor)?;
         Ok(())

+ 79 - 8
lang/syn/src/codegen/accounts/constraints.rs

@@ -149,16 +149,44 @@ pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macr
 pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream {
     let field = &f.ident;
     let ty_decl = f.ty_decl();
-    let from_account_info = f.from_account_info_unchecked(None);
+    let account_ty = f.account_ty();
+    let from_account_info = f.from_account_info(None);
+
+    let header_write = {
+        if cfg!(feature = "deprecated-layout") {
+            quote! {
+                use std::io::{Write, Cursor};
+                use anchor_lang::Discriminator;
+                let __dst: &mut [u8] = &mut __data;
+                let mut __cursor = Cursor::new(__dst);
+                Write::write_all(&mut __cursor, &#account_ty::discriminator()).unwrap();
+            }
+        } else {
+            quote! {
+                use std::io::{Write, Cursor};
+                use anchor_lang::Discriminator;
+                let __dst: &mut [u8] = &mut __data[2..];
+                let mut __cursor = Cursor::new(__dst);
+                Write::write_all(&mut __cursor, &#account_ty::discriminator()).unwrap();
+            }
+        }
+    };
+
+    // Check the *entire* account header is zero.
     quote! {
         let #field: #ty_decl = {
-            let mut __data: &[u8] = &#field.try_borrow_data()?;
-            let mut __disc_bytes = [0u8; 8];
-            __disc_bytes.copy_from_slice(&__data[..8]);
-            let __discriminator = u64::from_le_bytes(__disc_bytes);
-            if __discriminator != 0 {
-                return Err(anchor_lang::__private::ErrorCode::ConstraintZero.into());
+            {
+                let mut __data: &mut [u8] = &mut #field.try_borrow_mut_data()?;
+                let mut __header_bytes = [0u8; 8];
+                __header_bytes.copy_from_slice(&__data[..8]);
+                let __header = u64::from_le_bytes(__header_bytes);
+                if __header != 0 {
+                    return Err(anchor_lang::__private::ErrorCode::ConstraintZero.into());
+                }
+
+                #header_write
             }
+
             #from_account_info
         };
     }
@@ -425,7 +453,7 @@ pub fn generate_init(
 ) -> proc_macro2::TokenStream {
     let field = &f.ident;
     let ty_decl = f.ty_decl();
-    let from_account_info = f.from_account_info_unchecked(Some(kind));
+    let from_account_info = f.from_account_info(Some(kind));
     let if_needed = if if_needed {
         quote! {true}
     } else {
@@ -542,7 +570,9 @@ pub fn generate_init(
                         let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts);
                         anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.key(), #freeze_authority)?;
                     }
+
                     let pa: #ty_decl = #from_account_info;
+
                     if !(!#if_needed || #field.as_ref().owner == &anchor_lang::solana_program::system_program::ID) {
                         if pa.mint_authority != anchor_lang::solana_program::program_option::COption::Some(#owner.key()) {
                             return Err(anchor_lang::__private::ErrorCode::ConstraintMintMintAuthority.into());
@@ -611,6 +641,42 @@ pub fn generate_init(
             };
             let create_account =
                 generate_create_account(field, quote! {space}, owner.clone(), seeds_with_nonce);
+
+            let header_write = {
+                match &f.ty {
+                    Ty::Account(_) | Ty::ProgramAccount(_) => {
+                        let account_ty = f.account_ty();
+                        if cfg!(feature = "deprecated-layout") {
+                            quote! {
+                                {
+                                    use std::io::{Write, Cursor};
+                                    use anchor_lang::Discriminator;
+
+                                    let mut __data = actual_field.try_borrow_mut_data()?;
+                                    let __dst: &mut [u8] = &mut __data;
+                                    let mut __cursor = Cursor::new(__dst);
+                                    Write::write_all(&mut __cursor, &#account_ty::discriminator()).unwrap();
+                                }
+                            }
+                        } else {
+                            quote! {
+                                {
+                                    use std::io::{Write, Seek, SeekFrom, Cursor};
+                                    use anchor_lang::Discriminator;
+
+                                    let mut __data = actual_field.try_borrow_mut_data()?;
+                                    let __dst: &mut [u8] = &mut __data;
+                                    let mut __cursor = Cursor::new(__dst);
+                                    Seek::seek(&mut __cursor, SeekFrom::Start(2)).unwrap();
+                                    Write::write_all(&mut __cursor, &#account_ty::discriminator()).unwrap();
+                                }
+                            }
+                        }
+                    }
+                    _ => quote! {},
+                }
+            };
+
             quote! {
                 let #field = {
                     let actual_field = #field.to_account_info();
@@ -620,6 +686,11 @@ pub fn generate_init(
                         #payer
                         #create_account
                     }
+
+                    // Write the account header into the account data before
+                    // deserializing.
+                    #header_write
+
                     let pa: #ty_decl = #from_account_info;
                     if !(!#if_needed || actual_owner == &anchor_lang::solana_program::system_program::ID) {
                         if space != actual_field.data_len() {

+ 42 - 4
lang/syn/src/codegen/program/handlers.rs

@@ -84,7 +84,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
                 let seed = anchor_lang::idl::IdlAccount::seed();
                 let owner = accounts.program.key;
                 let to = Pubkey::create_with_seed(&base, seed, owner).unwrap();
-                // Space: account discriminator || authority pubkey || vec len || vec data
+                // Space: account header || authority pubkey || vec len || vec data
                 let space = 8 + 32 + 4 + data_len as usize;
                 let rent = Rent::get()?;
                 let lamports = rent.minimum_balance(space);
@@ -125,6 +125,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
                 let mut data = accounts.to.try_borrow_mut_data()?;
                 let dst: &mut [u8] = &mut data;
                 let mut cursor = std::io::Cursor::new(dst);
+                std::io::Seek::seek(&mut cursor, std::io::SeekFrom::Start(8)).unwrap();
                 idl_account.try_serialize(&mut cursor)?;
 
                 Ok(())
@@ -196,6 +197,34 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
                 let ix_name: proc_macro2::TokenStream =
                     generate_ctor_variant_name().parse().unwrap();
                 let ix_name_log = format!("Instruction: {}", ix_name);
+                let header_write = {
+                    if cfg!(feature = "deprecated-layout") {
+                        quote! {
+                            {
+                                use std::io::{Write, Cursor};
+                                use anchor_lang::Discriminator;
+
+                                let mut __data = ctor_accounts.to.try_borrow_mut_data()?;
+                                let __dst: &mut [u8] = &mut __data;
+                                let mut __cursor = Cursor::new(__dst);
+                                Write::write_all(&mut __cursor, &#name::discriminator()).unwrap();
+                            }
+                        }
+                    } else {
+                        quote! {
+                            {
+                                use std::io::{Write, Cursor, SeekFrom, Seek};
+                                use anchor_lang::Discriminator;
+
+                                let mut __data = ctor_accounts.to.try_borrow_mut_data()?;
+                                let __dst: &mut [u8] = &mut __data;
+                                let mut __cursor = Cursor::new(__dst);
+                                Seek::seek(&mut __cursor, SeekFrom::Start(2)).unwrap();
+                                Write::write_all(&mut __cursor, &#name::discriminator()).unwrap();
+                            }
+                        }
+                    }
+                };
                 if state.is_zero_copy {
                     quote! {
                         // One time state account initializer. Will faill on subsequent
@@ -245,14 +274,17 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
                                 &[seeds],
                             )?;
 
+                            // Initialize the header.
+                            #header_write
+
                             // Zero copy deserialize.
-                            let loader: anchor_lang::accounts::loader::Loader<#mod_name::#name> = anchor_lang::accounts::loader::Loader::try_from_unchecked(program_id, &ctor_accounts.to)?;
+                            let loader: anchor_lang::accounts::loader::Loader<#mod_name::#name> = anchor_lang::accounts::loader::Loader::try_from(program_id, &ctor_accounts.to)?;
 
                             // Invoke the ctor in a new lexical scope so that
                             // the zero-copy RefMut gets dropped. Required
                             // so that we can subsequently run the exit routine.
                             {
-                                let mut instance = loader.load_init()?;
+                                let mut instance = loader.load()?;
                                 instance.new(
                                     anchor_lang::context::Context::new(
                                         program_id,
@@ -329,11 +361,15 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
                                 &[seeds],
                             )?;
 
+                            // Initialize the account header.
+                            #header_write
+
                             // Serialize the state and save it to storage.
                             ctor_user_def_accounts.exit(program_id)?;
                             let mut data = ctor_accounts.to.try_borrow_mut_data()?;
                             let dst: &mut [u8] = &mut data;
                             let mut cursor = std::io::Cursor::new(dst);
+                            std::io::Seek::seek(&mut cursor, std::io::SeekFrom::Start(8)).unwrap();
                             instance.try_serialize(&mut cursor)?;
 
                             Ok(())
@@ -460,6 +496,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
                                     let mut data = acc_info.try_borrow_mut_data()?;
                                     let dst: &mut [u8] = &mut data;
                                     let mut cursor = std::io::Cursor::new(dst);
+																		std::io::Seek::seek(&mut cursor, std::io::SeekFrom::Start(8)).unwrap();
                                     state.try_serialize(&mut cursor)?;
 
                                     Ok(())
@@ -488,7 +525,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
                             .map(|ix| {
                                 // Easy to implement. Just need to write a test.
                                 // Feel free to open a PR.
-                                assert!(!state.is_zero_copy, "Trait implementations not yet implemented for zero copy state structs. Please file an issue.");                                
+                                assert!(!state.is_zero_copy, "Trait implementations not yet implemented for zero copy state structs. Please file an issue.");
 
                                 let ix_arg_names: Vec<&syn::Ident> =
                                     ix.args.iter().map(|arg| &arg.name).collect();
@@ -573,6 +610,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
                                             let mut data = acc_info.try_borrow_mut_data()?;
                                             let dst: &mut [u8] = &mut data;
                                             let mut cursor = std::io::Cursor::new(dst);
+																						std::io::Seek::seek(&mut cursor, std::io::SeekFrom::Start(8)).unwrap();
                                             state.try_serialize(&mut cursor)?;
 
                                             Ok(())

+ 5 - 5
lang/syn/src/lib.rs

@@ -280,7 +280,7 @@ impl Field {
 
     // TODO: remove the option once `CpiAccount` is completely removed (not
     //       just deprecated).
-    pub fn from_account_info_unchecked(&self, kind: Option<&InitKind>) -> proc_macro2::TokenStream {
+    pub fn from_account_info(&self, kind: Option<&InitKind>) -> proc_macro2::TokenStream {
         let field = &self.ident;
         let container_ty = self.container_ty();
         match &self.ty {
@@ -291,13 +291,13 @@ impl Field {
             Ty::Account(AccountTy { boxed, .. }) => {
                 if *boxed {
                     quote! {
-                        Box::new(#container_ty::try_from_unchecked(
+                        Box::new(#container_ty::try_from(
                             &#field,
                         )?)
                     }
                 } else {
                     quote! {
-                        #container_ty::try_from_unchecked(
+                        #container_ty::try_from(
                             &#field,
                         )?
                     }
@@ -305,7 +305,7 @@ impl Field {
             }
             Ty::CpiAccount(_) => {
                 quote! {
-                    #container_ty::try_from_unchecked(
+                    #container_ty::try_from(
                         &#field,
                     )?
                 }
@@ -321,7 +321,7 @@ impl Field {
                     },
                 };
                 quote! {
-                    #container_ty::try_from_unchecked(
+                    #container_ty::try_from(
                         #owner_addr,
                         &#field,
                     )?

+ 8 - 6
tests/misc/programs/misc/src/lib.rs

@@ -122,7 +122,7 @@ pub mod misc {
     }
 
     pub fn test_pda_init_zero_copy(ctx: Context<TestPdaInitZeroCopy>, bump: u8) -> ProgramResult {
-        let mut acc = ctx.accounts.my_pda.load_init()?;
+        let mut acc = ctx.accounts.my_pda.load_mut()?;
         acc.data = 9;
         acc.bump = bump;
         Ok(())
@@ -156,7 +156,7 @@ pub mod misc {
     }
 
     pub fn test_init_zero_copy(ctx: Context<TestInitZeroCopy>) -> ProgramResult {
-        let mut data = ctx.accounts.data.load_init()?;
+        let mut data = ctx.accounts.data.load_mut()?;
         data.data = 10;
         data.bump = 2;
         Ok(())
@@ -264,15 +264,17 @@ pub mod misc {
         **ctx.accounts.user.try_borrow_mut_lamports()? += 1;
         Ok(())
     }
-    
-    pub fn init_if_needed_checks_rent_exemption(_ctx: Context<InitIfNeededChecksRentExemption>) -> ProgramResult {
+
+    pub fn init_if_needed_checks_rent_exemption(
+        _ctx: Context<InitIfNeededChecksRentExemption>,
+    ) -> ProgramResult {
         Ok(())
     }
-        
+
     pub fn test_program_id_constraint(
         _ctx: Context<TestProgramIdConstraint>,
         _bump: u8,
-        _second_bump: u8
+        _second_bump: u8,
     ) -> ProgramResult {
         Ok(())
     }

+ 2 - 2
tests/misc/tests/misc.js

@@ -1,4 +1,5 @@
-const anchor = require("@project-serum/anchor");
+//const anchor = require("@project-serum/anchor");
+const anchor = require("../../../ts");
 const assert = require("assert");
 const {
   ASSOCIATED_TOKEN_PROGRAM_ID,
@@ -156,7 +157,6 @@ describe("misc", () => {
       "Program Z2Ddx1Lcd8CHTV9tkWtNnFQrSz6kxz2H38wrr18zZRZ consumed 4819 of 200000 compute units",
       "Program Z2Ddx1Lcd8CHTV9tkWtNnFQrSz6kxz2H38wrr18zZRZ success",
     ];
-
     assert.ok(JSON.stringify(expectedRaw), resp.raw);
     assert.ok(resp.events[0].name === "E1");
     assert.ok(resp.events[0].data.data === 44);

+ 11 - 11
ts/src/coder/borsh/accounts.ts

@@ -97,9 +97,9 @@ export class BorshAccountHeader {
   /**
    * Returns the default account header for an account with the given name.
    */
-  public static encode(accountName: string): Buffer {
+  public static encode(accountName: string, nameSpace?: string): Buffer {
     if (features.isSet("deprecated-layout")) {
-      return BorshAccountHeader.discriminator(accountName);
+      return BorshAccountHeader.discriminator(accountName, nameSpace);
     } else {
       return Buffer.concat([
         Buffer.from([0]), // Version.
@@ -115,18 +115,18 @@ export class BorshAccountHeader {
    *
    * @param name The name of the account to calculate the discriminator.
    */
-  public static discriminator(name: string): Buffer {
-    let size: number;
-    if (features.isSet("deprecated-layout")) {
-      size = DEPRECATED_ACCOUNT_DISCRIMINATOR_SIZE;
-    } else {
-      size = ACCOUNT_DISCRIMINATOR_SIZE;
-    }
+  public static discriminator(name: string, nameSpace?: string): Buffer {
     return Buffer.from(
-      sha256.digest(`account:${camelcase(name, { pascalCase: true })}`)
-    ).slice(0, size);
+      sha256.digest(`${nameSpace ?? "account"}:${camelcase(name, { pascalCase: true })}`)
+    ).slice(0, BorshAccountHeader.discriminatorSize());
   }
 
+	public static discriminatorSize(): number {
+		return features.isSet("deprecated-layout")
+			? DEPRECATED_ACCOUNT_DISCRIMINATOR_SIZE
+			: ACCOUNT_DISCRIMINATOR_SIZE;
+	}
+
   /**
    * Returns the account data index at which the discriminator starts.
    */

+ 31 - 4
ts/src/coder/borsh/event.ts

@@ -6,6 +6,7 @@ import { Idl, IdlEvent, IdlTypeDef } from "../../idl.js";
 import { Event, EventData } from "../../program/event.js";
 import { IdlCoder } from "./idl.js";
 import { EventCoder } from "../index.js";
+import * as features from "../../utils/features";
 
 export class BorshEventCoder implements EventCoder {
   /**
@@ -41,7 +42,7 @@ export class BorshEventCoder implements EventCoder {
       idl.events === undefined
         ? []
         : idl.events.map((e) => [
-            base64.fromByteArray(eventDiscriminator(e.name)),
+          base64.fromByteArray(EventHeader.discriminator(e.name)),
             e.name,
           ])
     );
@@ -57,7 +58,7 @@ export class BorshEventCoder implements EventCoder {
     } catch (e) {
       return null;
     }
-    const disc = base64.fromByteArray(logArr.slice(0, 8));
+    const disc = base64.fromByteArray(EventHeader.parseDiscriminator(logArr));
 
     // Only deserialize if the discriminator implies a proper event.
     const eventName = this.discriminators.get(disc);
@@ -69,7 +70,7 @@ export class BorshEventCoder implements EventCoder {
     if (!layout) {
       throw new Error(`Unknown event: ${eventName}`);
     }
-    const data = layout.decode(logArr.slice(8)) as EventData<
+    const data = layout.decode(logArr.slice(EventHeader.size())) as EventData<
       E["fields"][number],
       T
     >;
@@ -78,5 +79,31 @@ export class BorshEventCoder implements EventCoder {
 }
 
 export function eventDiscriminator(name: string): Buffer {
-  return Buffer.from(sha256.digest(`event:${name}`)).slice(0, 8);
+	return EventHeader.discriminator(name);
+}
+
+class EventHeader {
+	public static parseDiscriminator(data: Buffer): Buffer {
+		if (features.isSet("deprecated-layout")) {
+			return data.slice(0, 8);
+		} else {
+			return data.slice(0, 4);
+		}
+	}
+
+	public static size(): number {
+		if (features.isSet("deprecated-layout")) {
+			return 8;
+		} else {
+			return 4;
+		}
+	}
+
+	public static discriminator(name: string): Buffer {
+		if (features.isSet("deprecated-layout")) {
+		  return Buffer.from(sha256.digest(`event:${name}`)).slice(0, 8);
+		} else {
+			return Buffer.from(sha256.digest(`event:${name}`)).slice(0, 4);
+		}
+	}
 }

+ 8 - 6
ts/src/coder/borsh/state.ts

@@ -4,6 +4,7 @@ import { sha256 } from "js-sha256";
 import { Idl } from "../../idl.js";
 import { IdlCoder } from "./idl.js";
 import * as features from "../../utils/features.js";
+import { BorshAccountHeader } from "./accounts";
 
 export class BorshStateCoder {
   private layout: Layout;
@@ -19,15 +20,16 @@ export class BorshStateCoder {
     const buffer = Buffer.alloc(1000); // TODO: use a tighter buffer.
     const len = this.layout.encode(account, buffer);
 
-    const disc = await stateDiscriminator(name);
+		let ns = features.isSet("anchor-deprecated-state") ? "account" : "state";
+    const header = BorshAccountHeader.encode(name, ns);
     const accData = buffer.slice(0, len);
 
-    return Buffer.concat([disc, accData]);
+    return Buffer.concat([header, accData]);
   }
 
-  public decode<T = any>(ix: Buffer): T {
-    // Chop off discriminator.
-    const data = ix.slice(8);
+  public decode<T = any>(data: Buffer): T {
+    // Chop off header.
+    data = data.slice(BorshAccountHeader.size());
     return this.layout.decode(data);
   }
 }
@@ -35,5 +37,5 @@ export class BorshStateCoder {
 // Calculates unique 8 byte discriminator prepended to all anchor state accounts.
 export async function stateDiscriminator(name: string): Promise<Buffer> {
   let ns = features.isSet("anchor-deprecated-state") ? "account" : "state";
-  return Buffer.from(sha256.digest(`${ns}:${name}`)).slice(0, 8);
+  return Buffer.from(sha256.digest(`${ns}:${name}`)).slice(0, BorshAccountHeader.discriminatorSize());
 }

+ 13 - 3
ts/src/program/namespace/state.ts

@@ -24,6 +24,7 @@ import InstructionNamespaceFactory from "./instruction.js";
 import RpcNamespaceFactory from "./rpc.js";
 import TransactionNamespaceFactory from "./transaction.js";
 import { IdlTypes, TypeDef } from "./types.js";
+import * as features from "../../utils/features.js";
 
 export default class StateFactory {
   public static build<IDL extends Idl>(
@@ -172,10 +173,19 @@ export class StateClient<IDL extends Idl> {
     if (!state) {
       throw new Error("State is not specified in IDL.");
     }
+
     const expectedDiscriminator = await stateDiscriminator(state.struct.name);
-    if (expectedDiscriminator.compare(accountInfo.data.slice(0, 8))) {
-      throw new Error("Invalid account discriminator");
-    }
+
+		if (features.isSet('deprecated-layout')) {
+			if (expectedDiscriminator.compare(accountInfo.data.slice(0, 8))) {
+				throw new Error("Invalid state discriminator");
+			}
+		} else {
+			if (expectedDiscriminator.compare(accountInfo.data.slice(2, 6))) {
+				throw new Error("Invalid state discriminator");
+			}
+		}
+
     return this.coder.state.decode(accountInfo.data);
   }