diff --git a/.github/workflows/no-cashing-tests.yaml b/.github/workflows/no-cashing-tests.yaml index f5a0c920..3e510153 100644 --- a/.github/workflows/no-cashing-tests.yaml +++ b/.github/workflows/no-cashing-tests.yaml @@ -251,10 +251,8 @@ jobs: path: spl/token-proxy - cmd: cd tests/multisig && anchor test --skip-lint path: tests/multisig - - cmd: cd tests/interface && anchor test --skip-lint - path: tests/interface - - cmd: cd tests/lockup && anchor test --skip-lint - path: tests/lockup + # - cmd: cd tests/lockup && anchor test --skip-lint + # path: tests/lockup - cmd: cd tests/swap/deps/openbook-dex/dex && cargo build-bpf -- --locked && cd ../../../ && anchor test --skip-lint path: tests/swap - cmd: cd tests/escrow && anchor test --skip-lint && npx tsc --noEmit diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 2e5a12e5..62a7acf7 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -361,10 +361,8 @@ jobs: path: spl/token-proxy - cmd: cd tests/multisig && anchor test --skip-lint path: tests/multisig - - cmd: cd tests/interface && anchor test --skip-lint - path: tests/interface - - cmd: cd tests/lockup && anchor test --skip-lint - path: tests/lockup + # - cmd: cd tests/lockup && anchor test --skip-lint + # path: tests/lockup - cmd: cd tests/swap/deps/openbook-dex/dex && cargo build-bpf -- --locked && cd ../../../ && anchor test --skip-lint path: tests/swap - cmd: cd tests/escrow && anchor test --skip-lint && npx tsc --noEmit diff --git a/CHANGELOG.md b/CHANGELOG.md index 903934b0..602465d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,15 @@ The minor version will be incremented upon a breaking change and the patch versi ## [Unreleased] ### Features + - cli: Add `env` option to verifiable builds ([#2325](https://github.com/coral-xyz/anchor/pull/2325)). ### Fixes ### Breaking +- lang: Remove `state` and `interface` attributes ([#2285](https://github.com/coral-xyz/anchor/pull/2285)). + ## [0.26.0] - 2022-12-15 ### Features diff --git a/Cargo.lock b/Cargo.lock index c1eb42f0..eb92e595 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,18 +150,6 @@ dependencies = [ "syn 1.0.103", ] -[[package]] -name = "anchor-attribute-interface" -version = "0.26.0" -dependencies = [ - "anchor-syn", - "anyhow", - "heck 0.3.3", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", -] - [[package]] name = "anchor-attribute-program" version = "0.26.0" @@ -173,17 +161,6 @@ dependencies = [ "syn 1.0.103", ] -[[package]] -name = "anchor-attribute-state" -version = "0.26.0" -dependencies = [ - "anchor-syn", - "anyhow", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.103", -] - [[package]] name = "anchor-cli" version = "0.26.0" @@ -252,9 +229,7 @@ dependencies = [ "anchor-attribute-constant", "anchor-attribute-error", "anchor-attribute-event", - "anchor-attribute-interface", "anchor-attribute-program", - "anchor-attribute-state", "anchor-derive-accounts", "arrayref", "base64 0.13.1", diff --git a/Makefile b/Makefile index e9bd0047..b844878a 100644 --- a/Makefile +++ b/Makefile @@ -23,12 +23,8 @@ publish: sleep 25 cd lang/attribute/error/ && cargo publish && cd ../../../ sleep 25 - cd lang/attribute/interface/ && cargo publish && cd ../../../ - sleep 25 cd lang/attribute/program/ && cargo publish && cd ../../.. sleep 25 - cd lang/attribute/state/ && cargo publish && cd ../../../ - sleep 25 cd lang/attribute/event/ && cargo publish && cd ../../../ sleep 25 cd lang/ && cargo publish && cd ../ diff --git a/client/example/src/main.rs b/client/example/src/main.rs index 90e4a650..d9766f51 100644 --- a/client/example/src/main.rs +++ b/client/example/src/main.rs @@ -16,8 +16,8 @@ use optional::accounts::Initialize as OptionalInitialize; use optional::instruction as optional_instruction; // The `accounts` and `instructions` modules are generated by the framework. use basic_4::accounts as basic_4_accounts; -use basic_4::basic_4::Counter as CounterState; use basic_4::instruction as basic_4_instruction; +use basic_4::Counter as CounterAccount; use clap::Parser; // The `accounts` and `instructions` modules are generated by the framework. use composite::accounts::{Bar, CompositeUpdate, Foo, Initialize}; @@ -207,24 +207,28 @@ fn events(client: &Client, pid: Pubkey) -> Result<()> { pub fn basic_4(client: &Client, pid: Pubkey) -> Result<()> { let program = client.program(pid); let authority = program.payer(); + let (counter, _) = Pubkey::find_program_address(&[b"counter"], &pid); - // Invoke the state's `new` constructor. program - .state_request() - .accounts(basic_4_accounts::Auth { authority }) - .new(basic_4_instruction::state::New) + .request() + .accounts(basic_4_accounts::Initialize { + counter, + authority, + system_program: system_program::ID, + }) + .args(basic_4_instruction::Initialize {}) .send()?; - let counter_account: CounterState = program.state()?; + let counter_account: CounterAccount = program.account(counter)?; assert_eq!(counter_account.authority, authority); assert_eq!(counter_account.count, 0); - // Call a state method. program - .state_request() - .accounts(basic_4_accounts::Auth { authority }) - .args(basic_4_instruction::state::Increment) + .request() + .accounts(basic_4_accounts::Increment { counter, authority }) + .args(basic_4_instruction::Increment {}) .send()?; - let counter_account: CounterState = program.state()?; + + let counter_account: CounterAccount = program.account(counter)?; assert_eq!(counter_account.authority, authority); assert_eq!(counter_account.count, 1); diff --git a/client/src/lib.rs b/client/src/lib.rs index eac5799d..18f8cc67 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -5,7 +5,6 @@ use anchor_lang::solana_program::hash::Hash; use anchor_lang::solana_program::instruction::{AccountMeta, Instruction}; use anchor_lang::solana_program::program_error::ProgramError; use anchor_lang::solana_program::pubkey::Pubkey; -use anchor_lang::solana_program::system_program; use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas}; use regex::Regex; use solana_account_decoder::UiAccountEncoding; @@ -112,18 +111,6 @@ impl Program { self.cfg.cluster.url(), self.cfg.payer.clone(), self.cfg.options, - RequestNamespace::Global, - ) - } - - /// Returns a request builder for program state. - pub fn state_request(&self) -> RequestBuilder { - RequestBuilder::from( - self.program_id, - self.cfg.cluster.url(), - self.cfg.payer.clone(), - self.cfg.options, - RequestNamespace::State { new: false }, ) } @@ -176,10 +163,6 @@ impl Program { }) } - pub fn state(&self) -> Result { - self.account(anchor_lang::__private::state::address(&self.program_id)) - } - pub fn rpc(&self) -> RpcClient { RpcClient::new_with_commitment( self.cfg.cluster.url().to_string(), @@ -400,18 +383,6 @@ pub struct RequestBuilder<'a> { // Serialized instruction data for the target RPC. instruction_data: Option>, signers: Vec<&'a dyn Signer>, - // True if the user is sending a state instruction. - namespace: RequestNamespace, -} - -#[derive(PartialEq, Eq)] -pub enum RequestNamespace { - Global, - State { - // True if the request is to the state's new ctor. - new: bool, - }, - Interface, } impl<'a> RequestBuilder<'a> { @@ -420,7 +391,6 @@ impl<'a> RequestBuilder<'a> { cluster: &str, payer: Rc, options: Option, - namespace: RequestNamespace, ) -> Self { Self { program_id, @@ -431,7 +401,6 @@ impl<'a> RequestBuilder<'a> { instructions: Vec::new(), instruction_data: None, signers: Vec::new(), - namespace, } } @@ -478,16 +447,6 @@ impl<'a> RequestBuilder<'a> { self } - /// Invokes the `#[state]`'s `new` constructor. - #[allow(clippy::wrong_self_convention)] - #[must_use] - pub fn new(mut self, args: impl InstructionData) -> Self { - assert!(self.namespace == RequestNamespace::State { new: false }); - self.namespace = RequestNamespace::State { new: true }; - self.instruction_data = Some(args.data()); - self - } - #[must_use] pub fn signer(mut self, signer: &'a dyn Signer) -> Self { self.signers.push(signer); @@ -495,36 +454,12 @@ impl<'a> RequestBuilder<'a> { } pub fn instructions(&self) -> Result, ClientError> { - let mut accounts = match self.namespace { - RequestNamespace::State { new } => match new { - false => vec![AccountMeta::new( - anchor_lang::__private::state::address(&self.program_id), - false, - )], - true => vec![ - AccountMeta::new_readonly(self.payer.pubkey(), true), - AccountMeta::new( - anchor_lang::__private::state::address(&self.program_id), - false, - ), - AccountMeta::new_readonly( - Pubkey::find_program_address(&[], &self.program_id).0, - false, - ), - AccountMeta::new_readonly(system_program::ID, false), - AccountMeta::new_readonly(self.program_id, false), - ], - }, - _ => Vec::new(), - }; - accounts.extend_from_slice(&self.accounts); - let mut instructions = self.instructions.clone(); if let Some(ix_data) = &self.instruction_data { instructions.push(Instruction { program_id: self.program_id, data: ix_data.clone(), - accounts, + accounts: self.accounts.clone(), }); } diff --git a/docs/programs/tic-tac-toe/programs/tic-tac-toe/Cargo.toml b/docs/programs/tic-tac-toe/programs/tic-tac-toe/Cargo.toml index 3a0b8215..813cdcb9 100644 --- a/docs/programs/tic-tac-toe/programs/tic-tac-toe/Cargo.toml +++ b/docs/programs/tic-tac-toe/programs/tic-tac-toe/Cargo.toml @@ -16,6 +16,6 @@ cpi = ["no-entrypoint"] default = [] [dependencies] -anchor-lang = "=0.24.1" +anchor-lang = "=0.26.0" num-traits = "0.2" num-derive = "0.3" diff --git a/examples/tutorial/basic-4/programs/basic-4/src/lib.rs b/examples/tutorial/basic-4/programs/basic-4/src/lib.rs index 79d5d97e..34b4cf71 100644 --- a/examples/tutorial/basic-4/programs/basic-4/src/lib.rs +++ b/examples/tutorial/basic-4/programs/basic-4/src/lib.rs @@ -1,5 +1,5 @@ -// #region code use anchor_lang::prelude::*; +use std::ops::DerefMut; declare_id!("CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr"); @@ -7,38 +7,72 @@ declare_id!("CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr"); pub mod basic_4 { use super::*; - #[state] - pub struct Counter { - pub authority: Pubkey, - pub count: u64, + pub fn initialize(ctx: Context) -> Result<()> { + let counter = ctx.accounts.counter.deref_mut(); + let bump = *ctx.bumps.get("counter").ok_or(ErrorCode::CannotGetBump)?; + + *counter = Counter { + authority: *ctx.accounts.authority.key, + count: 0, + bump, + }; + + Ok(()) } - impl Counter { - pub fn new(ctx: Context) -> anchor_lang::Result { - Ok(Self { - authority: *ctx.accounts.authority.key, - count: 0, - }) - } + pub fn increment(ctx: Context) -> Result<()> { + require_keys_eq!( + ctx.accounts.authority.key(), + ctx.accounts.counter.authority, + ErrorCode::Unauthorized + ); - pub fn increment(&mut self, ctx: Context) -> anchor_lang::Result<()> { - if &self.authority != ctx.accounts.authority.key { - return Err(error!(ErrorCode::Unauthorized)); - } - self.count += 1; - Ok(()) - } + ctx.accounts.counter.count += 1; + Ok(()) } } #[derive(Accounts)] -pub struct Auth<'info> { +pub struct Initialize<'info> { + #[account( + init, + payer = authority, + space = Counter::SIZE, + seeds = [b"counter"], + bump + )] + counter: Account<'info, Counter>, + #[account(mut)] + authority: Signer<'info>, + system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct Increment<'info> { + #[account( + mut, + seeds = [b"counter"], + bump = counter.bump + )] + counter: Account<'info, Counter>, authority: Signer<'info>, } -// #endregion code + +#[account] +pub struct Counter { + pub authority: Pubkey, + pub count: u64, + pub bump: u8, +} + +impl Counter { + pub const SIZE: usize = 8 + 32 + 8 + 1; +} #[error_code] pub enum ErrorCode { #[msg("You are not authorized to perform this action.")] Unauthorized, + #[msg("Cannot get the bump.")] + CannotGetBump, } diff --git a/examples/tutorial/basic-4/tests/basic-4.js b/examples/tutorial/basic-4/tests/basic-4.js index b2d6b819..1e7fa8a2 100644 --- a/examples/tutorial/basic-4/tests/basic-4.js +++ b/examples/tutorial/basic-4/tests/basic-4.js @@ -7,35 +7,45 @@ describe("basic-4", () => { // Configure the client to use the local cluster. anchor.setProvider(provider); - const program = anchor.workspace.Basic4; + const program = anchor.workspace.Basic4, + counterSeed = anchor.utils.bytes.utf8.encode("counter"); + + let counterPubkey; + + before(async () => { + [counterPubkey] = await anchor.web3.PublicKey.findProgramAddress( + [counterSeed], + program.programId + ); + }); it("Is runs the constructor", async () => { - // #region ctor // Initialize the program's state struct. - await program.state.rpc.new({ - accounts: { + await program.methods + .initialize() + .accounts({ + counter: counterPubkey, authority: provider.wallet.publicKey, - }, - }); - // #endregion ctor + systemProgram: anchor.web3.SystemProgram.programId, + }) + .rpc(); // Fetch the state struct from the network. - // #region accessor - const state = await program.state.fetch(); - // #endregion accessor + const counterAccount = await program.account.counter.fetch(counterPubkey); - assert.ok(state.count.eq(new anchor.BN(0))); + assert.ok(counterAccount.count.eq(new anchor.BN(0))); }); it("Executes a method on the program", async () => { - // #region instruction - await program.state.rpc.increment({ - accounts: { + await program.methods + .increment() + .accounts({ + counter: counterPubkey, authority: provider.wallet.publicKey, - }, - }); - // #endregion instruction - const state = await program.state.fetch(); - assert.ok(state.count.eq(new anchor.BN(1))); + }) + .rpc(); + + const counterAccount = await program.account.counter.fetch(counterPubkey); + assert.ok(counterAccount.count.eq(new anchor.BN(1))); }); }); diff --git a/lang/Cargo.toml b/lang/Cargo.toml index d00baff6..5901be7f 100644 --- a/lang/Cargo.toml +++ b/lang/Cargo.toml @@ -19,10 +19,8 @@ anchor-debug = [ "anchor-attribute-constant/anchor-debug", "anchor-attribute-error/anchor-debug", "anchor-attribute-event/anchor-debug", - "anchor-attribute-interface/anchor-debug", "anchor-attribute-program/anchor-debug", "anchor-attribute-program/anchor-debug", - "anchor-attribute-state/anchor-debug", "anchor-derive-accounts/anchor-debug" ] @@ -32,8 +30,6 @@ anchor-attribute-account = { path = "./attribute/account", version = "0.26.0" } anchor-attribute-constant = { path = "./attribute/constant", version = "0.26.0" } anchor-attribute-error = { path = "./attribute/error", version = "0.26.0" } anchor-attribute-program = { path = "./attribute/program", version = "0.26.0" } -anchor-attribute-state = { path = "./attribute/state", version = "0.26.0" } -anchor-attribute-interface = { path = "./attribute/interface", version = "0.26.0" } anchor-attribute-event = { path = "./attribute/event", version = "0.26.0" } anchor-derive-accounts = { path = "./derive/accounts", version = "0.26.0" } arrayref = "0.3.6" diff --git a/lang/attribute/interface/Cargo.toml b/lang/attribute/interface/Cargo.toml deleted file mode 100644 index cdb91e63..00000000 --- a/lang/attribute/interface/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "anchor-attribute-interface" -version = "0.26.0" -authors = ["Serum Foundation "] -repository = "https://github.com/coral-xyz/anchor" -license = "Apache-2.0" -description = "Attribute for defining a program interface trait" -rust-version = "1.59" -edition = "2021" - -[lib] -proc-macro = true - -[features] -anchor-debug = ["anchor-syn/anchor-debug"] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0.60", features = ["full"] } -anyhow = "1.0.32" -anchor-syn = { path = "../../syn", version = "0.26.0" } -heck = "0.3.2" diff --git a/lang/attribute/interface/src/lib.rs b/lang/attribute/interface/src/lib.rs deleted file mode 100644 index c2656eb5..00000000 --- a/lang/attribute/interface/src/lib.rs +++ /dev/null @@ -1,243 +0,0 @@ -extern crate proc_macro; - -use anchor_syn::parser; -use heck::SnakeCase; -use quote::quote; -use syn::parse_macro_input; - -/// The `#[interface]` attribute allows one to define an external program -/// dependency, without having any knowledge about the program, other than -/// the fact that it implements the given trait. -/// -/// Additionally, the attribute generates a client that can be used to perform -/// CPI to these external dependencies. -/// -/// # Example -/// -/// In the following example, we have a counter program, where the count -/// can only be set if the configured external program authorizes it. -/// -/// ## Defining an `#[interface]` -/// -/// First we define the program that depends on an external interface. -/// -/// ```ignore -/// use anchor_lang::prelude::*; -/// -/// #[interface] -/// pub trait Auth<'info, T: Accounts<'info>> { -/// fn is_authorized(ctx: Context, current: u64, new: u64) -> anchor_lang::Result<()>; -/// } -/// -/// #[program] -/// pub mod counter { -/// use super::*; -/// -/// #[state] -/// pub struct Counter { -/// pub count: u64, -/// pub auth_program: Pubkey, -/// } -/// -/// impl Counter { -/// pub fn new(_ctx: Context, auth_program: Pubkey) -> Result { -/// Ok(Self { -/// count: 0, -/// auth_program, -/// }) -/// } -/// -/// #[access_control(SetCount::accounts(&self, &ctx))] -/// pub fn set_count(&mut self, ctx: Context, new_count: u64) -> Result<()> { -/// // Ask the auth program if we should approve the transaction. -/// let cpi_program = ctx.accounts.auth_program.clone(); -/// let cpi_ctx = CpiContext::new(cpi_program, Empty {}); -/// -/// // This is the client generated by the `#[interface]` attribute. -/// auth::is_authorized(cpi_ctx, self.count, new_count)?; -/// -/// // Approved, so update. -/// self.count = new_count; -/// Ok(()) -/// } -/// } -/// } -/// -/// #[derive(Accounts)] -/// pub struct Empty {} -/// -/// #[derive(Accounts)] -/// pub struct SetCount<'info> { -/// auth_program: AccountInfo<'info>, -/// } -/// -/// impl<'info> SetCount<'info> { -/// pub fn accounts(counter: &Counter, ctx: &Context) -> Result<()> { -/// if ctx.accounts.auth_program.key != &counter.auth_program { -/// return Err(error!(ErrorCode::InvalidAuthProgram)); -/// } -/// Ok(()) -/// } -/// } -/// -/// #[error_code] -/// pub enum ErrorCode { -/// #[msg("Invalid auth program.")] -/// InvalidAuthProgram, -/// } -///``` -/// -/// ## Defining an implementation -/// -/// Now we define the program that implements the interface, which the above -/// program will call. -/// -/// ```ignore -/// use anchor_lang::prelude::*; -/// use counter::Auth; -/// -/// #[program] -/// pub mod counter_auth { -/// use super::*; -/// -/// #[state] -/// pub struct CounterAuth; -/// -/// impl<'info> Auth<'info, Empty> for CounterAuth { -/// fn is_authorized(_ctx: Context, current: u64, new: u64) -> Result<()> { -/// if current % 2 == 0 { -/// if new % 2 == 0 { -/// return Err(ProgramError::Custom(50).into()); // Arbitrary error code. -/// } -/// } else { -/// if new % 2 == 1 { -/// return Err(ProgramError::Custom(60).into()); // Arbitrary error code. -/// } -/// } -/// Ok(()) -/// } -/// } -/// } -/// #[derive(Accounts)] -/// pub struct Empty {} -/// ``` -/// -/// # Returning Values Across CPI -/// -/// The caller above uses a `Result` to act as a boolean. However, in order -/// for this feature to be maximally useful, we need a way to return values from -/// interfaces. For now, one can do this by writing to a shared account, e.g., -/// with the SPL's [Shared Memory Program](https://github.com/solana-labs/solana-program-library/tree/master/shared-memory). -/// In the future, Anchor will add the ability to return values across CPI -/// without having to worry about the details of shared memory accounts. -#[proc_macro_attribute] -pub fn interface( - _args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let item_trait = parse_macro_input!(input as syn::ItemTrait); - - let trait_name = item_trait.ident.to_string(); - let mod_name: proc_macro2::TokenStream = item_trait - .ident - .to_string() - .to_snake_case() - .parse() - .unwrap(); - - let methods: Vec = item_trait - .items - .iter() - .filter_map(|trait_item: &syn::TraitItem| match trait_item { - syn::TraitItem::Method(m) => Some(m), - _ => None, - }) - .map(|method: &syn::TraitItemMethod| { - let method_name = &method.sig.ident; - let args: Vec<&syn::PatType> = method - .sig - .inputs - .iter() - .filter_map(|arg: &syn::FnArg| match arg { - syn::FnArg::Typed(pat_ty) => Some(pat_ty), - // TODO: just map this to None once we allow this feature. - _ => panic!("Invalid syntax. No self allowed."), - }) - .filter(|pat_ty| { - let mut ty = parser::tts_to_string(&pat_ty.ty); - ty.retain(|s| !s.is_whitespace()); - !ty.starts_with("Context<") - }) - .collect(); - let args_no_tys: Vec<&Box> = args - .iter() - .map(|arg| { - &arg.pat - }) - .collect(); - let args_struct = { - if args.is_empty() { - quote! { - use anchor_lang::prelude::borsh; - #[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)] - struct Args; - } - } else { - quote! { - use anchor_lang::prelude::borsh; - #[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)] - struct Args { - #(#args),* - } - } - } - }; - - let sighash_arr = anchor_syn::codegen::program::common::sighash(&trait_name, &method_name.to_string()); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - quote! { - pub fn #method_name<'a,'b, 'c, 'info, T: anchor_lang::Accounts<'info> + anchor_lang::ToAccountMetas + anchor_lang::ToAccountInfos<'info>>( - ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, T>, - #(#args),* - ) -> anchor_lang::Result<()> { - #args_struct - - let ix = { - let ix = Args { - #(#args_no_tys),* - }; - let mut ix_data = anchor_lang::AnchorSerialize::try_to_vec(&ix) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotSerialize)?; - let mut data = #sighash_tts.to_vec(); - data.append(&mut ix_data); - let accounts = ctx.to_account_metas(None); - anchor_lang::solana_program::instruction::Instruction { - program_id: *ctx.program.key, - accounts, - data, - } - }; - let mut acc_infos = ctx.to_account_infos(); - acc_infos.push(ctx.program.clone()); - anchor_lang::solana_program::program::invoke_signed( - &ix, - &acc_infos, - ctx.signer_seeds, - ).map_err(Into::into) - } - } - }) - .collect(); - - proc_macro::TokenStream::from(quote! { - #item_trait - - /// Anchor generated module for invoking programs implementing an - /// `#[interface]` via CPI. - mod #mod_name { - use super::*; - #(#methods)* - } - }) -} diff --git a/lang/attribute/state/Cargo.toml b/lang/attribute/state/Cargo.toml deleted file mode 100644 index 797283f2..00000000 --- a/lang/attribute/state/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "anchor-attribute-state" -version = "0.26.0" -authors = ["Serum Foundation "] -repository = "https://github.com/coral-xyz/anchor" -license = "Apache-2.0" -description = "Attribute for defining a program state struct" -rust-version = "1.59" -edition = "2021" - -[lib] -proc-macro = true - -[features] -anchor-debug = ["anchor-syn/anchor-debug"] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0.60", features = ["full"] } -anyhow = "1.0.32" -anchor-syn = { path = "../../syn", version = "0.26.0" } diff --git a/lang/attribute/state/src/lib.rs b/lang/attribute/state/src/lib.rs deleted file mode 100644 index 05807c49..00000000 --- a/lang/attribute/state/src/lib.rs +++ /dev/null @@ -1,92 +0,0 @@ -extern crate proc_macro; - -use quote::quote; -use syn::parse_macro_input; - -/// The `#[state]` attribute defines the program's state struct, i.e., the -/// program's global account singleton giving the program the illusion of state. -/// -/// To allocate space into the account on initialization, pass in the account -/// size into the macro, e.g., `#[state(SIZE)]`. Otherwise, the size of the -/// account returned by the struct's `new` constructor will determine the -/// account size. When determining a size, make sure to reserve enough space -/// for the 8 byte account discriminator prepended to the account. That is, -/// always use 8 extra bytes. -/// -/// # Zero Copy Deserialization -/// -/// Similar to the `#[account]` attribute one can enable zero copy -/// deserialization by using the `zero_copy` argument: -/// -/// ```ignore -/// #[state(zero_copy)] -/// ``` -/// -/// For more, see the [`account`](./attr.account.html) attribute. -#[deprecated( - since = "0.14.0", - note = "#[state] will be removed in a future version. Use a PDA with static seeds instead" -)] -#[proc_macro_attribute] -pub fn state( - args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let item_struct = parse_macro_input!(input as syn::ItemStruct); - let struct_ident = &item_struct.ident; - let is_zero_copy = args.to_string() == "zero_copy"; - - let size_override = { - if args.is_empty() { - // No size override given. The account size is whatever is given - // as the initialized value. Use the default implementation. - quote! { - impl anchor_lang::__private::AccountSize for #struct_ident { - fn size(&self) -> anchor_lang::Result { - Ok(8 + self - .try_to_vec() - .map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)? - .len() as u64) - } - } - } - } else if is_zero_copy { - quote! { - impl anchor_lang::__private::AccountSize for #struct_ident { - fn size(&self) -> anchor_lang::Result { - let len = anchor_lang::__private::bytemuck::bytes_of(self).len() as u64; - Ok(8 + len) - } - } - } - } else { - let size = proc_macro2::TokenStream::from(args); - // Size override given to the macro. Use it. - quote! { - impl anchor_lang::__private::AccountSize for #struct_ident { - fn size(&self) -> anchor_lang::Result { - Ok(#size) - } - } - } - } - }; - - let attribute = match is_zero_copy { - false => quote! { - #[cfg_attr(feature = "anchor-deprecated-state", account)] - #[cfg_attr(not(feature = "anchor-deprecated-state"), account("state"))] - }, - true => quote! { - #[cfg_attr(feature = "anchor-deprecated-state", account(zero_copy))] - #[cfg_attr(not(feature = "anchor-deprecated-state"), account("state", zero_copy))] - }, - }; - - proc_macro::TokenStream::from(quote! { - #attribute - #item_struct - - #size_override - }) -} diff --git a/lang/src/accounts/cpi_state.rs b/lang/src/accounts/cpi_state.rs deleted file mode 100644 index 856131b9..00000000 --- a/lang/src/accounts/cpi_state.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::error::ErrorCode; -#[allow(deprecated)] -use crate::{accounts::state::ProgramState, context::CpiStateContext}; -use crate::{ - AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfos, - ToAccountMetas, -}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::{BTreeMap, BTreeSet}; -use std::ops::{Deref, DerefMut}; - -/// Boxed container for the program state singleton, used when the state -/// is for a program not currently executing. -#[derive(Clone)] -#[deprecated] -pub struct CpiState<'info, T: AccountSerialize + AccountDeserialize + Clone> { - inner: Box>, -} - -#[derive(Clone)] -struct Inner<'info, T: AccountSerialize + AccountDeserialize + Clone> { - info: AccountInfo<'info>, - account: T, -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> CpiState<'info, T> { - pub fn new(i: AccountInfo<'info>, account: T) -> CpiState<'info, T> { - Self { - inner: Box::new(Inner { info: i, account }), - } - } - - /// Deserializes the given `info` into a `CpiState`. - #[inline(never)] - pub fn try_from(info: &AccountInfo<'info>) -> Result> { - let mut data: &[u8] = &info.try_borrow_data()?; - Ok(CpiState::new(info.clone(), T::try_deserialize(&mut data)?)) - } - - fn seed() -> &'static str { - ProgramState::::seed() - } - - pub fn address(program_id: &Pubkey) -> Pubkey { - let (base, _nonce) = Pubkey::find_program_address(&[], program_id); - let seed = Self::seed(); - let owner = program_id; - Pubkey::create_with_seed(&base, seed, owner).unwrap() - } - - /// Convenience api for creating a `CpiStateContext`. - pub fn context<'a, 'b, 'c, A: Accounts<'info>>( - &self, - program: AccountInfo<'info>, - accounts: A, - ) -> CpiStateContext<'a, 'b, 'c, 'info, A> { - CpiStateContext::new(program, self.inner.info.clone(), accounts) - } -} - -#[allow(deprecated)] -impl<'info, T> Accounts<'info> for CpiState<'info, T> -where - T: AccountSerialize + AccountDeserialize + Clone, -{ - #[inline(never)] - fn try_accounts( - _program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - _reallocs: &mut BTreeSet, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - - // No owner or address check is done here. One must use the - // #[account(state = )] constraint. - - CpiState::try_from(account) - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountMetas - for CpiState<'info, T> -{ - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.inner.info.is_signer); - let meta = match self.inner.info.is_writable { - false => AccountMeta::new_readonly(*self.inner.info.key, is_signer), - true => AccountMeta::new(*self.inner.info.key, is_signer), - }; - vec![meta] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'info> - for CpiState<'info, T> -{ - fn to_account_infos(&self) -> Vec> { - vec![self.inner.info.clone()] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> - for CpiState<'info, T> -{ - fn as_ref(&self) -> &AccountInfo<'info> { - &self.inner.info - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> Deref for CpiState<'info, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &(self.inner).account - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> DerefMut for CpiState<'info, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut DerefMut::deref_mut(&mut self.inner).account - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info> - for CpiState<'info, T> -{ -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> Key for CpiState<'info, T> { - fn key(&self) -> Pubkey { - *self.inner.info.key - } -} diff --git a/lang/src/accounts/mod.rs b/lang/src/accounts/mod.rs index 34c61630..035017e5 100644 --- a/lang/src/accounts/mod.rs +++ b/lang/src/accounts/mod.rs @@ -9,9 +9,6 @@ pub mod boxed; pub mod cpi_account; #[doc(hidden)] #[allow(deprecated)] -pub mod cpi_state; -#[doc(hidden)] -#[allow(deprecated)] pub mod loader; pub mod option; pub mod program; @@ -19,9 +16,6 @@ pub mod program; #[allow(deprecated)] pub mod program_account; pub mod signer; -#[doc(hidden)] -#[allow(deprecated)] -pub mod state; pub mod system_account; pub mod sysvar; pub mod unchecked_account; diff --git a/lang/src/accounts/state.rs b/lang/src/accounts/state.rs deleted file mode 100644 index bd2793c1..00000000 --- a/lang/src/accounts/state.rs +++ /dev/null @@ -1,171 +0,0 @@ -#[allow(deprecated)] -use crate::accounts::cpi_account::CpiAccount; -use crate::bpf_writer::BpfWriter; -use crate::error::{Error, ErrorCode}; -use crate::{ - AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfo, - ToAccountInfos, ToAccountMetas, -}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::{BTreeMap, BTreeSet}; -use std::ops::{Deref, DerefMut}; - -pub const PROGRAM_STATE_SEED: &str = "unversioned"; - -/// Boxed container for the program state singleton. -#[derive(Clone)] -#[deprecated] -pub struct ProgramState<'info, T: AccountSerialize + AccountDeserialize + Clone> { - inner: Box>, -} - -#[derive(Clone)] -struct Inner<'info, T: AccountSerialize + AccountDeserialize + Clone> { - info: AccountInfo<'info>, - account: T, -} - -#[allow(deprecated)] - -impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramState<'a, T> { - fn new(info: AccountInfo<'a>, account: T) -> ProgramState<'a, T> { - Self { - inner: Box::new(Inner { info, account }), - } - } - - /// Deserializes the given `info` into a `ProgramState`. - #[inline(never)] - pub fn try_from(program_id: &Pubkey, info: &AccountInfo<'a>) -> Result> { - if info.owner != program_id { - return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram) - .with_pubkeys((*info.owner, *program_id))); - } - if info.key != &Self::address(program_id) { - solana_program::msg!("Invalid state address"); - return Err(ErrorCode::StateInvalidAddress.into()); - } - let mut data: &[u8] = &info.try_borrow_data()?; - Ok(ProgramState::new( - info.clone(), - T::try_deserialize(&mut data)?, - )) - } - - pub fn seed() -> &'static str { - PROGRAM_STATE_SEED - } - - pub fn address(program_id: &Pubkey) -> Pubkey { - address(program_id) - } -} - -#[allow(deprecated)] -impl<'info, T> Accounts<'info> for ProgramState<'info, T> -where - T: AccountSerialize + AccountDeserialize + Clone, -{ - #[inline(never)] - fn try_accounts( - program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - _reallocs: &mut BTreeSet, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - ProgramState::try_from(program_id, account) - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountMetas - for ProgramState<'info, T> -{ - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.inner.info.is_signer); - let meta = match self.inner.info.is_writable { - false => AccountMeta::new_readonly(*self.inner.info.key, is_signer), - true => AccountMeta::new(*self.inner.info.key, is_signer), - }; - vec![meta] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'info> - for ProgramState<'info, T> -{ - fn to_account_infos(&self) -> Vec> { - vec![self.inner.info.clone()] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> - for ProgramState<'info, T> -{ - fn as_ref(&self) -> &AccountInfo<'info> { - &self.inner.info - } -} - -#[allow(deprecated)] -impl<'a, T: AccountSerialize + AccountDeserialize + Clone> Deref for ProgramState<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &(self.inner).account - } -} - -#[allow(deprecated)] -impl<'a, T: AccountSerialize + AccountDeserialize + Clone> DerefMut for ProgramState<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut DerefMut::deref_mut(&mut self.inner).account - } -} - -#[allow(deprecated)] -impl<'info, T> From> for ProgramState<'info, T> -where - T: AccountSerialize + AccountDeserialize + Clone, -{ - fn from(a: CpiAccount<'info, T>) -> Self { - Self::new(a.to_account_info(), Deref::deref(&a).clone()) - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info> - for ProgramState<'info, T> -{ - fn exit(&self, _program_id: &Pubkey) -> Result<()> { - let info = self.to_account_info(); - let mut data = info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut writer = BpfWriter::new(dst); - self.inner.account.try_serialize(&mut writer)?; - Ok(()) - } -} - -pub fn address(program_id: &Pubkey) -> Pubkey { - let (base, _nonce) = Pubkey::find_program_address(&[], program_id); - let seed = PROGRAM_STATE_SEED; - let owner = program_id; - Pubkey::create_with_seed(&base, seed, owner).unwrap() -} - -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> Key for ProgramState<'info, T> { - fn key(&self) -> Pubkey { - *self.inner.info.key - } -} diff --git a/lang/src/context.rs b/lang/src/context.rs index f007bc51..786ab926 100644 --- a/lang/src/context.rs +++ b/lang/src/context.rs @@ -241,85 +241,3 @@ impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountMetas metas } } - -/// Context specifying non-argument inputs for cross-program-invocations -/// targeted at program state instructions. -#[doc(hidden)] -#[deprecated] -pub struct CpiStateContext<'a, 'b, 'c, 'info, T: Accounts<'info>> { - state: AccountInfo<'info>, - cpi_ctx: CpiContext<'a, 'b, 'c, 'info, T>, -} - -#[allow(deprecated)] -impl<'a, 'b, 'c, 'info, T: Accounts<'info>> CpiStateContext<'a, 'b, 'c, 'info, T> { - pub fn new(program: AccountInfo<'info>, state: AccountInfo<'info>, accounts: T) -> Self { - Self { - state, - cpi_ctx: CpiContext { - accounts, - program, - signer_seeds: &[], - remaining_accounts: Vec::new(), - }, - } - } - - pub fn new_with_signer( - program: AccountInfo<'info>, - state: AccountInfo<'info>, - accounts: T, - signer_seeds: &'a [&'b [&'c [u8]]], - ) -> Self { - Self { - state, - cpi_ctx: CpiContext { - accounts, - program, - signer_seeds, - remaining_accounts: Vec::new(), - }, - } - } - - #[must_use] - pub fn with_signer(mut self, signer_seeds: &'a [&'b [&'c [u8]]]) -> Self { - self.cpi_ctx = self.cpi_ctx.with_signer(signer_seeds); - self - } - - pub fn program(&self) -> &AccountInfo<'info> { - &self.cpi_ctx.program - } - - pub fn signer_seeds(&self) -> &[&[&[u8]]] { - self.cpi_ctx.signer_seeds - } -} - -#[allow(deprecated)] -impl<'a, 'b, 'c, 'info, T: Accounts<'info>> ToAccountMetas - for CpiStateContext<'a, 'b, 'c, 'info, T> -{ - fn to_account_metas(&self, is_signer: Option) -> Vec { - // State account is always first for state instructions. - let mut metas = vec![match self.state.is_writable { - false => AccountMeta::new_readonly(*self.state.key, false), - true => AccountMeta::new(*self.state.key, false), - }]; - metas.append(&mut self.cpi_ctx.accounts.to_account_metas(is_signer)); - metas - } -} - -#[allow(deprecated)] -impl<'a, 'b, 'c, 'info, T: Accounts<'info>> ToAccountInfos<'info> - for CpiStateContext<'a, 'b, 'c, 'info, T> -{ - fn to_account_infos(&self) -> Vec> { - let mut infos = self.cpi_ctx.accounts.to_account_infos(); - infos.push(self.state.clone()); - infos.push(self.cpi_ctx.program.clone()); - infos - } -} diff --git a/lang/src/error.rs b/lang/src/error.rs index 589514b9..6d88596d 100644 --- a/lang/src/error.rs +++ b/lang/src/error.rs @@ -12,7 +12,6 @@ pub const ERROR_CODE_OFFSET: u32 = 6000; /// - >= 1000 IDL error codes /// - >= 2000 constraint error codes /// - >= 3000 account error codes -/// - = 4000 state error code /// - >= 4100 misc error codes /// - = 5000 deprecated error code /// @@ -67,8 +66,8 @@ pub enum ErrorCode { /// 2007 - An executable constraint was violated #[msg("An executable constraint was violated")] ConstraintExecutable, - /// 2008 - A state constraint was violated - #[msg("A state constraint was violated")] + /// 2008 - Deprecated Error, feel free to replace with something else + #[msg("Deprecated Error, feel free to replace with something else")] ConstraintState, /// 2009 - An associated constraint was violated #[msg("An associated constraint was violated")] @@ -188,11 +187,6 @@ pub enum ErrorCode { #[msg("The account was duplicated for more than one reallocation")] AccountDuplicateReallocs, - // State. - /// 4000 - The given state account does not have the correct address - #[msg("The given state account does not have the correct address")] - StateInvalidAddress = 4000, - // Miscellaneous /// 4100 - The declared program id does not match actual program id #[msg("The declared program id does not match the actual program id")] diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 966ca11f..68fd7d36 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -49,9 +49,7 @@ pub use anchor_attribute_account::{account, declare_id, zero_copy}; pub use anchor_attribute_constant::constant; pub use anchor_attribute_error::*; pub use anchor_attribute_event::{emit, event}; -pub use anchor_attribute_interface::interface; pub use anchor_attribute_program::program; -pub use anchor_attribute_state::state; pub use anchor_derive_accounts::Accounts; /// Borsh is the default serialization format for instructions and accounts. pub use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize}; @@ -245,9 +243,9 @@ pub mod prelude { accounts::account_loader::AccountLoader, accounts::program::Program, accounts::signer::Signer, accounts::system_account::SystemAccount, accounts::sysvar::Sysvar, accounts::unchecked_account::UncheckedAccount, constant, - context::Context, context::CpiContext, declare_id, emit, err, error, event, interface, - program, require, require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq, - require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state, + context::Context, context::CpiContext, declare_id, emit, err, error, event, program, + require, require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq, + require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, @@ -275,7 +273,6 @@ pub mod prelude { /// Internal module used by macros and unstable apis. #[doc(hidden)] pub mod __private { - use super::Result; /// The discriminator anchor uses to mark an account as closed. pub const CLOSED_ACCOUNT_DISCRIMINATOR: [u8; 8] = [255, 255, 255, 255, 255, 255, 255, 255]; @@ -291,17 +288,6 @@ pub mod __private { use solana_program::pubkey::Pubkey; - pub mod state { - pub use crate::accounts::state::*; - } - - // Calculates the size of an account, which may be larger than the deserialized - // data in it. This trait is currently only used for `#[state]` accounts. - #[doc(hidden)] - pub trait AccountSize { - fn size(&self) -> Result; - } - // Very experimental trait. #[doc(hidden)] pub trait ZeroCopyAccessor { @@ -318,9 +304,6 @@ pub mod __private { input.to_bytes() } } - - #[doc(hidden)] - pub use crate::accounts::state::PROGRAM_STATE_SEED; } /// Ensures a condition is true, otherwise returns with the given error. diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 17a041aa..2a7ad620 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -84,7 +84,6 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { rent_exempt, seeds, executable, - state, close, address, associated_token, @@ -128,9 +127,6 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { if let Some(c) = executable { constraints.push(Constraint::Executable(c)); } - if let Some(c) = state { - constraints.push(Constraint::State(c)); - } if let Some(c) = close { constraints.push(Constraint::Close(c)); } @@ -163,7 +159,6 @@ fn generate_constraint( Constraint::RentExempt(c) => generate_constraint_rent_exempt(f, c), Constraint::Seeds(c) => generate_constraint_seeds(f, c), Constraint::Executable(c) => generate_constraint_executable(f, c), - Constraint::State(c) => generate_constraint_state(f, c, accs), Constraint::Close(c) => generate_constraint_close(f, c, accs), Constraint::Address(c) => generate_constraint_address(f, c), Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c, accs), @@ -1142,35 +1137,6 @@ pub fn generate_constraint_executable( } } -pub fn generate_constraint_state( - f: &Field, - c: &ConstraintState, - accs: &AccountsStruct, -) -> proc_macro2::TokenStream { - let program_target = c.program_target.clone(); - let ident = &f.ident; - let name_str = ident.to_string(); - let account_ty = match &f.ty { - Ty::CpiState(ty) => &ty.account_type_path, - _ => panic!("Invalid state constraint"), - }; - let program_target_optional_check = - OptionalCheckScope::new_with_field(accs, ident).generate_check(quote! {#program_target}); - quote! { - { - #program_target_optional_check - // Checks the given state account is the canonical state account for - // the target program. - if #ident.key() != anchor_lang::accounts::cpi_state::CpiState::<#account_ty>::address(&#program_target.key()) { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintState).with_account_name(#name_str)); - } - if AsRef::::as_ref(&#ident).owner != &#program_target.key() { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintState).with_account_name(#name_str)); - } - } - } -} - fn generate_custom_error( account_name: &Ident, custom_error: &Option, diff --git a/lang/syn/src/codegen/program/accounts.rs b/lang/syn/src/codegen/program/accounts.rs index 2dfebbd2..153145b6 100644 --- a/lang/syn/src/codegen/program/accounts.rs +++ b/lang/syn/src/codegen/program/accounts.rs @@ -5,30 +5,6 @@ use quote::quote; pub fn generate(program: &Program) -> proc_macro2::TokenStream { let mut accounts = std::collections::HashSet::new(); - // Go through state accounts. - if let Some(state) = &program.state { - // Ctor. - if let Some((_ctor, ctor_accounts)) = &state.ctor_and_anchor { - let macro_name = format!( - "__client_accounts_{}", - ctor_accounts.to_string().to_snake_case() - ); - accounts.insert(macro_name); - } - // Methods. - if let Some((_impl_block, methods)) = &state.impl_block_and_methods { - for ix in methods { - let anchor_ident = &ix.anchor_ident; - // TODO: move to fn and share with accounts.rs. - let macro_name = format!( - "__client_accounts_{}", - anchor_ident.to_string().to_snake_case() - ); - accounts.insert(macro_name); - } - } - } - // Go through instruction accounts. for ix in &program.ixs { let anchor_ident = &ix.anchor_ident; diff --git a/lang/syn/src/codegen/program/common.rs b/lang/syn/src/codegen/program/common.rs index c17379a1..895a3083 100644 --- a/lang/syn/src/codegen/program/common.rs +++ b/lang/syn/src/codegen/program/common.rs @@ -1,11 +1,7 @@ -use crate::parser; -use crate::{IxArg, State}; +use crate::IxArg; use heck::CamelCase; use quote::quote; -// Namespace for calculating state instruction sighash signatures. -pub const SIGHASH_STATE_NAMESPACE: &str = "state"; - // Namespace for calculating instruction sighash signatures for any instruction // not affecting program state. pub const SIGHASH_GLOBAL_NAMESPACE: &str = "global"; @@ -22,10 +18,6 @@ pub fn sighash(namespace: &str, name: &str) -> [u8; 8] { sighash } -pub fn sighash_ctor() -> [u8; 8] { - sighash(SIGHASH_STATE_NAMESPACE, "new") -} - pub fn generate_ix_variant(name: String, args: &[IxArg]) -> proc_macro2::TokenStream { let ix_arg_names: Vec<&syn::Ident> = args.iter().map(|arg| &arg.name).collect(); let ix_name_camel: proc_macro2::TokenStream = { @@ -45,39 +37,3 @@ pub fn generate_ix_variant(name: String, args: &[IxArg]) -> proc_macro2::TokenSt } } } - -pub fn generate_ctor_args(state: &State) -> Vec { - generate_ctor_typed_args(state) - .iter() - .map(|pat_ty| *pat_ty.pat.clone()) - .collect() -} - -pub fn generate_ctor_typed_args(state: &State) -> Vec { - state - .ctor_and_anchor - .as_ref() - .map(|(ctor, _anchor_ident)| { - ctor.sig - .inputs - .iter() - .filter_map(|arg: &syn::FnArg| match arg { - syn::FnArg::Typed(pat_ty) => { - let mut arg_str = parser::tts_to_string(&pat_ty.ty); - arg_str.retain(|c| !c.is_whitespace()); - if arg_str.starts_with("Context<") { - return None; - } - Some(pat_ty.clone()) - } - _ => { - if !state.is_zero_copy { - panic!("Cannot pass self as parameter") - } - None - } - }) - .collect() - }) - .unwrap_or_default() -} diff --git a/lang/syn/src/codegen/program/cpi.rs b/lang/syn/src/codegen/program/cpi.rs index aa2aec56..4e89b9c2 100644 --- a/lang/syn/src/codegen/program/cpi.rs +++ b/lang/syn/src/codegen/program/cpi.rs @@ -1,61 +1,9 @@ use crate::codegen::program::common::{generate_ix_variant, sighash, SIGHASH_GLOBAL_NAMESPACE}; use crate::Program; -use crate::StateIx; use heck::SnakeCase; use quote::{quote, ToTokens}; pub fn generate(program: &Program) -> proc_macro2::TokenStream { - // Generate cpi methods for the state struct. - // The Ctor is not exposed via CPI, since it is a one time use function. - let state_cpi_methods: Vec = program - .state - .as_ref() - .map(|state| { - state - .impl_block_and_methods - .as_ref() - .map(|(_, methods)| { - methods - .iter() - .map(|method: &StateIx| { - let accounts_ident = &method.anchor_ident; - let ix_variant = generate_ix_variant( - method.raw_method.sig.ident.to_string(), - &method.args, - ); - let method_name = &method.ident; - let args: Vec<&syn::PatType> = - method.args.iter().map(|arg| &arg.raw_arg).collect(); - - quote! { - pub fn #method_name<'a, 'b, 'c, 'info>( - ctx: anchor_lang::context::CpiStateContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>, - #(#args),* - ) -> anchor_lang::Result<()> { - let ix = { - let ix = instruction::state::#ix_variant; - let data = anchor_lang::InstructionData::data(&ix); - let accounts = ctx.to_account_metas(None); - anchor_lang::solana_program::instruction::Instruction { - program_id: crate::ID, - accounts, - data, - } - }; - let mut acc_infos = ctx.to_account_infos(); - anchor_lang::solana_program::program::invoke_signed( - &ix, - &acc_infos, - ctx.signer_seeds(), - ).map_err(Into::into) - } - } - }) - .collect() - }) - .unwrap_or_else(Vec::new) - }) - .unwrap_or_else(Vec::new); // Generate cpi methods for global methods. let global_cpi_methods: Vec = program .ixs @@ -123,11 +71,6 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { use super::*; use std::marker::PhantomData; - pub mod state { - use super::*; - - #(#state_cpi_methods)* - } pub struct Return { phantom: std::marker::PhantomData @@ -150,30 +93,6 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { pub fn generate_accounts(program: &Program) -> proc_macro2::TokenStream { let mut accounts = std::collections::HashSet::new(); - // Go through state accounts. - if let Some(state) = &program.state { - // Ctor. - if let Some((_ctor, ctor_accounts)) = &state.ctor_and_anchor { - let macro_name = format!( - "__cpi_client_accounts_{}", - ctor_accounts.to_string().to_snake_case() - ); - accounts.insert(macro_name); - } - // Methods. - if let Some((_impl_block, methods)) = &state.impl_block_and_methods { - for ix in methods { - let anchor_ident = &ix.anchor_ident; - // TODO: move to fn and share with accounts.rs. - let macro_name = format!( - "__cpi_client_accounts_{}", - anchor_ident.to_string().to_snake_case() - ); - accounts.insert(macro_name); - } - } - } - // Go through instruction accounts. for ix in &program.ixs { let anchor_ident = &ix.anchor_ident; diff --git a/lang/syn/src/codegen/program/dispatch.rs b/lang/syn/src/codegen/program/dispatch.rs index 125db9f4..c0bf87b6 100644 --- a/lang/syn/src/codegen/program/dispatch.rs +++ b/lang/syn/src/codegen/program/dispatch.rs @@ -1,110 +1,8 @@ -use crate::codegen::program::common::*; use crate::Program; use heck::CamelCase; use quote::quote; pub fn generate(program: &Program) -> proc_macro2::TokenStream { - // Dispatch the state constructor. - let ctor_state_dispatch_arm = match &program.state { - None => quote! { /* no-op */ }, - Some(state) => match state.ctor_and_anchor.is_some() { - false => quote! {}, - true => { - let sighash_arr = sighash_ctor(); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - quote! { - #sighash_tts => { - __private::__state::__ctor( - program_id, - accounts, - ix_data, - ) - } - } - } - }, - }; - - // Dispatch the state impl instructions. - let state_dispatch_arms: Vec = match &program.state { - None => vec![], - Some(s) => s - .impl_block_and_methods - .as_ref() - .map(|(_impl_block, methods)| { - methods - .iter() - .map(|ix: &crate::StateIx| { - let ix_method_name: proc_macro2::TokenStream = - format!("__{}", ix.raw_method.sig.ident).parse().expect( - "Failed to parse ix method name with `__` as `TokenStream`", - ); - - let ix_name_camel: proc_macro2::TokenStream = ix - .raw_method - .sig - .ident - .to_string() - .as_str() - .to_camel_case() - .parse() - .expect( - "Failed to parse state ix method name in camel as `TokenStream`", - ); - - quote! { - instruction::state::#ix_name_camel::DISCRIMINATOR => { - __private::__state::#ix_method_name( - program_id, - accounts, - ix_data, - ) - } - } - }) - .collect() - }) - .unwrap_or_default(), - }; - - // Dispatch all trait interface implementations. - let trait_dispatch_arms: Vec = match &program.state { - None => vec![], - Some(s) => s - .interfaces - .as_ref() - .map(|interfaces| { - interfaces - .iter() - .flat_map(|iface: &crate::StateInterface| { - iface - .methods - .iter() - .map(|m: &crate::StateIx| { - let sighash_arr = sighash(&iface.trait_name, &m.ident.to_string()); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - let name = &m.raw_method.sig.ident.to_string(); - let ix_method_name: proc_macro2::TokenStream = - format!("__{}_{}", iface.trait_name, name).parse().unwrap(); - quote! { - #sighash_tts => { - __private::__interface::#ix_method_name( - program_id, - accounts, - ix_data, - ) - } - } - }) - .collect::>() - }) - .collect() - }) - .unwrap_or_default(), - }; - // Dispatch all global instructions. let global_dispatch_arms: Vec = program .ixs @@ -142,11 +40,8 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { /// /// Sha256(":")[..8], /// - /// where the namespace can be one of three types. 1) "global" for a - /// regular instruction, 2) "state" for a state struct instruction - /// handler and 3) a trait namespace (used in combination with the - /// `#[interface]` attribute), which is defined by the trait name, e.. - /// `MyTrait`. + /// where the namespace can be one type. "global" for a + /// regular instruction. /// /// With this 8 byte identifier, Anchor performs method dispatch, /// matching the given 8 byte identifier to the associated method @@ -180,9 +75,6 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { use anchor_lang::Discriminator; match sighash { - #ctor_state_dispatch_arm - #(#state_dispatch_arms)* - #(#trait_dispatch_arms)* #(#global_dispatch_arms)* _ => { #fallback_fn diff --git a/lang/syn/src/codegen/program/entry.rs b/lang/syn/src/codegen/program/entry.rs index 4efa97d9..38fe38db 100644 --- a/lang/syn/src/codegen/program/entry.rs +++ b/lang/syn/src/codegen/program/entry.rs @@ -17,13 +17,9 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { /// code wrapping these user defined methods into something that can be /// executed on Solana. /// - /// These methods fall into one of three categories, each of which - /// can be considered a different "namespace" of the program. + /// These methods fall into one categorie for now. /// - /// 1) Global methods - regular methods inside of the `#[program]`. - /// 2) State methods - associated methods inside a `#[state]` struct. - /// 3) Interface methods - methods inside a strait struct's - /// implementation of an `#[interface]` trait. + /// Global methods - regular methods inside of the `#[program]`. /// /// Care must be taken by the codegen to prevent collisions between /// methods in these different namespaces. For this reason, Anchor uses diff --git a/lang/syn/src/codegen/program/handlers.rs b/lang/syn/src/codegen/program/handlers.rs index f965bd3f..0172aab5 100644 --- a/lang/syn/src/codegen/program/handlers.rs +++ b/lang/syn/src/codegen/program/handlers.rs @@ -1,5 +1,5 @@ use crate::codegen::program::common::*; -use crate::{Program, State}; +use crate::Program; use heck::CamelCase; use quote::{quote, ToTokens}; @@ -193,520 +193,6 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { } } }; - // Constructor handler. - let non_inlined_ctor: proc_macro2::TokenStream = match &program.state { - None => quote! {}, - Some(state) => match state.ctor_and_anchor.as_ref() { - None => quote! {}, - Some((_ctor, anchor_ident)) => { - let ctor_untyped_args = generate_ctor_args(state); - let name = &state.strct.ident; - let mod_name = &program.name; - let variant_arm = generate_ctor_variant(state); - let ix_name: proc_macro2::TokenStream = - generate_ctor_variant_name().parse().unwrap(); - let ix_name_log = format!("Instruction: {}", ix_name); - if state.is_zero_copy { - quote! { - // One time state account initializer. Will faill on subsequent - // invocations. - #[inline(never)] - pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], ix_data: &[u8]) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction data. - let ix = instruction::state::#ix_name::deserialize(&mut &ix_data[..]) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - let instruction::state::#variant_arm = ix; - - let mut __bumps = std::collections::BTreeMap::new(); - let mut __reallocs = std::collections::BTreeSet::new(); - - // Deserialize accounts. - let mut remaining_accounts: &[AccountInfo] = accounts; - let ctor_accounts = - anchor_lang::__private::Ctor::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps, &mut __reallocs)?; - let mut ctor_user_def_accounts = - #anchor_ident::try_accounts(program_id, &mut remaining_accounts, ix_data, &mut __bumps, &mut __reallocs)?; - - // Create the solana account for the ctor data. - let from = ctor_accounts.from.key; - let (base, nonce) = Pubkey::find_program_address(&[], ctor_accounts.program.key); - let seed = anchor_lang::__private::PROGRAM_STATE_SEED; - let owner = ctor_accounts.program.key; - let to = Pubkey::create_with_seed(&base, seed, owner).unwrap(); - let space = 8 + std::mem::size_of::<#name>(); - let rent = Rent::get()?; - let lamports = rent.minimum_balance(std::convert::TryInto::try_into(space).unwrap()); - let seeds = &[&[nonce][..]]; - let ix = anchor_lang::solana_program::system_instruction::create_account_with_seed( - from, - &to, - &base, - seed, - lamports, - space as u64, - owner, - ); - anchor_lang::solana_program::program::invoke_signed( - &ix, - &[ - ctor_accounts.from.clone(), - ctor_accounts.to.clone(), - ctor_accounts.base.clone(), - ctor_accounts.system_program.clone(), - ], - &[seeds], - )?; - - // 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)?; - - // 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()?; - instance.new( - anchor_lang::context::Context::new( - program_id, - &mut ctor_user_def_accounts, - remaining_accounts, - __bumps, - ), - #(#ctor_untyped_args),* - )?; - } - - // Exit routines. - ctor_user_def_accounts.exit(program_id)?; - loader.exit(program_id)?; - - Ok(()) - } - } - } else { - quote! { - // One time state account initializer. Will faill on subsequent - // invocations. - #[inline(never)] - pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], ix_data: &[u8]) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction data. - let ix = instruction::state::#ix_name::deserialize(&mut &ix_data[..]) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - let instruction::state::#variant_arm = ix; - - let mut __bumps = std::collections::BTreeMap::new(); - let mut __reallocs = std::collections::BTreeSet::new(); - - // Deserialize accounts. - let mut remaining_accounts: &[AccountInfo] = accounts; - let ctor_accounts = - anchor_lang::__private::Ctor::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps, &mut __reallocs)?; - let mut ctor_user_def_accounts = - #anchor_ident::try_accounts(program_id, &mut remaining_accounts, ix_data, &mut __bumps, &mut __reallocs)?; - - // Invoke the ctor. - let instance = #mod_name::#name::new( - anchor_lang::context::Context::new( - program_id, - &mut ctor_user_def_accounts, - remaining_accounts, - __bumps, - ), - #(#ctor_untyped_args),* - )?; - - // Create the solana account for the ctor data. - let from = ctor_accounts.from.key; - let (base, nonce) = Pubkey::find_program_address(&[], ctor_accounts.program.key); - let seed = anchor_lang::accounts::state::ProgramState::<#name>::seed(); - let owner = ctor_accounts.program.key; - let to = Pubkey::create_with_seed(&base, seed, owner).unwrap(); - let space = anchor_lang::__private::AccountSize::size(&instance)?; - let rent = Rent::get()?; - let lamports = rent.minimum_balance(std::convert::TryInto::try_into(space).unwrap()); - let seeds = &[&[nonce][..]]; - let ix = anchor_lang::solana_program::system_instruction::create_account_with_seed( - from, - &to, - &base, - seed, - lamports, - space, - owner, - ); - anchor_lang::solana_program::program::invoke_signed( - &ix, - &[ - ctor_accounts.from.clone(), - ctor_accounts.to.clone(), - ctor_accounts.base.clone(), - ctor_accounts.system_program.clone(), - ], - &[seeds], - )?; - - // 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); - instance.try_serialize(&mut cursor)?; - - Ok(()) - } - } - } - } - }, - }; - - // State method handlers. - let non_inlined_state_handlers: Vec = match &program.state { - None => vec![], - Some(state) => state - .impl_block_and_methods - .as_ref() - .map(|(_impl_block, methods)| { - methods - .iter() - .map(|ix| { - let ix_arg_names: Vec<&syn::Ident> = - ix.args.iter().map(|arg| &arg.name).collect(); - let private_ix_method_name: proc_macro2::TokenStream = { - let n = format!("__{}", &ix.raw_method.sig.ident.to_string()); - n.parse().unwrap() - }; - let ix_method_name = &ix.raw_method.sig.ident; - let state_ty: proc_macro2::TokenStream = state.name.parse().unwrap(); - let anchor_ident = &ix.anchor_ident; - let name = &state.strct.ident; - let mod_name = &program.name; - - let variant_arm = - generate_ix_variant(ix.raw_method.sig.ident.to_string(), &ix.args); - let ix_name = generate_ix_variant_name(ix.raw_method.sig.ident.to_string()); - let ix_name_log = format!("Instruction: {}", ix_name); - - if state.is_zero_copy { - quote! { - #[inline(never)] - pub fn #private_ix_method_name( - program_id: &Pubkey, - accounts: &[AccountInfo], - ix_data: &[u8], - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction. - let ix = instruction::state::#ix_name::deserialize(&mut &ix_data[..]) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - let instruction::state::#variant_arm = ix; - - // Bump collector. - let mut __bumps = std::collections::BTreeMap::new(); - - // Realloc tracker - let mut __reallocs= std::collections::BTreeSet::new(); - - // Load state. - let mut remaining_accounts: &[AccountInfo] = accounts; - if remaining_accounts.is_empty() { - return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); - } - let loader: anchor_lang::accounts::loader::Loader<#mod_name::#name> = anchor_lang::accounts::loader::Loader::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps, &mut __reallocs)?; - - // Deserialize accounts. - let mut accounts = #anchor_ident::try_accounts( - program_id, - &mut remaining_accounts, - ix_data, - &mut __bumps, - &mut __reallocs, - )?; - let ctx = - anchor_lang::context::Context::new( - program_id, - &mut accounts, - remaining_accounts, - __bumps, - ); - - // Execute user defined function. - { - let mut state = loader.load_mut()?; - state.#ix_method_name( - ctx, - #(#ix_arg_names),* - )?; - } - // Serialize the state and save it to storage. - accounts.exit(program_id)?; - loader.exit(program_id)?; - - Ok(()) - } - } - } else { - quote! { - #[inline(never)] - pub fn #private_ix_method_name( - program_id: &Pubkey, - accounts: &[AccountInfo], - ix_data: &[u8], - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction. - let ix = instruction::state::#ix_name::deserialize(&mut &ix_data[..]) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - let instruction::state::#variant_arm = ix; - - // Bump collector. - let mut __bumps = std::collections::BTreeMap::new(); - - // Realloc tracker. - let mut __reallocs = std::collections::BTreeSet::new(); - - // Load state. - let mut remaining_accounts: &[AccountInfo] = accounts; - if remaining_accounts.is_empty() { - return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); - } - let mut state: anchor_lang::accounts::state::ProgramState<#state_ty> = anchor_lang::accounts::state::ProgramState::try_accounts( - program_id, - &mut remaining_accounts, - &[], - &mut __bumps, - &mut __reallocs, - )?; - - // Deserialize accounts. - let mut accounts = #anchor_ident::try_accounts( - program_id, - &mut remaining_accounts, - ix_data, - &mut __bumps, - &mut __reallocs, - )?; - let ctx = - anchor_lang::context::Context::new( - program_id, - &mut accounts, - remaining_accounts, - __bumps - ); - - // Execute user defined function. - state.#ix_method_name( - ctx, - #(#ix_arg_names),* - )?; - - // Serialize the state and save it to storage. - accounts.exit(program_id)?; - let acc_info = state.to_account_info(); - let mut data = acc_info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut cursor = std::io::Cursor::new(dst); - state.try_serialize(&mut cursor)?; - - Ok(()) - } - } - } - }) - .collect() - }) - .unwrap_or_default(), - }; - - // State trait handlers. - let non_inlined_state_trait_handlers: Vec = match &program.state { - None => Vec::new(), - Some(state) => state - .interfaces - .as_ref() - .map(|interfaces| { - interfaces - .iter() - .flat_map(|iface: &crate::StateInterface| { - iface - .methods - .iter() - .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."); - - let ix_arg_names: Vec<&syn::Ident> = - ix.args.iter().map(|arg| &arg.name).collect(); - let private_ix_method_name: proc_macro2::TokenStream = { - let n = format!("__{}_{}", iface.trait_name, &ix.raw_method.sig.ident.to_string()); - n.parse().unwrap() - }; - let ix_method_name = &ix.raw_method.sig.ident; - let state_ty: proc_macro2::TokenStream = state.name.parse().unwrap(); - let anchor_ident = &ix.anchor_ident; - let ix_name = generate_ix_variant_name(ix.raw_method.sig.ident.to_string()); - let ix_name_log = format!("Instruction: {}", ix_name); - - let raw_args: Vec<&syn::PatType> = ix - .args - .iter() - .map(|arg: &crate::IxArg| &arg.raw_arg) - .collect(); - let args_struct = { - if ix.args.is_empty() { - quote! { - #[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)] - struct Args; - } - } else { - quote! { - #[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)] - struct Args { - #(#raw_args),* - } - } - } - }; - - let deserialize_instruction = quote! { - #args_struct - let ix = Args::deserialize(&mut &ix_data[..]) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - let Args { - #(#ix_arg_names),* - } = ix; - }; - - if ix.has_receiver { - quote! { - #[inline(never)] - pub fn #private_ix_method_name( - program_id: &Pubkey, - accounts: &[AccountInfo], - ix_data: &[u8], - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction. - #deserialize_instruction - - // Bump collector. - let mut __bumps = std::collections::BTreeMap::new(); - - // Realloc tracker. - let mut __reallocs= std::collections::BTreeSet::new(); - - // Deserialize the program state account. - let mut remaining_accounts: &[AccountInfo] = accounts; - if remaining_accounts.is_empty() { - return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); - } - let mut state: anchor_lang::accounts::state::ProgramState<#state_ty> = anchor_lang::accounts::state::ProgramState::try_accounts( - program_id, - &mut remaining_accounts, - &[], - &mut __bumps, - &mut __reallocs, - )?; - - // Deserialize accounts. - let mut accounts = #anchor_ident::try_accounts( - program_id, - &mut remaining_accounts, - ix_data, - &mut __bumps, - &mut __reallocs, - )?; - let ctx = - anchor_lang::context::Context::new( - program_id, - &mut accounts, - remaining_accounts, - __bumps, - ); - - // Execute user defined function. - state.#ix_method_name( - ctx, - #(#ix_arg_names),* - )?; - - // Exit procedures. - accounts.exit(program_id)?; - let acc_info = state.to_account_info(); - let mut data = acc_info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut cursor = std::io::Cursor::new(dst); - state.try_serialize(&mut cursor)?; - - Ok(()) - } - } - } else { - let state_name: proc_macro2::TokenStream = state.name.parse().unwrap(); - quote! { - #[inline(never)] - pub fn #private_ix_method_name( - program_id: &Pubkey, - accounts: &[AccountInfo], - ix_data: &[u8], - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction. - #deserialize_instruction - - // Bump collector. - let mut __bumps = std::collections::BTreeMap::new(); - - let mut __reallocs = std::collections::BTreeSet::new(); - - // Deserialize accounts. - let mut remaining_accounts: &[AccountInfo] = accounts; - let mut accounts = #anchor_ident::try_accounts( - program_id, - &mut remaining_accounts, - ix_data, - &mut __bumps, - &mut __reallocs, - )?; - - // Execute user defined function. - #state_name::#ix_method_name( - anchor_lang::context::Context::new( - program_id, - &mut accounts, - remaining_accounts, - __bumps - ), - #(#ix_arg_names),* - )?; - - // Exit procedure. - accounts.exit(program_id) - } - } - } - }) - .collect::>() - }) - .collect() - }) - .unwrap_or_default(), - }; let non_inlined_handlers: Vec = program .ixs @@ -789,21 +275,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { #non_inlined_idl } - /// __state mod defines wrapped handlers for state instructions. - pub mod __state { - use super::*; - #non_inlined_ctor - #(#non_inlined_state_handlers)* - } - - /// __interface mod defines wrapped handlers for `#[interface]` trait - /// implementations. - pub mod __interface { - use super::*; - - #(#non_inlined_state_trait_handlers)* - } /// __global mod defines wrapped handlers for global instructions. pub mod __global { @@ -819,23 +291,3 @@ fn generate_ix_variant_name(name: String) -> proc_macro2::TokenStream { let n = name.to_camel_case(); n.parse().unwrap() } - -fn generate_ctor_variant_name() -> String { - "New".to_string() -} - -fn generate_ctor_variant(state: &State) -> proc_macro2::TokenStream { - let ctor_args = generate_ctor_args(state); - let ctor_variant_name: proc_macro2::TokenStream = generate_ctor_variant_name().parse().unwrap(); - if ctor_args.is_empty() { - quote! { - #ctor_variant_name - } - } else { - quote! { - #ctor_variant_name { - #(#ctor_args),* - } - } - } -} diff --git a/lang/syn/src/codegen/program/instruction.rs b/lang/syn/src/codegen/program/instruction.rs index 53e56271..d248dda0 100644 --- a/lang/syn/src/codegen/program/instruction.rs +++ b/lang/syn/src/codegen/program/instruction.rs @@ -5,122 +5,6 @@ use heck::CamelCase; use quote::quote; pub fn generate(program: &Program) -> proc_macro2::TokenStream { - let ctor_variant = match &program.state { - None => quote! {}, - Some(state) => { - let ctor_args: Vec = generate_ctor_typed_args(state) - .iter() - .map(|arg| { - format!("pub {}", parser::tts_to_string(arg)) - .parse() - .unwrap() - }) - .collect(); - let strct = { - if ctor_args.is_empty() { - quote! { - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct New; - } - } else { - quote! { - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct New { - #(#ctor_args),* - } - } - } - }; - let sighash_arr = sighash_ctor(); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - quote! { - /// Instruction arguments to the `#[state]`'s `new` - /// constructor. - #strct - - impl anchor_lang::Discriminator for New { - const DISCRIMINATOR: [u8; 8] = #sighash_tts; - } - impl anchor_lang::InstructionData for New {} - impl anchor_lang::Owner for New { - fn owner() -> Pubkey { - ID - } - } - } - } - }; - let state_method_variants: Vec = match &program.state { - None => vec![], - Some(state) => state - .impl_block_and_methods - .as_ref() - .map(|(_impl_block, methods)| { - methods - .iter() - .map(|method| { - let ix_name_camel: proc_macro2::TokenStream = method - .raw_method - .sig - .ident - .to_string() - .to_camel_case() - .parse() - .unwrap(); - let raw_args: Vec = method - .args - .iter() - .map(|arg| { - format!("pub {}", parser::tts_to_string(&arg.raw_arg)) - .parse() - .unwrap() - }) - .collect(); - - let ix_data_trait = { - let name = method.raw_method.sig.ident.to_string(); - let sighash_arr = sighash(SIGHASH_STATE_NAMESPACE, &name); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - quote! { - impl anchor_lang::Discriminator for #ix_name_camel { - const DISCRIMINATOR: [u8; 8] = #sighash_tts; - } - impl anchor_lang::InstructionData for #ix_name_camel {} - impl anchor_lang::Owner for #ix_name_camel { - fn owner() -> Pubkey { - ID - } - } - } - }; - - // If no args, output a "unit" variant instead of a struct variant. - if method.args.is_empty() { - quote! { - /// Anchor generated instruction. - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct #ix_name_camel; - - #ix_data_trait - } - } else { - quote! { - /// Anchor generated instruction. - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct #ix_name_camel { - #(#raw_args),* - } - - #ix_data_trait - } - } - }) - .collect() - }) - .unwrap_or_default(), - }; let variants: Vec = program .ixs .iter() @@ -186,13 +70,6 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { pub mod instruction { use super::*; - /// Instruction struct definitions for `#[state]` methods. - pub mod state { - use super::*; - - #ctor_variant - #(#state_method_variants)* - } #(#variants)* } diff --git a/lang/syn/src/idl/file.rs b/lang/syn/src/idl/file.rs index 88f4723d..908a1285 100644 --- a/lang/syn/src/idl/file.rs +++ b/lang/syn/src/idl/file.rs @@ -2,7 +2,7 @@ use crate::idl::*; use crate::parser::context::CrateContext; use crate::parser::{self, accounts, docs, error, program}; use crate::Ty; -use crate::{AccountField, AccountsStruct, StateIx}; +use crate::{AccountField, AccountsStruct}; use anyhow::Result; use heck::MixedCase; use quote::ToTokens; @@ -45,139 +45,6 @@ pub fn parse( let accs = parse_account_derives(&ctx); - let state = match p.state { - None => None, - Some(state) => match state.ctor_and_anchor { - None => None, // State struct defined but no implementation - Some((ctor, anchor_ident)) => { - let mut methods = state - .impl_block_and_methods - .map(|(_impl_block, methods)| { - methods - .iter() - .map(|method: &StateIx| { - let name = method.ident.to_string().to_mixed_case(); - let args = method - .args - .iter() - .map(|arg| { - let mut tts = proc_macro2::TokenStream::new(); - arg.raw_arg.ty.to_tokens(&mut tts); - let doc = if !no_docs { - docs::parse(&arg.raw_arg.attrs) - } else { - None - }; - let ty = tts.to_string().parse().unwrap(); - IdlField { - name: arg.name.to_string().to_mixed_case(), - docs: doc, - ty, - } - }) - .collect::>(); - let accounts_strct = - accs.get(&method.anchor_ident.to_string()).unwrap(); - let accounts = idl_accounts( - &ctx, - accounts_strct, - &accs, - seeds_feature, - no_docs, - ); - IdlInstruction { - name, - docs: None, - accounts, - args, - returns: None, - } - }) - .collect::>() - }) - .unwrap_or_default(); - let ctor = { - let name = "new".to_string(); - let args = ctor - .sig - .inputs - .iter() - .filter(|arg| match arg { - syn::FnArg::Typed(pat_ty) => { - // TODO: this filtering should be done in the parser. - let mut arg_str = parser::tts_to_string(&pat_ty.ty); - arg_str.retain(|c| !c.is_whitespace()); - !arg_str.starts_with("Context<") - } - _ => false, - }) - .map(|arg: &syn::FnArg| match arg { - syn::FnArg::Typed(arg_typed) => { - let mut tts = proc_macro2::TokenStream::new(); - arg_typed.ty.to_tokens(&mut tts); - let doc = if !no_docs { - docs::parse(&arg_typed.attrs) - } else { - None - }; - let ty = tts.to_string().parse().unwrap(); - IdlField { - name: parser::tts_to_string(&arg_typed.pat).to_mixed_case(), - docs: doc, - ty, - } - } - _ => panic!("Invalid syntax"), - }) - .collect(); - let accounts_strct = accs.get(&anchor_ident.to_string()).unwrap(); - let accounts = - idl_accounts(&ctx, accounts_strct, &accs, seeds_feature, no_docs); - IdlInstruction { - name, - docs: None, - accounts, - args, - returns: None, - } - }; - - methods.insert(0, ctor); - - let strct = { - let fields = match state.strct.fields { - syn::Fields::Named(f_named) => f_named - .named - .iter() - .map(|f: &syn::Field| { - let mut tts = proc_macro2::TokenStream::new(); - f.ty.to_tokens(&mut tts); - let doc = if !no_docs { - docs::parse(&f.attrs) - } else { - None - }; - let ty = tts.to_string().parse().unwrap(); - IdlField { - name: f.ident.as_ref().unwrap().to_string().to_mixed_case(), - docs: doc, - ty, - } - }) - .collect::>(), - _ => panic!("State must be a struct"), - }; - IdlTypeDefinition { - name: state.name, - docs: None, - ty: IdlTypeDefinitionTy::Struct { fields }, - } - }; - - Some(IdlState { strct, methods }) - } - }, - }; let error = parse_error_enum(&ctx).map(|mut e| error::parse(&mut e, None)); let error_codes = error.as_ref().map(|e| { e.codes @@ -292,7 +159,6 @@ pub fn parse( version, name: p.name.to_string(), docs: p.docs.clone(), - state, instructions, types, accounts, diff --git a/lang/syn/src/idl/mod.rs b/lang/syn/src/idl/mod.rs index 91352906..6729ca4a 100644 --- a/lang/syn/src/idl/mod.rs +++ b/lang/syn/src/idl/mod.rs @@ -14,8 +14,6 @@ pub struct Idl { #[serde(skip_serializing_if = "Vec::is_empty", default)] pub constants: Vec, pub instructions: Vec, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub state: Option, #[serde(skip_serializing_if = "Vec::is_empty", default)] pub accounts: Vec, #[serde(skip_serializing_if = "Vec::is_empty", default)] diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index d68a9fe9..13a9935c 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -14,8 +14,8 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::Comma; use syn::{ - Expr, Generics, Ident, ImplItemMethod, ItemEnum, ItemFn, ItemImpl, ItemMod, ItemStruct, LitInt, - LitStr, PatType, Token, Type, TypePath, + Expr, Generics, Ident, ItemEnum, ItemFn, ItemMod, ItemStruct, LitInt, LitStr, PatType, Token, + Type, TypePath, }; pub mod codegen; @@ -29,7 +29,6 @@ pub mod parser; #[derive(Debug)] pub struct Program { - pub state: Option, pub ixs: Vec, pub name: Ident, pub docs: Option>, @@ -56,32 +55,6 @@ impl ToTokens for Program { } } -#[derive(Debug)] -pub struct State { - pub name: String, - pub strct: ItemStruct, - pub ctor_and_anchor: Option<(ImplItemMethod, Ident)>, - pub impl_block_and_methods: Option<(ItemImpl, Vec)>, - pub interfaces: Option>, - pub is_zero_copy: bool, -} - -#[derive(Debug)] -pub struct StateIx { - pub raw_method: ImplItemMethod, - pub ident: Ident, - pub args: Vec, - pub anchor_ident: Ident, - // True if there exists a &self on the method. - pub has_receiver: bool, -} - -#[derive(Debug)] -pub struct StateInterface { - pub trait_name: String, - pub methods: Vec, -} - #[derive(Debug)] pub struct Ix { pub raw_method: ItemFn, @@ -449,8 +422,6 @@ impl Field { anchor_lang::accounts::cpi_account::CpiAccount }, Ty::Sysvar(_) => quote! { anchor_lang::accounts::sysvar::Sysvar }, - Ty::CpiState(_) => quote! { anchor_lang::accounts::cpi_state::CpiState }, - Ty::ProgramState(_) => quote! { anchor_lang::accounts::state::ProgramState }, Ty::Program(_) => quote! { anchor_lang::accounts::program::Program }, Ty::AccountInfo => quote! {}, Ty::UncheckedAccount => quote! {}, @@ -508,18 +479,6 @@ impl Field { #ident } } - Ty::ProgramState(ty) => { - let account = &ty.account_type_path; - quote! { - #account - } - } - Ty::CpiState(ty) => { - let account = &ty.account_type_path; - quote! { - #account - } - } Ty::Sysvar(ty) => match ty { SysvarTy::Clock => quote! {Clock}, SysvarTy::Rent => quote! {Rent}, @@ -557,8 +516,6 @@ pub struct CompositeField { pub enum Ty { AccountInfo, UncheckedAccount, - ProgramState(ProgramStateTy), - CpiState(CpiStateTy), ProgramAccount(ProgramAccountTy), Loader(LoaderTy), AccountLoader(AccountLoaderTy), @@ -585,16 +542,6 @@ pub enum SysvarTy { Rewards, } -#[derive(Debug, PartialEq, Eq)] -pub struct ProgramStateTy { - pub account_type_path: TypePath, -} - -#[derive(Debug, PartialEq, Eq)] -pub struct CpiStateTy { - pub account_type_path: TypePath, -} - #[derive(Debug, PartialEq, Eq)] pub struct ProgramAccountTy { // The struct type of the account. @@ -679,7 +626,6 @@ pub struct ConstraintGroup { rent_exempt: Option, seeds: Option, executable: Option, - state: Option, has_one: Vec, literal: Vec, raw: Vec, @@ -727,7 +673,6 @@ pub enum Constraint { Seeds(ConstraintSeedsGroup), AssociatedToken(ConstraintAssociatedToken), Executable(ConstraintExecutable), - State(ConstraintState), Close(ConstraintClose), Address(ConstraintAddress), TokenAccount(ConstraintTokenAccountGroup), @@ -750,7 +695,6 @@ pub enum ConstraintToken { RentExempt(Context), Seeds(Context), Executable(Context), - State(Context), Close(Context), Payer(Context), Space(Context), @@ -878,11 +822,6 @@ pub struct ConstraintSeeds { #[derive(Debug, Clone)] pub struct ConstraintExecutable {} -#[derive(Debug, Clone)] -pub struct ConstraintState { - pub program_target: Ident, -} - #[derive(Debug, Clone)] pub struct ConstraintPayer { pub target: Expr, diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs index ac71a680..e02eb7ef 100644 --- a/lang/syn/src/parser/accounts/constraints.rs +++ b/lang/syn/src/parser/accounts/constraints.rs @@ -271,12 +271,6 @@ pub fn parse_token(stream: ParseStream) -> ParseResult { } }, )), - "state" => ConstraintToken::State(Context::new( - span, - ConstraintState { - program_target: stream.parse()?, - }, - )), "payer" => ConstraintToken::Payer(Context::new( span, ConstraintPayer { @@ -340,7 +334,6 @@ pub struct ConstraintGroupBuilder<'ty> { pub rent_exempt: Option>, pub seeds: Option>, pub executable: Option>, - pub state: Option>, pub payer: Option>, pub space: Option>, pub close: Option>, @@ -374,7 +367,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { rent_exempt: None, seeds: None, executable: None, - state: None, payer: None, space: None, close: None, @@ -575,7 +567,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { rent_exempt, seeds, executable, - state, payer, space, close, @@ -724,7 +715,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { owner: into_inner!(owner), rent_exempt: into_inner!(rent_exempt), executable: into_inner!(executable), - state: into_inner!(state), close: into_inner!(close), address: into_inner!(address), associated_token: if !is_init { associated_token } else { None }, @@ -747,7 +737,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { ConstraintToken::RentExempt(c) => self.add_rent_exempt(c), ConstraintToken::Seeds(c) => self.add_seeds(c), ConstraintToken::Executable(c) => self.add_executable(c), - ConstraintToken::State(c) => self.add_state(c), ConstraintToken::Payer(c) => self.add_payer(c), ConstraintToken::Space(c) => self.add_space(c), ConstraintToken::Close(c) => self.add_close(c), @@ -1115,14 +1104,6 @@ impl<'ty> ConstraintGroupBuilder<'ty> { Ok(()) } - fn add_state(&mut self, c: Context) -> ParseResult<()> { - if self.state.is_some() { - return Err(ParseError::new(c.span(), "state already provided")); - } - self.state.replace(c); - Ok(()) - } - fn add_payer(&mut self, c: Context) -> ParseResult<()> { if self.init.is_none() { return Err(ParseError::new( diff --git a/lang/syn/src/parser/accounts/mod.rs b/lang/syn/src/parser/accounts/mod.rs index b5def650..ae4a4ab5 100644 --- a/lang/syn/src/parser/accounts/mod.rs +++ b/lang/syn/src/parser/accounts/mod.rs @@ -283,13 +283,11 @@ pub fn parse_account_field(f: &syn::Field) -> ParseResult { fn is_field_primitive(f: &syn::Field) -> ParseResult { let r = matches!( ident_string(f)?.0.as_str(), - "ProgramState" - | "ProgramAccount" + "ProgramAccount" | "CpiAccount" | "Sysvar" | "AccountInfo" | "UncheckedAccount" - | "CpiState" | "Loader" | "AccountLoader" | "Account" @@ -304,8 +302,6 @@ fn is_field_primitive(f: &syn::Field) -> ParseResult { fn parse_ty(f: &syn::Field) -> ParseResult<(Ty, bool)> { let (ident, optional, path) = ident_string(f)?; let ty = match ident.as_str() { - "ProgramState" => Ty::ProgramState(parse_program_state(&path)?), - "CpiState" => Ty::CpiState(parse_cpi_state(&path)?), "ProgramAccount" => Ty::ProgramAccount(parse_program_account(&path)?), "CpiAccount" => Ty::CpiAccount(parse_cpi_account(&path)?), "Sysvar" => Ty::Sysvar(parse_sysvar(&path)?), @@ -380,20 +376,6 @@ fn ident_string(f: &syn::Field) -> ParseResult<(String, bool, Path)> { Ok((segments.ident.to_string(), optional, path)) } -fn parse_program_state(path: &syn::Path) -> ParseResult { - let account_ident = parse_account(path)?; - Ok(ProgramStateTy { - account_type_path: account_ident, - }) -} - -fn parse_cpi_state(path: &syn::Path) -> ParseResult { - let account_ident = parse_account(path)?; - Ok(CpiStateTy { - account_type_path: account_ident, - }) -} - fn parse_cpi_account(path: &syn::Path) -> ParseResult { let account_ident = parse_account(path)?; Ok(CpiAccountTy { diff --git a/lang/syn/src/parser/program/mod.rs b/lang/syn/src/parser/program/mod.rs index 87c29843..412d921d 100644 --- a/lang/syn/src/parser/program/mod.rs +++ b/lang/syn/src/parser/program/mod.rs @@ -4,14 +4,11 @@ use syn::parse::{Error as ParseError, Result as ParseResult}; use syn::spanned::Spanned; mod instructions; -mod state; pub fn parse(program_mod: syn::ItemMod) -> ParseResult { - let state = state::parse(&program_mod)?; let docs = docs::parse(&program_mod.attrs); let (ixs, fallback_fn) = instructions::parse(&program_mod)?; Ok(Program { - state, ixs, name: program_mod.ident.clone(), docs, diff --git a/lang/syn/src/parser/program/state.rs b/lang/syn/src/parser/program/state.rs deleted file mode 100644 index 04510f2a..00000000 --- a/lang/syn/src/parser/program/state.rs +++ /dev/null @@ -1,315 +0,0 @@ -use crate::parser; -use crate::parser::docs; -use crate::parser::program::ctx_accounts_ident; -use crate::{IxArg, State, StateInterface, StateIx}; -use syn::parse::{Error as ParseError, Result as ParseResult}; -use syn::spanned::Spanned; - -// Name of the attribute denoting a state struct. -const STATE_STRUCT_ATTRIBUTE: &str = "state"; - -// Reserved keyword for the constructor method. -const CTOR_METHOD_NAME: &str = "new"; - -// Parse the state from the program mod definition. -pub fn parse(program_mod: &syn::ItemMod) -> ParseResult> { - let mod_content = &program_mod - .content - .as_ref() - .ok_or_else(|| ParseError::new(program_mod.span(), "program content not provided"))? - .1; - - // Parse `struct` marked with the `#[state]` attribute. - let strct: Option<(&syn::ItemStruct, bool)> = mod_content - .iter() - .filter_map(|item| match item { - syn::Item::Struct(item_strct) => { - let attrs = &item_strct.attrs; - if attrs.is_empty() { - return None; - } - let attr_label = attrs[0].path.get_ident().map(|i| i.to_string()); - if attr_label != Some(STATE_STRUCT_ATTRIBUTE.to_string()) { - return None; - } - let is_zero_copy = parser::tts_to_string(&attrs[0].tokens) == "(zero_copy)"; - Some((item_strct, is_zero_copy)) - } - _ => None, - }) - .next(); - - // Parse `impl` block for the state struct. - let impl_block: Option = match strct { - None => None, - Some((strct, _)) => mod_content - .iter() - .filter_map(|item| match item { - syn::Item::Impl(item_impl) => { - let impl_ty_str = parser::tts_to_string(&item_impl.self_ty); - let strct_name = strct.ident.to_string(); - if item_impl.trait_.is_some() { - return None; - } - if strct_name != impl_ty_str { - return None; - } - Some(item_impl.clone()) - } - _ => None, - }) - .next(), - }; - - // Parse ctor and the generic type in `Context`. - let ctor_and_anchor: Option<(syn::ImplItemMethod, syn::Ident)> = impl_block - .as_ref() - .map(|impl_block| { - let r: Option> = impl_block - .items - .iter() - .filter_map(|item: &syn::ImplItem| match item { - syn::ImplItem::Method(m) => match m.sig.ident == CTOR_METHOD_NAME { - false => None, - true => Some(m), - }, - _ => None, - }) - .map(|m: &syn::ImplItemMethod| { - let (_, is_zero_copy) = strct - .as_ref() - .expect("impl_block exists therefore the struct exists"); - let ctx_arg = { - if *is_zero_copy { - // Second param is context. - let mut iter = m.sig.inputs.iter(); - match iter.next() { - None => { - return Err(ParseError::new( - m.sig.span(), - "first parameter must be &mut self", - )) - } - Some(arg) => match arg { - syn::FnArg::Receiver(r) => { - if r.mutability.is_none() { - return Err(ParseError::new( - m.sig.span(), - "first parameter must be &mut self", - )); - } - } - syn::FnArg::Typed(_) => { - return Err(ParseError::new( - m.sig.span(), - "first parameter must be &mut self", - )) - } - }, - }; - match iter.next() { - None => { - return Err(ParseError::new( - m.sig.span(), - "second parameter must be the Context", - )) - } - Some(ctx_arg) => match ctx_arg { - syn::FnArg::Receiver(_) => { - return Err(ParseError::new( - ctx_arg.span(), - "second parameter must be the Context", - )) - } - syn::FnArg::Typed(arg) => arg, - }, - } - } else { - match m.sig.inputs.first() { - None => { - return Err(ParseError::new( - m.sig.span(), - "first parameter must be the Context", - )) - } - Some(ctx_arg) => match ctx_arg { - syn::FnArg::Receiver(_) => { - return Err(ParseError::new( - ctx_arg.span(), - "second parameter must be the Context", - )) - } - syn::FnArg::Typed(arg) => arg, - }, - } - } - }; - Ok((m.clone(), ctx_accounts_ident(ctx_arg)?)) - }) - .next(); - r.transpose() - }) - .transpose()? - .unwrap_or(None); - - // Parse all methods in the above `impl` block. - let methods: Option> = impl_block - .as_ref() - .map(|impl_block| { - impl_block - .items - .iter() - .filter_map(|item| match item { - syn::ImplItem::Method(m) => match m.sig.ident != CTOR_METHOD_NAME { - false => None, - true => Some(m), - }, - _ => None, - }) - .map(|m: &syn::ImplItemMethod| { - let mut args = m - .sig - .inputs - .iter() - .filter_map(|arg| match arg { - syn::FnArg::Receiver(_) => None, - syn::FnArg::Typed(arg) => Some(arg), - }) - .map(|raw_arg| { - let docs = docs::parse(&raw_arg.attrs); - let ident = match &*raw_arg.pat { - syn::Pat::Ident(ident) => &ident.ident, - _ => { - return Err(ParseError::new( - raw_arg.pat.span(), - "unexpected type argument", - )) - } - }; - Ok(IxArg { - name: ident.clone(), - docs, - raw_arg: raw_arg.clone(), - }) - }) - .collect::>>()?; - // Remove the Anchor accounts argument - let anchor = args.remove(0); - let anchor_ident = ctx_accounts_ident(&anchor.raw_arg)?; - - Ok(StateIx { - raw_method: m.clone(), - ident: m.sig.ident.clone(), - args, - anchor_ident, - has_receiver: true, - }) - }) - .collect::>>() - }) - .transpose()?; - - // Parse all trait implementations for the above `#[state]` struct. - let trait_impls: Option> = strct - .map(|_strct| { - mod_content - .iter() - .filter_map(|item| match item { - syn::Item::Impl(item_impl) => match &item_impl.trait_ { - None => None, - Some((_, path, _)) => { - let trait_name = path - .segments - .iter() - .next() - .expect("Must have one segment in a path") - .ident - .clone() - .to_string(); - Some((item_impl, trait_name)) - } - }, - _ => None, - }) - .map(|(item_impl, trait_name)| { - let methods = item_impl - .items - .iter() - .filter_map(|item: &syn::ImplItem| match item { - syn::ImplItem::Method(m) => Some(m), - _ => None, - }) - .map(|m: &syn::ImplItemMethod| { - match m.sig.inputs.first() { - None => Err(ParseError::new( - m.sig.inputs.span(), - "state methods must have a self argument", - )), - Some(_arg) => { - let mut has_receiver = false; - let mut args = m - .sig - .inputs - .iter() - .filter_map(|arg| match arg { - syn::FnArg::Receiver(_) => { - has_receiver = true; - None - } - syn::FnArg::Typed(arg) => Some(arg), - }) - .map(|raw_arg| { - let docs = docs::parse(&raw_arg.attrs); - let ident = match &*raw_arg.pat { - syn::Pat::Ident(ident) => &ident.ident, - _ => panic!("invalid syntax"), - }; - IxArg { - name: ident.clone(), - docs, - raw_arg: raw_arg.clone(), - } - }) - .collect::>(); - // Remove the Anchor accounts argument - let anchor = args.remove(0); - let anchor_ident = ctx_accounts_ident(&anchor.raw_arg)?; - - Ok(StateIx { - raw_method: m.clone(), - ident: m.sig.ident.clone(), - args, - anchor_ident, - has_receiver, - }) - } - } - }) - .collect::>>()?; - Ok(StateInterface { - trait_name, - methods, - }) - }) - .collect::>>() - }) - .transpose()?; - - Ok(strct.map(|(strct, is_zero_copy)| { - // Chop off the `#[state]` attribute. It's just a marker. - // - // TODO: instead of mutating the syntax, we should just implement - // a macro that does nothing. - let mut strct = strct.clone(); - strct.attrs = vec![]; - - State { - name: strct.ident.to_string(), - strct, - interfaces: trait_impls, - impl_block_and_methods: impl_block.map(|impl_block| (impl_block, methods.unwrap())), - ctor_and_anchor, - is_zero_copy, - } - })) -} diff --git a/tests/interface/Anchor.toml b/tests/interface/Anchor.toml deleted file mode 100644 index 3a313174..00000000 --- a/tests/interface/Anchor.toml +++ /dev/null @@ -1,12 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -counter = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" -counter_auth = "Aws2XRVHjNqCUbMmaU245ojT2DBJFYX58KVo2YySEeeP" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" - -[features] diff --git a/tests/interface/Cargo.toml b/tests/interface/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/interface/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/interface/package.json b/tests/interface/package.json deleted file mode 100644 index d5342217..00000000 --- a/tests/interface/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "interface", - "version": "0.26.0", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/coral-xyz/anchor#readme", - "bugs": { - "url": "https://github.com/coral-xyz/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/coral-xyz/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/interface/programs/counter-auth/Cargo.toml b/tests/interface/programs/counter-auth/Cargo.toml deleted file mode 100644 index abf20bb5..00000000 --- a/tests/interface/programs/counter-auth/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "counter-auth" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.59" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "counter_auth" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -counter = { path = "../counter", features = ["cpi"] } diff --git a/tests/interface/programs/counter-auth/Xargo.toml b/tests/interface/programs/counter-auth/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/interface/programs/counter-auth/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/interface/programs/counter-auth/src/lib.rs b/tests/interface/programs/counter-auth/src/lib.rs deleted file mode 100644 index 544c74a0..00000000 --- a/tests/interface/programs/counter-auth/src/lib.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! counter-auth is an example of a program *implementing* an external program -//! interface. Here the `counter::Auth` trait, where we only allow a count -//! to be incremented if it changes the counter from odd -> even or even -> odd. -//! Creative, I know. :P. - -use anchor_lang::prelude::*; -use counter::Auth; - -declare_id!("Aws2XRVHjNqCUbMmaU245ojT2DBJFYX58KVo2YySEeeP"); - -#[program] -pub mod counter_auth { - use super::*; - - #[state] - pub struct CounterAuth; - - impl<'info> Auth<'info, Empty> for CounterAuth { - fn is_authorized(_ctx: Context, current: u64, new: u64) -> Result<()> { - if current % 2 == 0 { - if new % 2 == 0 { - return Err(ProgramError::Custom(15000).into()); // Arbitrary error code. - } - } else { - if new % 2 == 1 { - return Err(ProgramError::Custom(16000).into()); // Arbitrary error code. - } - } - Ok(()) - } - } -} - -#[derive(Accounts)] -pub struct Empty {} diff --git a/tests/interface/programs/counter/Cargo.toml b/tests/interface/programs/counter/Cargo.toml deleted file mode 100644 index d73bc60d..00000000 --- a/tests/interface/programs/counter/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "counter" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.59" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "counter" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/interface/programs/counter/Xargo.toml b/tests/interface/programs/counter/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/interface/programs/counter/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/interface/programs/counter/src/lib.rs b/tests/interface/programs/counter/src/lib.rs deleted file mode 100644 index b3f28b39..00000000 --- a/tests/interface/programs/counter/src/lib.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! counter is an example program that depends on an external interface -//! that another program (here counter-auth/src/lib.rs) must implement. This allows -//! our program to depend on another program, without knowing anything about it -//! other than that it implements the `Auth` trait. -//! -//! Here, we have a counter, where, in order to set the count, the `Auth` -//! program must first approve the transaction. - -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod counter { - use super::*; - - #[state] - pub struct Counter { - pub count: u64, - pub auth_program: Pubkey, - } - - impl Counter { - pub fn new(_ctx: Context, auth_program: Pubkey) -> Result { - Ok(Self { - count: 0, - auth_program, - }) - } - - #[access_control(SetCount::accounts(&self, &ctx))] - pub fn set_count(&mut self, ctx: Context, new_count: u64) -> Result<()> { - // Ask the auth program if we should approve the transaction. - let cpi_program = ctx.accounts.auth_program.clone(); - let cpi_ctx = CpiContext::new(cpi_program, Empty {}); - auth::is_authorized(cpi_ctx, self.count, new_count)?; - - // Approved, so update. - self.count = new_count; - Ok(()) - } - } -} - -#[derive(Accounts)] -pub struct Empty {} - -#[derive(Accounts)] -pub struct SetCount<'info> { - auth_program: AccountInfo<'info>, -} - -impl<'info> SetCount<'info> { - // Auxiliary account validation requiring program inputs. As a convention, - // we separate it from the business logic of the instruction handler itself. - pub fn accounts(counter: &Counter, ctx: &Context) -> Result<()> { - if ctx.accounts.auth_program.key != &counter.auth_program { - return err!(ErrorCode::InvalidAuthProgram); - } - Ok(()) - } -} - -#[interface] -pub trait Auth<'info, T: Accounts<'info>> { - fn is_authorized(ctx: Context, current: u64, new: u64) -> Result<()>; -} - -#[error_code] -pub enum ErrorCode { - #[msg("Invalid auth program.")] - InvalidAuthProgram, -} diff --git a/tests/interface/tests/interface.js b/tests/interface/tests/interface.js deleted file mode 100644 index 4320944b..00000000 --- a/tests/interface/tests/interface.js +++ /dev/null @@ -1,46 +0,0 @@ -const anchor = require("@coral-xyz/anchor"); -const { assert } = require("chai"); -const nativeAssert = require("assert"); - -describe("interface", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const counter = anchor.workspace.Counter; - const counterAuth = anchor.workspace.CounterAuth; - it("Is initialized!", async () => { - await counter.state.rpc.new(counterAuth.programId); - - const stateAccount = await counter.state.fetch(); - assert.isTrue(stateAccount.count.eq(new anchor.BN(0))); - assert.isTrue(stateAccount.authProgram.equals(counterAuth.programId)); - }); - - it("Should fail to go from even to even", async () => { - await nativeAssert.rejects( - async () => { - await counter.state.rpc.setCount(new anchor.BN(4), { - accounts: { - authProgram: counterAuth.programId, - }, - }); - }, - (err) => { - if (err.toString().split("custom program error: 0x3a98").length !== 2) { - return false; - } - return true; - } - ); - }); - - it("Should succeed to go from even to odd", async () => { - await counter.state.rpc.setCount(new anchor.BN(3), { - accounts: { - authProgram: counterAuth.programId, - }, - }); - const stateAccount = await counter.state.fetch(); - assert.isTrue(stateAccount.count.eq(new anchor.BN(3))); - }); -}); diff --git a/tests/misc/Anchor.toml b/tests/misc/Anchor.toml index d7dd74bc..5c8022e1 100644 --- a/tests/misc/Anchor.toml +++ b/tests/misc/Anchor.toml @@ -4,7 +4,6 @@ wallet = "~/.config/solana/id.json" [programs.localnet] misc = "3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh" -misc2 = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L" misc_optional = "FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG" idl_doc = "BqmKjZGVa8fqyWuojJzG16zaKSV1GjAisZToNuvEaz6m" init_if_needed = "BZoppwWi6jMnydnUBEJzotgEXHwLr3b3NramJgZtWeF2" diff --git a/tests/misc/programs/misc-optional/Cargo.toml b/tests/misc/programs/misc-optional/Cargo.toml index 526b5c59..e976f249 100644 --- a/tests/misc/programs/misc-optional/Cargo.toml +++ b/tests/misc/programs/misc-optional/Cargo.toml @@ -18,5 +18,4 @@ default = [] [dependencies] anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } anchor-spl = { path = "../../../../spl" } -misc2 = { path = "../misc2", features = ["cpi"] } spl-associated-token-account = "1.1.1" diff --git a/tests/misc/programs/misc-optional/src/context.rs b/tests/misc/programs/misc-optional/src/context.rs index 4f75a101..a23294db 100644 --- a/tests/misc/programs/misc-optional/src/context.rs +++ b/tests/misc/programs/misc-optional/src/context.rs @@ -1,10 +1,8 @@ use crate::account::*; -use anchor_lang::accounts::cpi_state::CpiState; use anchor_lang::accounts::loader::Loader; use anchor_lang::prelude::*; use anchor_spl::associated_token::AssociatedToken; use anchor_spl::token::{Mint, Token, TokenAccount}; -use misc2::misc2::MyState as Misc2State; #[derive(Accounts)] pub struct TestTokenSeedsInit<'info> { @@ -120,12 +118,6 @@ pub struct TestPdaMutZeroCopy<'info> { pub my_payer: Option>, } -#[derive(Accounts)] -pub struct Ctor {} - -#[derive(Accounts)] -pub struct RemainingAccounts {} - #[derive(Accounts)] pub struct Initialize<'info> { #[account(zero)] @@ -160,18 +152,6 @@ pub struct TestExecutable<'info> { pub program: Option>, } -#[derive(Accounts)] -pub struct TestStateCpi<'info> { - #[account(signer)] - /// CHECK: - pub authority: Option>, - #[account(mut, state = misc2_program)] - pub cpi_state: Option>, - #[account(executable)] - /// CHECK: - pub misc2_program: Option>, -} - #[derive(Accounts)] pub struct TestClose<'info> { #[account(mut, close = sol_dest)] diff --git a/tests/misc/programs/misc-optional/src/lib.rs b/tests/misc/programs/misc-optional/src/lib.rs index aa7e042d..e903425a 100644 --- a/tests/misc/programs/misc-optional/src/lib.rs +++ b/tests/misc/programs/misc-optional/src/lib.rs @@ -5,7 +5,6 @@ use account::MAX_SIZE; use anchor_lang::prelude::*; use context::*; use event::*; -use misc2::Auth; mod account; mod context; @@ -23,26 +22,6 @@ pub const NO_IDL: u16 = 55; pub mod misc_optional { use super::*; - pub const SIZE: u64 = 99; - - #[state(SIZE)] - pub struct MyState { - pub v: Vec, - } - - impl MyState { - pub fn new(_ctx: Context) -> Result { - Ok(Self { v: vec![] }) - } - - pub fn remaining_accounts(&mut self, ctx: Context) -> Result<()> { - if ctx.remaining_accounts.len() != 1 { - return Err(ProgramError::Custom(1).into()); // Arbitrary error. - } - Ok(()) - } - } - pub fn initialize(ctx: Context, udata: u128, idata: i128) -> Result<()> { ctx.accounts.data.as_mut().unwrap().udata = udata; ctx.accounts.data.as_mut().unwrap().idata = idata; @@ -65,20 +44,6 @@ pub mod misc_optional { Ok(()) } - pub fn test_state_cpi(ctx: Context, data: u64) -> Result<()> { - let cpi_program = ctx.accounts.misc2_program.as_ref().unwrap().clone(); - let cpi_accounts = Auth { - authority: ctx.accounts.authority.as_ref().unwrap().clone(), - }; - let ctx = ctx - .accounts - .cpi_state - .as_ref() - .unwrap() - .context(cpi_program, cpi_accounts); - misc2::cpi::state::set_data(ctx, data) - } - pub fn test_u16(ctx: Context, data: u16) -> Result<()> { ctx.accounts.my_account.as_mut().unwrap().data = data; Ok(()) diff --git a/tests/misc/programs/misc/Cargo.toml b/tests/misc/programs/misc/Cargo.toml index 2ff11fcc..f326a182 100644 --- a/tests/misc/programs/misc/Cargo.toml +++ b/tests/misc/programs/misc/Cargo.toml @@ -18,5 +18,4 @@ default = [] [dependencies] anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } anchor-spl = { path = "../../../../spl" } -misc2 = { path = "../misc2", features = ["cpi"] } spl-associated-token-account = "1.1.1" diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs index d23a6543..6be21da2 100644 --- a/tests/misc/programs/misc/src/context.rs +++ b/tests/misc/programs/misc/src/context.rs @@ -1,10 +1,8 @@ use crate::account::*; -use anchor_lang::accounts::cpi_state::CpiState; use anchor_lang::accounts::loader::Loader; use anchor_lang::prelude::*; use anchor_spl::associated_token::AssociatedToken; use anchor_spl::token::{Mint, Token, TokenAccount}; -use misc2::misc2::MyState as Misc2State; #[derive(Accounts)] pub struct TestTokenSeedsInit<'info> { @@ -160,18 +158,6 @@ pub struct TestExecutable<'info> { pub program: AccountInfo<'info>, } -#[derive(Accounts)] -pub struct TestStateCpi<'info> { - #[account(signer)] - /// CHECK: - pub authority: AccountInfo<'info>, - #[account(mut, state = misc2_program)] - pub cpi_state: CpiState<'info, Misc2State>, - #[account(executable)] - /// CHECK: - pub misc2_program: AccountInfo<'info>, -} - #[derive(Accounts)] pub struct TestClose<'info> { #[account(mut, close = sol_dest)] diff --git a/tests/misc/programs/misc/src/lib.rs b/tests/misc/programs/misc/src/lib.rs index d6b646b2..64b99ccb 100644 --- a/tests/misc/programs/misc/src/lib.rs +++ b/tests/misc/programs/misc/src/lib.rs @@ -5,7 +5,6 @@ use account::MAX_SIZE; use anchor_lang::prelude::*; use context::*; use event::*; -use misc2::Auth; mod account; mod context; @@ -27,26 +26,6 @@ pub const NO_IDL: u16 = 55; pub mod misc { use super::*; - pub const SIZE: u64 = 99; - - #[state(SIZE)] - pub struct MyState { - pub v: Vec, - } - - impl MyState { - pub fn new(_ctx: Context) -> Result { - Ok(Self { v: vec![] }) - } - - pub fn remaining_accounts(&mut self, ctx: Context) -> Result<()> { - if ctx.remaining_accounts.len() != 1 { - return Err(ProgramError::Custom(1).into()); // Arbitrary error. - } - Ok(()) - } - } - pub fn initialize(ctx: Context, udata: u128, idata: i128) -> Result<()> { ctx.accounts.data.udata = udata; ctx.accounts.data.idata = idata; @@ -69,15 +48,6 @@ pub mod misc { Ok(()) } - pub fn test_state_cpi(ctx: Context, data: u64) -> Result<()> { - let cpi_program = ctx.accounts.misc2_program.clone(); - let cpi_accounts = Auth { - authority: ctx.accounts.authority.clone(), - }; - let ctx = ctx.accounts.cpi_state.context(cpi_program, cpi_accounts); - misc2::cpi::state::set_data(ctx, data) - } - pub fn test_u16(ctx: Context, data: u16) -> Result<()> { ctx.accounts.my_account.data = data; Ok(()) diff --git a/tests/misc/programs/misc2/Cargo.toml b/tests/misc/programs/misc2/Cargo.toml deleted file mode 100644 index 2a66a37a..00000000 --- a/tests/misc/programs/misc2/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "misc2" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.59" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "misc2" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/misc/programs/misc2/Xargo.toml b/tests/misc/programs/misc2/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/misc/programs/misc2/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/misc/programs/misc2/src/lib.rs b/tests/misc/programs/misc2/src/lib.rs deleted file mode 100644 index d651a2c6..00000000 --- a/tests/misc/programs/misc2/src/lib.rs +++ /dev/null @@ -1,38 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"); - -#[program] -pub mod misc2 { - use super::*; - - #[state] - pub struct MyState { - pub data: u64, - pub auth: Pubkey, - } - - impl MyState { - pub fn new(ctx: Context) -> Result { - Ok(Self { - data: 0, - auth: *ctx.accounts.authority.key, - }) - } - - pub fn set_data(&mut self, ctx: Context, data: u64) -> Result<()> { - if self.auth != *ctx.accounts.authority.key { - return Err(ProgramError::Custom(1234).into()); // Arbitrary error code. - } - self.data = data; - Ok(()) - } - } -} - -#[derive(Accounts)] -pub struct Auth<'info> { - #[account(signer)] - /// CHECK: - pub authority: AccountInfo<'info>, -} diff --git a/tests/misc/tests/misc/misc.ts b/tests/misc/tests/misc/misc.ts index d897650d..336b2fa3 100644 --- a/tests/misc/tests/misc/misc.ts +++ b/tests/misc/tests/misc/misc.ts @@ -1,18 +1,9 @@ import * as anchor from "@coral-xyz/anchor"; -import { - Program, - BN, - IdlAccounts, - AnchorError, - Wallet, - IdlTypes, - IdlEvents, -} from "@coral-xyz/anchor"; +import { Program, BN, AnchorError, Wallet, IdlEvents } from "@coral-xyz/anchor"; import { PublicKey, Keypair, SystemProgram, - SYSVAR_RENT_PUBKEY, Message, VersionedTransaction, } from "@solana/web3.js"; @@ -23,7 +14,6 @@ import { } from "@solana/spl-token"; import { Misc } from "../../target/types/misc"; import { MiscOptional } from "../../target/types/misc_optional"; -import { Misc2 } from "../../target/types/misc2"; const utf8 = anchor.utils.bytes.utf8; const { assert, expect } = require("chai"); @@ -38,31 +28,6 @@ const miscTest = ( const provider = anchor.AnchorProvider.env(); const wallet = provider.wallet as Wallet; anchor.setProvider(provider); - const misc2Program = anchor.workspace.Misc2 as Program; - - it("Can allocate extra space for a state constructor", async () => { - // @ts-expect-error - const tx = await program.state.rpc.new(); - const addr = await program.state.address(); - const state = await program.state.fetch(); - const accountInfo = await program.provider.connection.getAccountInfo( - addr - ); - assert.isTrue(state.v.equals(Buffer.from([]))); - assert.lengthOf(accountInfo.data, 99); - }); - - it("Can use remaining accounts for a state instruction", async () => { - await program.state.rpc.remainingAccounts({ - remainingAccounts: [ - { - pubkey: misc2Program.programId, - isWritable: false, - isSigner: false, - }, - ], - }); - }); const data = anchor.web3.Keypair.generate(); @@ -148,42 +113,6 @@ const miscTest = ( ); }); - it("Can CPI to state instructions", async () => { - const oldData = new anchor.BN(0); - try { - await misc2Program.state.fetch(); - // if state account already exists, reset data to oldData. - await program.rpc.testStateCpi(oldData, { - accounts: { - authority: provider.wallet.publicKey, - cpiState: await misc2Program.state.address(), - misc2Program: misc2Program.programId, - }, - }); - } catch (e) { - // initialize if it doesn't exist - await misc2Program.state.rpc.new({ - accounts: { - authority: provider.wallet.publicKey, - }, - }); - } - let stateAccount = await misc2Program.state.fetch(); - assert.isTrue(stateAccount.data.eq(oldData)); - assert.isTrue(stateAccount.auth.equals(provider.wallet.publicKey)); - const newData = new anchor.BN(2134); - await program.rpc.testStateCpi(newData, { - accounts: { - authority: provider.wallet.publicKey, - cpiState: await misc2Program.state.address(), - misc2Program: misc2Program.programId, - }, - }); - stateAccount = await misc2Program.state.fetch(); - assert.isTrue(stateAccount.data.eq(newData)); - assert.isTrue(stateAccount.auth.equals(provider.wallet.publicKey)); - }); - it("Can retrieve events when simulating a transaction", async () => { const resp = await program.methods.testSimulate(44).simulate(); const expectedRaw = [ diff --git a/tests/zero-copy/programs/zero-cpi/src/lib.rs b/tests/zero-copy/programs/zero-cpi/src/lib.rs index c1d41c2e..1ce2fdaa 100644 --- a/tests/zero-copy/programs/zero-cpi/src/lib.rs +++ b/tests/zero-copy/programs/zero-cpi/src/lib.rs @@ -16,7 +16,7 @@ pub mod zero_cpi { foo: ctx.accounts.foo.to_account_info(), }; let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - zero_copy::cpi::update_bar(cpi_ctx, data); + zero_copy::cpi::update_bar(cpi_ctx, data)?; Ok(()) } } diff --git a/ts/packages/anchor/src/coder/borsh/index.ts b/ts/packages/anchor/src/coder/borsh/index.ts index 708ff2c0..26ed989c 100644 --- a/ts/packages/anchor/src/coder/borsh/index.ts +++ b/ts/packages/anchor/src/coder/borsh/index.ts @@ -2,14 +2,12 @@ import { Idl } from "../../idl.js"; import { BorshInstructionCoder } from "./instruction.js"; import { BorshAccountsCoder } from "./accounts.js"; import { BorshEventCoder } from "./event.js"; -import { BorshStateCoder } from "./state.js"; import { BorshTypesCoder } from "./types.js"; import { Coder } from "../index.js"; export { BorshInstructionCoder } from "./instruction.js"; export { BorshAccountsCoder, ACCOUNT_DISCRIMINATOR_SIZE } from "./accounts.js"; export { BorshEventCoder, eventDiscriminator } from "./event.js"; -export { BorshStateCoder, stateDiscriminator } from "./state.js"; /** * BorshCoder is the default Coder for Anchor programs implementing the @@ -28,11 +26,6 @@ export class BorshCoder */ readonly accounts: BorshAccountsCoder; - /** - * Coder for state structs. - */ - readonly state: BorshStateCoder; - /** * Coder for events. */ @@ -47,9 +40,6 @@ export class BorshCoder this.instruction = new BorshInstructionCoder(idl); this.accounts = new BorshAccountsCoder(idl); this.events = new BorshEventCoder(idl); - if (idl.state) { - this.state = new BorshStateCoder(idl); - } this.types = new BorshTypesCoder(idl); } } diff --git a/ts/packages/anchor/src/coder/borsh/instruction.ts b/ts/packages/anchor/src/coder/borsh/instruction.ts index 8c5f9039..d3525758 100644 --- a/ts/packages/anchor/src/coder/borsh/instruction.ts +++ b/ts/packages/anchor/src/coder/borsh/instruction.ts @@ -9,7 +9,6 @@ import { AccountMeta, PublicKey } from "@solana/web3.js"; import { Idl, IdlField, - IdlStateMethod, IdlType, IdlTypeDef, IdlAccount, @@ -23,10 +22,6 @@ import { import { IdlCoder } from "./idl.js"; import { InstructionCoder } from "../index.js"; -/** - * Namespace for state method function signatures. - */ -export const SIGHASH_STATE_NAMESPACE = "state"; /** * Namespace for global instruction function signatures (i.e. functions * that aren't namespaced by the state or any of its trait implementations). @@ -55,16 +50,6 @@ export class BorshInstructionCoder implements InstructionCoder { }); }); - if (idl.state) { - idl.state.methods.map((ix) => { - const sh = sighash(SIGHASH_STATE_NAMESPACE, ix.name); - sighashLayouts.set(bs58.encode(sh), { - layout: this.ixLayout.get(ix.name) as Layout, - name: ix.name, - }); - }); - } - this.sighashLayouts = sighashLayouts; } @@ -75,13 +60,6 @@ export class BorshInstructionCoder implements InstructionCoder { return this._encode(SIGHASH_GLOBAL_NAMESPACE, ixName, ix); } - /** - * Encodes a program state instruction. - */ - public encodeState(ixName: string, ix: any): Buffer { - return this._encode(SIGHASH_STATE_NAMESPACE, ixName, ix); - } - private _encode(nameSpace: string, ixName: string, ix: any): Buffer { const buffer = Buffer.alloc(1000); // TODO: use a tighter buffer. const methodName = camelCase(ixName); @@ -95,31 +73,17 @@ export class BorshInstructionCoder implements InstructionCoder { } private static parseIxLayout(idl: Idl): Map { - const stateMethods = idl.state ? idl.state.methods : []; - - const ixLayouts = stateMethods - .map((m: IdlStateMethod): [string, Layout] => { - let fieldLayouts = m.args.map((arg: IdlField) => { - return IdlCoder.fieldLayout( - arg, - Array.from([...(idl.accounts ?? []), ...(idl.types ?? [])]) - ); - }); - const name = camelCase(m.name); - return [name, borsh.struct(fieldLayouts, name)]; - }) - .concat( - idl.instructions.map((ix) => { - let fieldLayouts = ix.args.map((arg: IdlField) => - IdlCoder.fieldLayout( - arg, - Array.from([...(idl.accounts ?? []), ...(idl.types ?? [])]) - ) - ); - const name = camelCase(ix.name); - return [name, borsh.struct(fieldLayouts, name)]; - }) + const ixLayouts = idl.instructions.map((ix): [string, Layout] => { + let fieldLayouts = ix.args.map((arg: IdlField) => + IdlCoder.fieldLayout( + arg, + Array.from([...(idl.accounts ?? []), ...(idl.types ?? [])]) + ) ); + const name = camelCase(ix.name); + return [name, borsh.struct(fieldLayouts, name)]; + }); + return new Map(ixLayouts); } diff --git a/ts/packages/anchor/src/coder/borsh/state.ts b/ts/packages/anchor/src/coder/borsh/state.ts deleted file mode 100644 index b33f7b20..00000000 --- a/ts/packages/anchor/src/coder/borsh/state.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Buffer } from "buffer"; -import { Layout } from "buffer-layout"; -import { sha256 } from "js-sha256"; -import { Idl } from "../../idl.js"; -import { IdlCoder } from "./idl.js"; -import * as features from "../../utils/features.js"; - -export class BorshStateCoder { - private layout: Layout; - - public constructor(idl: Idl) { - if (idl.state === undefined) { - throw new Error("Idl state not defined."); - } - this.layout = IdlCoder.typeDefLayout(idl.state.struct, idl.types); - } - - public async encode(name: string, account: T): Promise { - const buffer = Buffer.alloc(1000); // TODO: use a tighter buffer. - const len = this.layout.encode(account, buffer); - - const disc = await stateDiscriminator(name); - const accData = buffer.slice(0, len); - - return Buffer.concat([disc, accData]); - } - - public decode(ix: Buffer): T { - // Chop off discriminator. - const data = ix.slice(8); - return this.layout.decode(data); - } -} - -// Calculates unique 8 byte discriminator prepended to all anchor state accounts. -export async function stateDiscriminator(name: string): Promise { - let ns = features.isSet("anchor-deprecated-state") ? "account" : "state"; - return Buffer.from(sha256.digest(`${ns}:${name}`)).slice(0, 8); -} diff --git a/ts/packages/anchor/src/coder/index.ts b/ts/packages/anchor/src/coder/index.ts index 8b4ba5c0..7d4ae356 100644 --- a/ts/packages/anchor/src/coder/index.ts +++ b/ts/packages/anchor/src/coder/index.ts @@ -18,11 +18,6 @@ export interface Coder { */ readonly accounts: AccountsCoder; - /** - * Coder for state structs. - */ - readonly state: StateCoder; - /** * Coder for events. */ @@ -49,7 +44,6 @@ export interface AccountsCoder { export interface InstructionCoder { encode(ixName: string, ix: any): Buffer; - encodeState(ixName: string, ix: any): Buffer; } export interface EventCoder { diff --git a/ts/packages/anchor/src/coder/system/index.ts b/ts/packages/anchor/src/coder/system/index.ts index f4efa131..0bab2738 100644 --- a/ts/packages/anchor/src/coder/system/index.ts +++ b/ts/packages/anchor/src/coder/system/index.ts @@ -1,7 +1,6 @@ import { Idl } from "../../idl.js"; import { Coder } from "../index.js"; import { SystemInstructionCoder } from "./instruction.js"; -import { SystemStateCoder } from "./state.js"; import { SystemAccountsCoder } from "./accounts.js"; import { SystemEventsCoder } from "./events.js"; import { SystemTypesCoder } from "./types.js"; @@ -12,7 +11,6 @@ import { SystemTypesCoder } from "./types.js"; export class SystemCoder implements Coder { readonly instruction: SystemInstructionCoder; readonly accounts: SystemAccountsCoder; - readonly state: SystemStateCoder; readonly events: SystemEventsCoder; readonly types: SystemTypesCoder; @@ -20,7 +18,6 @@ export class SystemCoder implements Coder { this.instruction = new SystemInstructionCoder(idl); this.accounts = new SystemAccountsCoder(idl); this.events = new SystemEventsCoder(idl); - this.state = new SystemStateCoder(idl); this.types = new SystemTypesCoder(idl); } } diff --git a/ts/packages/anchor/src/coder/system/state.ts b/ts/packages/anchor/src/coder/system/state.ts deleted file mode 100644 index 2d7f1a2e..00000000 --- a/ts/packages/anchor/src/coder/system/state.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { StateCoder } from "../index.js"; -import { Idl } from "../../idl"; - -export class SystemStateCoder implements StateCoder { - // eslint-disable-next-line @typescript-eslint/no-empty-function - constructor(_idl: Idl) {} - - encode(_name: string, _account: T): Promise { - throw new Error("System does not have state"); - } - decode(_ix: Buffer): T { - throw new Error("System does not have state"); - } -} diff --git a/ts/packages/anchor/src/error.ts b/ts/packages/anchor/src/error.ts index 0844efb1..f7ebc86c 100644 --- a/ts/packages/anchor/src/error.ts +++ b/ts/packages/anchor/src/error.ts @@ -368,9 +368,6 @@ export const LangErrorCode = { AccountReallocExceedsLimit: 3016, AccountDuplicateReallocs: 3017, - // State. - StateInvalidAddress: 4000, - // Miscellaneous DeclaredProgramIdMismatch: 4100, @@ -419,7 +416,10 @@ export const LangErrorMessage = new Map([ ], [LangErrorCode.ConstraintSeeds, "A seeds constraint was violated"], [LangErrorCode.ConstraintExecutable, "An executable constraint was violated"], - [LangErrorCode.ConstraintState, "A state constraint was violated"], + [ + LangErrorCode.ConstraintState, + "Deprecated Error, feel free to replace with something else", + ], [LangErrorCode.ConstraintAssociated, "An associated constraint was violated"], [ LangErrorCode.ConstraintAssociatedInit, @@ -519,12 +519,6 @@ export const LangErrorMessage = new Map([ "The account was duplicated for more than one reallocation", ], - // State. - [ - LangErrorCode.StateInvalidAddress, - "The given state account does not have the correct address", - ], - // Miscellaneous [ LangErrorCode.DeclaredProgramIdMismatch, diff --git a/ts/packages/anchor/src/idl.ts b/ts/packages/anchor/src/idl.ts index 488fdad9..71e0cd5f 100644 --- a/ts/packages/anchor/src/idl.ts +++ b/ts/packages/anchor/src/idl.ts @@ -7,7 +7,6 @@ export type Idl = { name: string; docs?: string[]; instructions: IdlInstruction[]; - state?: IdlState; accounts?: IdlAccountDef[]; types?: IdlTypeDef[]; events?: IdlEvent[]; @@ -43,11 +42,6 @@ export type IdlInstruction = { returns?: IdlType; }; -export type IdlState = { - struct: IdlTypeDef; - methods: IdlStateMethod[]; -}; - export type IdlStateMethod = IdlInstruction; export type IdlAccountItem = IdlAccount | IdlAccounts; diff --git a/ts/packages/anchor/src/program/index.ts b/ts/packages/anchor/src/program/index.ts index 5a1b054a..58d962f2 100644 --- a/ts/packages/anchor/src/program/index.ts +++ b/ts/packages/anchor/src/program/index.ts @@ -8,7 +8,6 @@ import NamespaceFactory, { InstructionNamespace, TransactionNamespace, AccountNamespace, - StateClient, SimulateNamespace, MethodsNamespace, ViewNamespace, @@ -207,13 +206,6 @@ export class Program { */ readonly simulate: SimulateNamespace; - /** - * A client for the program state. Similar to the base [[Program]] client, - * one can use this to send transactions and read accounts for the state - * abstraction. - */ - readonly state?: StateClient; - /** * The namespace provides a builder API for all APIs on the program. * This is an alternative to using namespace the other namespaces.. @@ -291,29 +283,20 @@ export class Program { this._events = new EventManager(this._programId, provider, this._coder); // Dynamic namespaces. - const [ - rpc, - instruction, - transaction, - account, - simulate, - methods, - state, - views, - ] = NamespaceFactory.build( - idl, - this._coder, - programId, - provider, - getCustomResolver ?? (() => undefined) - ); + const [rpc, instruction, transaction, account, simulate, methods, views] = + NamespaceFactory.build( + idl, + this._coder, + programId, + provider, + getCustomResolver ?? (() => undefined) + ); this.rpc = rpc; this.instruction = instruction; this.transaction = transaction; this.account = account; this.simulate = simulate; this.methods = methods; - this.state = state; this.views = views; } diff --git a/ts/packages/anchor/src/program/namespace/index.ts b/ts/packages/anchor/src/program/namespace/index.ts index 8a10d50d..b917df22 100644 --- a/ts/packages/anchor/src/program/namespace/index.ts +++ b/ts/packages/anchor/src/program/namespace/index.ts @@ -3,7 +3,6 @@ import { PublicKey } from "@solana/web3.js"; import { Coder } from "../../coder/index.js"; import Provider from "../../provider.js"; import { Idl, IdlInstruction } from "../../idl.js"; -import StateFactory, { StateClient } from "./state.js"; import InstructionFactory, { InstructionNamespace } from "./instruction.js"; import TransactionFactory, { TransactionNamespace } from "./transaction.js"; import RpcFactory, { RpcNamespace } from "./rpc.js"; @@ -15,7 +14,6 @@ import ViewFactory, { ViewNamespace } from "./views"; import { CustomAccountResolver } from "../accounts-resolver.js"; // Re-exports. -export { StateClient } from "./state.js"; export { InstructionNamespace, InstructionFn } from "./instruction.js"; export { TransactionNamespace, TransactionFn } from "./transaction.js"; export { RpcNamespace, RpcFn } from "./rpc.js"; @@ -44,7 +42,6 @@ export default class NamespaceFactory { AccountNamespace, SimulateNamespace, MethodsNamespace, - StateClient | undefined, ViewNamespace | undefined ] { const rpc: RpcNamespace = {}; @@ -60,8 +57,6 @@ export default class NamespaceFactory { ? AccountFactory.build(idl, coder, programId, provider) : ({} as AccountNamespace); - const state = StateFactory.build(idl, coder, programId, provider); - idl.instructions.forEach((idlIx) => { const ixItem = InstructionFactory.build( idlIx, @@ -112,7 +107,6 @@ export default class NamespaceFactory { account, simulate as SimulateNamespace, methods as MethodsNamespace, - state, view as ViewNamespace, ]; } diff --git a/ts/packages/anchor/src/program/namespace/state.ts b/ts/packages/anchor/src/program/namespace/state.ts deleted file mode 100644 index 805cc516..00000000 --- a/ts/packages/anchor/src/program/namespace/state.ts +++ /dev/null @@ -1,286 +0,0 @@ -import EventEmitter from "eventemitter3"; -import camelCase from "camelcase"; -import { - PublicKey, - SystemProgram, - Commitment, - AccountMeta, -} from "@solana/web3.js"; -import Provider, { getProvider } from "../../provider.js"; -import { Idl, IdlInstruction, IdlStateMethod, IdlTypeDef } from "../../idl.js"; -import { BorshCoder, Coder, stateDiscriminator } from "../../coder/index.js"; -import { - RpcNamespace, - InstructionNamespace, - TransactionNamespace, -} from "./index.js"; -import { Subscription, validateAccounts, parseIdlErrors } from "../common.js"; -import { - findProgramAddressSync, - createWithSeedSync, -} from "../../utils/pubkey.js"; -import { Accounts } from "../context.js"; -import InstructionNamespaceFactory from "./instruction.js"; -import RpcNamespaceFactory from "./rpc.js"; -import TransactionNamespaceFactory from "./transaction.js"; -import { IdlTypes, TypeDef } from "./types.js"; - -export default class StateFactory { - public static build( - idl: IDL, - coder: Coder, - programId: PublicKey, - provider?: Provider - ): StateClient | undefined { - if (idl.state === undefined) { - return undefined; - } - return new StateClient(idl, programId, provider, coder); - } -} - -type NullableMethods = IDL["state"] extends undefined - ? IdlInstruction[] - : NonNullable["methods"]; - -/** - * A client for the program state. Similar to the base [[Program]] client, - * one can use this to send transactions and read accounts for the state - * abstraction. - */ -export class StateClient { - /** - * [[RpcNamespace]] for all state methods. - */ - readonly rpc: RpcNamespace[number]>; - - /** - * [[InstructionNamespace]] for all state methods. - */ - readonly instruction: InstructionNamespace[number]>; - - /** - * [[TransactionNamespace]] for all state methods. - */ - readonly transaction: TransactionNamespace[number]>; - - /** - * Returns the program ID owning the state. - */ - get programId(): PublicKey { - return this._programId; - } - private _programId: PublicKey; - - private _address: PublicKey; - private _coder: Coder; - private _idl: IDL; - private _sub: Subscription | null; - - constructor( - idl: IDL, - programId: PublicKey, - /** - * Returns the client's wallet and network provider. - */ - public readonly provider: Provider = getProvider(), - /** - * Returns the coder. - */ - public readonly coder: Coder = new BorshCoder(idl) - ) { - this._idl = idl; - this._programId = programId; - this._address = programStateAddress(programId); - this._sub = null; - - // Build namespaces. - const [instruction, transaction, rpc] = ((): [ - InstructionNamespace[number]>, - TransactionNamespace[number]>, - RpcNamespace[number]> - ] => { - let instruction: InstructionNamespace = {}; - let transaction: TransactionNamespace = {}; - let rpc: RpcNamespace = {}; - - idl.state?.methods.forEach( - [number]>(m: I) => { - // Build instruction method. - const ixItem = InstructionNamespaceFactory.build( - m, - (ixName, ix) => coder.instruction.encodeState(ixName, ix), - programId - ); - ixItem["accounts"] = (accounts) => { - const keys = stateInstructionKeys(programId, provider, m, accounts); - return keys.concat( - InstructionNamespaceFactory.accountsArray( - accounts, - m.accounts, - programId, - m.name - ) - ); - }; - // Build transaction method. - const txItem = TransactionNamespaceFactory.build(m, ixItem); - // Build RPC method. - const rpcItem = RpcNamespaceFactory.build( - m, - txItem, - parseIdlErrors(idl), - provider - ); - - // Attach them all to their respective namespaces. - const name = camelCase(m.name); - instruction[name] = ixItem; - transaction[name] = txItem; - rpc[name] = rpcItem; - } - ); - - return [ - instruction as InstructionNamespace[number]>, - transaction as TransactionNamespace[number]>, - rpc as RpcNamespace[number]>, - ]; - })(); - this.instruction = instruction; - this.transaction = transaction; - this.rpc = rpc; - } - - /** - * Returns the deserialized state account. - */ - async fetch(): Promise< - TypeDef< - IDL["state"] extends undefined - ? IdlTypeDef - : NonNullable["struct"], - IdlTypes - > - > { - const addr = this.address(); - const accountInfo = await this.provider.connection.getAccountInfo(addr); - if (accountInfo === null) { - throw new Error(`Account does not exist ${addr.toString()}`); - } - // Assert the account discriminator is correct. - const state = this._idl.state; - 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"); - } - return this.coder.state.decode(accountInfo.data); - } - - /** - * Returns the state address. - */ - address(): PublicKey { - return this._address; - } - - /** - * Returns an `EventEmitter` with a `"change"` event that's fired whenever - * the state account cahnges. - */ - subscribe(commitment?: Commitment): EventEmitter { - if (this._sub !== null) { - return this._sub.ee; - } - const ee = new EventEmitter(); - - const listener = this.provider.connection.onAccountChange( - this.address(), - (acc) => { - const account = this.coder.state.decode(acc.data); - ee.emit("change", account); - }, - commitment - ); - - this._sub = { - ee, - listener, - }; - - return ee; - } - - /** - * Unsubscribes to state changes. - */ - unsubscribe() { - if (this._sub !== null) { - this.provider.connection - .removeAccountChangeListener(this._sub.listener) - .then(async () => { - this._sub = null; - }) - .catch(console.error); - } - } -} - -// Calculates the deterministic address of the program's "state" account. -function programStateAddress(programId: PublicKey): PublicKey { - let [registrySigner] = findProgramAddressSync([], programId); - return createWithSeedSync(registrySigner, "unversioned", programId); -} - -// Returns the common keys that are prepended to all instructions targeting -// the "state" of a program. -function stateInstructionKeys( - programId: PublicKey, - provider: Provider, - m: M, - accounts: Accounts -): AccountMeta[] { - if (m.name === "new") { - // Ctor `new` method. - const [programSigner] = findProgramAddressSync([], programId); - // @ts-expect-error - if (provider.wallet === undefined) { - throw new Error( - "This function requires the Provider interface implementor to have a 'wallet' field." - ); - } - return [ - { - // @ts-expect-error - pubkey: provider.wallet.publicKey, - isWritable: false, - isSigner: true, - }, - { - pubkey: programStateAddress(programId), - isWritable: true, - isSigner: false, - }, - { pubkey: programSigner, isWritable: false, isSigner: false }, - { - pubkey: SystemProgram.programId, - isWritable: false, - isSigner: false, - }, - - { pubkey: programId, isWritable: false, isSigner: false }, - ]; - } else { - validateAccounts(m.accounts, accounts); - return [ - { - pubkey: programStateAddress(programId), - isWritable: true, - isSigner: false, - }, - ]; - } -}