From d2b5510537c7f8b8be3073fab8c5259a2002c62a Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sun, 31 Jul 2022 15:32:21 -0400 Subject: [PATCH 001/109] optional accounts initial implementation --- lang/src/accounts/mod.rs | 1 + lang/src/accounts/option.rs | 66 +++++++++++++++++++++++++++++ lang/syn/src/lib.rs | 12 +++++- lang/syn/src/parser/accounts/mod.rs | 63 ++++++++++++++++++++++----- 4 files changed, 131 insertions(+), 11 deletions(-) create mode 100644 lang/src/accounts/option.rs diff --git a/lang/src/accounts/mod.rs b/lang/src/accounts/mod.rs index 8839ef35a2..21146995a1 100644 --- a/lang/src/accounts/mod.rs +++ b/lang/src/accounts/mod.rs @@ -4,6 +4,7 @@ pub mod account; pub mod account_info; pub mod account_loader; pub mod boxed; +pub mod option; #[doc(hidden)] #[allow(deprecated)] pub mod cpi_account; diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs new file mode 100644 index 0000000000..9f5d0259e9 --- /dev/null +++ b/lang/src/accounts/option.rs @@ -0,0 +1,66 @@ +//! Option type for optional accounts. +//! +//! # Example +//! ```ignore +//! #[derive(Accounts)] +//! pub struct Example { +//! pub my_acc: Option> +//! } +//! ``` + +use std::collections::{BTreeMap, BTreeSet}; + +use solana_program::account_info::AccountInfo; +use solana_program::instruction::AccountMeta; +use solana_program::pubkey::Pubkey; + +use anchor_lang::error::ErrorCode; + +use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas}; + +impl<'info, T: Accounts<'info>> Accounts<'info> for Option { + 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]; + if account.key == program_id { + *accounts = &accounts[1..]; + Ok(None) + } else { + T::try_accounts(program_id, accounts, ix_data, bumps, reallocs).map(Some) + } + } +} + +impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Option { + fn exit(&self, program_id: &Pubkey) -> Result<()> { + self.as_ref().map_or(Ok(()), |t| T::exit(t, program_id)) + } +} + +impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option { + fn to_account_infos(&self) -> Vec> { + self.as_ref().map_or(vec![], |t| T::to_account_infos(t)) + } +} + +impl ToAccountMetas for Option { + fn to_account_metas(&self, is_signer: Option) -> Vec { + self.as_ref() + .map_or(vec![], |t| T::to_account_metas(t, is_signer)) + } +} + +impl<'info, T: AccountsClose<'info>> AccountsClose<'info> for Option { + fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> { + self.as_ref() + .map_or(Ok(()), |t| T::close(t, sol_destination)) + } +} diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 5aeac91b4a..1e148aac2b 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -215,6 +215,7 @@ pub struct Field { pub ident: Ident, pub constraints: ConstraintGroup, pub ty: Ty, + pub optional: bool, /// IDL Doc comment pub docs: Option>, } @@ -231,7 +232,7 @@ impl Field { pub fn ty_decl(&self) -> proc_macro2::TokenStream { let account_ty = self.account_ty(); let container_ty = self.container_ty(); - match &self.ty { + let inner_ty = match &self.ty { Ty::AccountInfo => quote! { AccountInfo }, @@ -278,6 +279,15 @@ impl Field { _ => quote! { #container_ty<#account_ty> }, + }; + if self.optional { + quote! { + Option<#inner_ty> + } + } else { + quote! { + #inner_ty + } } } diff --git a/lang/syn/src/parser/accounts/mod.rs b/lang/syn/src/parser/accounts/mod.rs index 0f158d2a57..96b62f64cb 100644 --- a/lang/syn/src/parser/accounts/mod.rs +++ b/lang/syn/src/parser/accounts/mod.rs @@ -5,6 +5,7 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::Comma; use syn::Expr; +use syn::Path; pub mod constraints; @@ -204,11 +205,12 @@ pub fn parse_account_field(f: &syn::Field) -> ParseResult { let docs = docs::parse(&f.attrs); let account_field = match is_field_primitive(f)? { true => { - let ty = parse_ty(f)?; + let (ty, optional) = parse_ty(f)?; let account_constraints = constraints::parse(f, Some(&ty))?; AccountField::Field(Field { ident, ty, + optional, constraints: account_constraints, docs, }) @@ -218,7 +220,7 @@ pub fn parse_account_field(f: &syn::Field) -> ParseResult { AccountField::CompositeField(CompositeField { ident, constraints: account_constraints, - symbol: ident_string(f)?, + symbol: ident_string(f)?.0, raw_field: f.clone(), docs, }) @@ -229,7 +231,7 @@ pub fn parse_account_field(f: &syn::Field) -> ParseResult { fn is_field_primitive(f: &syn::Field) -> ParseResult { let r = matches!( - ident_string(f)?.as_str(), + ident_string(f)?.0.as_str(), "ProgramState" | "ProgramAccount" | "CpiAccount" @@ -248,12 +250,13 @@ fn is_field_primitive(f: &syn::Field) -> ParseResult { Ok(r) } -fn parse_ty(f: &syn::Field) -> ParseResult { +fn parse_ty(f: &syn::Field) -> ParseResult<(Ty, bool)> { let path = match &f.ty { syn::Type::Path(ty_path) => ty_path.path.clone(), _ => return Err(ParseError::new(f.ty.span(), "invalid account type given")), }; - let ty = match ident_string(f)?.as_str() { + let (ident, optional) = 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)?), @@ -271,19 +274,52 @@ fn parse_ty(f: &syn::Field) -> ParseResult { _ => return Err(ParseError::new(f.ty.span(), "invalid account type given")), }; - Ok(ty) + Ok((ty, optional)) } -fn ident_string(f: &syn::Field) -> ParseResult { - let path = match &f.ty { +fn option_to_inner_path(path: &Path) -> ParseResult { + let segment_0 = path.segments[0].clone(); + match segment_0.arguments { + syn::PathArguments::AngleBracketed(args) => { + if args.args.len() != 1 { + return Err(ParseError::new( + args.args.span(), + "can only have one argument in option", + )); + } + match &args.args[0] { + syn::GenericArgument::Type(syn::Type::Path(ty_path)) => Ok(ty_path.path.clone()), + _ => Err(ParseError::new( + args.args[1].span(), + "first bracket argument must be a lifetime", + )), + } + } + _ => Err(ParseError::new( + segment_0.arguments.span(), + "expected angle brackets with a lifetime and type", + )), + } +} + +fn ident_string(f: &syn::Field) -> ParseResult<(String, bool)> { + let mut path = match &f.ty { syn::Type::Path(ty_path) => ty_path.path.clone(), _ => return Err(ParseError::new(f.ty.span(), "invalid type")), }; + let mut optional = false; + if parser::tts_to_string(&path) + .replace(' ', "") + .starts_with("Option<") + { + path = option_to_inner_path(&path)?; + optional = true; + } if parser::tts_to_string(&path) .replace(' ', "") .starts_with("Box ParseResult { } let segments = &path.segments[0]; - Ok(segments.ident.to_string()) + Ok((segments.ident.to_string(), optional)) } fn parse_program_state(path: &syn::Path) -> ParseResult { @@ -390,6 +426,13 @@ fn parse_account(mut path: &syn::Path) -> ParseResult { } } } + if parser::tts_to_string(path) + .replace(' ', "") + .starts_with("Option<") + { + let inner_path = option_to_inner_path(path)?; + return parse_account(&inner_path); + } let segments = &path.segments[0]; match &segments.arguments { From 724cf9ecf39878d3e4ac71cc548b012d31f275ef Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 1 Aug 2022 14:06:20 -0500 Subject: [PATCH 002/109] cargo fmt --- lang/src/accounts/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/src/accounts/mod.rs b/lang/src/accounts/mod.rs index 21146995a1..34c6163086 100644 --- a/lang/src/accounts/mod.rs +++ b/lang/src/accounts/mod.rs @@ -4,7 +4,6 @@ pub mod account; pub mod account_info; pub mod account_loader; pub mod boxed; -pub mod option; #[doc(hidden)] #[allow(deprecated)] pub mod cpi_account; @@ -14,6 +13,7 @@ pub mod cpi_state; #[doc(hidden)] #[allow(deprecated)] pub mod loader; +pub mod option; pub mod program; #[doc(hidden)] #[allow(deprecated)] From f46cb223b4272dc715419ea3e090dee6932efd6e Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 1 Aug 2022 17:41:05 -0500 Subject: [PATCH 003/109] panic if Account related traits are run on none --- lang/src/accounts/option.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index 9f5d0259e9..b6d3273a2d 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -41,26 +41,32 @@ impl<'info, T: Accounts<'info>> Accounts<'info> for Option { impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Option { fn exit(&self, program_id: &Pubkey) -> Result<()> { - self.as_ref().map_or(Ok(()), |t| T::exit(t, program_id)) + self.as_ref() + .expect("Cannot run `exit` on None") + .exit(program_id) } } impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option { fn to_account_infos(&self) -> Vec> { - self.as_ref().map_or(vec![], |t| T::to_account_infos(t)) + self.as_ref() + .expect("Cannot run `to_account_infos` on None") + .to_account_infos() } } impl ToAccountMetas for Option { fn to_account_metas(&self, is_signer: Option) -> Vec { self.as_ref() - .map_or(vec![], |t| T::to_account_metas(t, is_signer)) + .expect("Cannot run `to_account_metas` on None") + .to_account_metas(is_signer) } } impl<'info, T: AccountsClose<'info>> AccountsClose<'info> for Option { fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> { self.as_ref() - .map_or(Ok(()), |t| T::close(t, sol_destination)) + .expect("Cannot run `close` on None") + .close(sol_destination) } } From 7c6c2fbb59ca7e51f8a0b1c2e219eefa34dc14d8 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 1 Aug 2022 20:36:46 -0500 Subject: [PATCH 004/109] Allow empty accounts to deserialize to None for optional accounts --- lang/src/accounts/option.rs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index b6d3273a2d..0659b55c6a 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -14,8 +14,6 @@ use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; -use anchor_lang::error::ErrorCode; - use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas}; impl<'info, T: Accounts<'info>> Accounts<'info> for Option { @@ -27,7 +25,7 @@ impl<'info, T: Accounts<'info>> Accounts<'info> for Option { reallocs: &mut BTreeSet, ) -> Result { if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); + return Ok(None); } let account = &accounts[0]; if account.key == program_id { @@ -39,14 +37,6 @@ impl<'info, T: Accounts<'info>> Accounts<'info> for Option { } } -impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Option { - fn exit(&self, program_id: &Pubkey) -> Result<()> { - self.as_ref() - .expect("Cannot run `exit` on None") - .exit(program_id) - } -} - impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option { fn to_account_infos(&self) -> Vec> { self.as_ref() @@ -66,7 +56,12 @@ impl ToAccountMetas for Option { impl<'info, T: AccountsClose<'info>> AccountsClose<'info> for Option { fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> { self.as_ref() - .expect("Cannot run `close` on None") - .close(sol_destination) + .map_or(Ok(()), |t| T::close(t, sol_destination)) + } +} + +impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Option { + fn exit(&self, program_id: &Pubkey) -> Result<()> { + self.as_ref().map_or(Ok(()), |t| T::exit(t, program_id)) } } From 34e34b0522b9b3ed7bbd3e3406426ea6bfb9f714 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 1 Aug 2022 20:40:06 -0500 Subject: [PATCH 005/109] implement constraints for optional accounts --- lang/syn/src/codegen/accounts/constraints.rs | 30 +++++++++++++++++-- lang/syn/src/codegen/accounts/try_accounts.rs | 24 +++++++++++---- lang/syn/src/lib.rs | 6 ++-- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index d8286eb79a..282bbbc95d 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -116,7 +116,7 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { } fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream { - match c { + let stream = match c { Constraint::Init(c) => generate_constraint_init(f, c), Constraint::Zeroed(c) => generate_constraint_zeroed(f, c), Constraint::Mut(c) => generate_constraint_mut(f, c), @@ -135,6 +135,30 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream { Constraint::TokenAccount(c) => generate_constraint_token_account(f, c), Constraint::Mint(c) => generate_constraint_mint(f, c), Constraint::Realloc(c) => generate_constraint_realloc(f, c), + }; + if f.optional { + let ident = &f.ident; + match c { + Constraint::Init(_) | Constraint::Zeroed(_) => { + quote! { + let #ident = if let Some(#ident) = #ident { + #stream + Some(#ident) + } else { + None + }; + } + } + _ => { + quote! { + if let Some(#ident) = &#ident { + #stream + } + } + } + } + } else { + stream } } @@ -173,7 +197,7 @@ pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macr pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream { let field = &f.ident; let name_str = field.to_string(); - let ty_decl = f.ty_decl(); + let ty_decl = f.ty_decl(true); let from_account_info = f.from_account_info(None, false); quote! { let #field: #ty_decl = { @@ -381,7 +405,7 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream { let field = &f.ident; let name_str = f.ident.to_string(); - let ty_decl = f.ty_decl(); + let ty_decl = f.ty_decl(true); let if_needed = if c.if_needed { quote! {true} } else { diff --git a/lang/syn/src/codegen/accounts/try_accounts.rs b/lang/syn/src/codegen/accounts/try_accounts.rs index 0cacf9c863..ccd4869ffc 100644 --- a/lang/syn/src/codegen/accounts/try_accounts.rs +++ b/lang/syn/src/codegen/accounts/try_accounts.rs @@ -32,14 +32,26 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { // `init` and `zero` acccounts are special cased as they are // deserialized by constraints. Here, we just take out the // AccountInfo for later use at constraint validation time. - if is_init(af) || f.constraints.zeroed.is_some() { + if is_init(af) || f.constraints.zeroed.is_some() { let name = &f.ident; - quote!{ - if accounts.is_empty() { - return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); + if f.optional { + quote! { + let #name = if accounts.is_empty() || accounts[0].key == program_id { + None + } else { + let account = &accounts[0]; + *accounts = &accounts[1..]; + Some(account) + }; + } + } else { + quote!{ + if accounts.is_empty() { + return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); + } + let #name = &accounts[0]; + *accounts = &accounts[1..]; } - let #name = &accounts[0]; - *accounts = &accounts[1..]; } } else { let name = f.ident.to_string(); diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 1e148aac2b..0ef33bc41f 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -223,13 +223,13 @@ pub struct Field { impl Field { pub fn typed_ident(&self) -> proc_macro2::TokenStream { let name = &self.ident; - let ty_decl = self.ty_decl(); + let ty_decl = self.ty_decl(false); quote! { #name: #ty_decl } } - pub fn ty_decl(&self) -> proc_macro2::TokenStream { + pub fn ty_decl(&self, ignore_option: bool) -> proc_macro2::TokenStream { let account_ty = self.account_ty(); let container_ty = self.container_ty(); let inner_ty = match &self.ty { @@ -280,7 +280,7 @@ impl Field { #container_ty<#account_ty> }, }; - if self.optional { + if self.optional && !ignore_option { quote! { Option<#inner_ty> } From e2210e012d32eb362f11c41aa7f8733cb0070fac Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 1 Aug 2022 23:53:06 -0500 Subject: [PATCH 006/109] optional accounts to idl gen --- lang/syn/src/idl/file.rs | 1 + lang/syn/src/idl/mod.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/lang/syn/src/idl/file.rs b/lang/syn/src/idl/file.rs index 23e813aa52..76aefe3fd2 100644 --- a/lang/syn/src/idl/file.rs +++ b/lang/syn/src/idl/file.rs @@ -643,6 +643,7 @@ fn idl_accounts( Ty::Signer => true, _ => acc.constraints.is_signer(), }, + optional: if acc.optional { Some(true) } else { None }, docs: if !no_docs { acc.docs.clone() } else { None }, pda: pda::parse(ctx, accounts, acc, seeds_feature), }), diff --git a/lang/syn/src/idl/mod.rs b/lang/syn/src/idl/mod.rs index 3d3dd12b38..7de15ed27d 100644 --- a/lang/syn/src/idl/mod.rs +++ b/lang/syn/src/idl/mod.rs @@ -74,6 +74,8 @@ pub struct IdlAccount { pub is_mut: bool, pub is_signer: bool, #[serde(skip_serializing_if = "Option::is_none")] + pub optional: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub docs: Option>, #[serde(skip_serializing_if = "Option::is_none", default)] pub pda: Option, From 25e6b5ec0540a710e840de589c4e1ca4b4f5ea1f Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 1 Aug 2022 23:55:58 -0500 Subject: [PATCH 007/109] accountstruct helper method --- lang/syn/src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 0ef33bc41f..d4a9fe55c7 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -179,6 +179,17 @@ impl AccountsStruct { .map(|field| field.ident().to_string()) .collect() } + + pub fn has_optional(&self) -> bool { + for field in &self.fields { + if let AccountField::Field(field) = field { + if field.optional { + return true; + } + } + } + false + } } #[allow(clippy::large_enum_variant)] From eca2c768067858ca359be87e719d747809b28d9c Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 1 Aug 2022 23:57:42 -0500 Subject: [PATCH 008/109] implemented to_account_metas and infos --- .../src/codegen/accounts/__client_accounts.rs | 27 ++++++++++--- .../codegen/accounts/__cpi_client_accounts.rs | 40 +++++++++++++++---- .../src/codegen/accounts/to_account_infos.rs | 15 ++++--- .../src/codegen/accounts/to_account_metas.rs | 20 +++++++--- 4 files changed, 80 insertions(+), 22 deletions(-) diff --git a/lang/syn/src/codegen/accounts/__client_accounts.rs b/lang/syn/src/codegen/accounts/__client_accounts.rs index 90a82069fc..8f7a40e3b3 100644 --- a/lang/syn/src/codegen/accounts/__client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__client_accounts.rs @@ -61,9 +61,16 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { } else { quote!() }; - quote! { - #docs - pub #name: anchor_lang::solana_program::pubkey::Pubkey + if f.optional { + quote! { + #docs + pub #name: Option + } + } else { + quote! { + #docs + pub #name: anchor_lang::solana_program::pubkey::Pubkey + } } } }) @@ -93,8 +100,18 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new }, }; let name = &f.ident; - quote! { - account_metas.push(#meta(self.#name, #is_signer)); + if f.optional { + quote! { + if let Some(#name) = &self.#name { + account_metas.push(#meta(*#name, #is_signer)); + } else { + account_metas.push(#meta(ID, #is_signer)); + } + } + } else { + quote! { + account_metas.push(#meta(self.#name, #is_signer)); + } } } }) diff --git a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs index b70a3a0842..66b9066ce6 100644 --- a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs @@ -62,9 +62,16 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { } else { quote!() }; - quote! { - #docs - pub #name: anchor_lang::solana_program::account_info::AccountInfo<'info> + if f.optional { + quote! { + #docs + pub #name: Option> + } + } else { + quote! { + #docs + pub #name: anchor_lang::solana_program::account_info::AccountInfo<'info> + } } } }) @@ -94,8 +101,18 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new }, }; let name = &f.ident; - quote! { - account_metas.push(#meta(anchor_lang::Key::key(&self.#name), #is_signer)); + if f.optional { + quote! { + if let Some(#name) = &self.#name { + account_metas.push(#meta(anchor_lang::Key::key(#name), #is_signer)); + } else { + account_metas.push(#meta(ID, #is_signer)); + } + } + } else { + quote! { + account_metas.push(#meta(anchor_lang::Key::key(&self.#name), #is_signer)); + } } } }) @@ -113,8 +130,17 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { } AccountField::Field(f) => { let name = &f.ident; - quote! { - account_infos.push(anchor_lang::ToAccountInfo::to_account_info(&self.#name)); + //TODO: figure out how to handle None gen + if f.optional { + quote! { + if let Some(account) = &self.#name { + account_infos.push(anchor_lang::ToAccountInfo::to_account_info(account)); + } + } + } else { + quote! { + account_infos.push(anchor_lang::ToAccountInfo::to_account_info(&self.#name)); + } } } }) diff --git a/lang/syn/src/codegen/accounts/to_account_infos.rs b/lang/syn/src/codegen/accounts/to_account_infos.rs index 6ba143115a..84fad58034 100644 --- a/lang/syn/src/codegen/accounts/to_account_infos.rs +++ b/lang/syn/src/codegen/accounts/to_account_infos.rs @@ -16,12 +16,17 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { .fields .iter() .map(|f: &AccountField| { - let name = match f { - AccountField::CompositeField(s) => &s.ident, - AccountField::Field(f) => &f.ident, + let (name, optional) = match f { + AccountField::CompositeField(s) => (&s.ident, false), + AccountField::Field(f) => (&f.ident, f.optional), }; - quote! { - account_infos.extend(self.#name.to_account_infos()); + //TODO: figure out how to handle None + if !optional { + quote! { + account_infos.extend(self.#name.to_account_infos()); + } + } else { + quote! {} } }) .collect(); diff --git a/lang/syn/src/codegen/accounts/to_account_metas.rs b/lang/syn/src/codegen/accounts/to_account_metas.rs index f36483835f..d33f16b543 100644 --- a/lang/syn/src/codegen/accounts/to_account_metas.rs +++ b/lang/syn/src/codegen/accounts/to_account_metas.rs @@ -9,18 +9,28 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { .fields .iter() .map(|f: &AccountField| { - let (name, is_signer) = match f { - AccountField::CompositeField(s) => (&s.ident, quote! {None}), + let (name, is_signer, optional) = match f { + AccountField::CompositeField(s) => (&s.ident, quote! {None}, false), AccountField::Field(f) => { let is_signer = match f.constraints.is_signer() { false => quote! {None}, true => quote! {Some(true)}, }; - (&f.ident, is_signer) + (&f.ident, is_signer, f.optional) } }; - quote! { - account_metas.extend(self.#name.to_account_metas(#is_signer)); + if optional { + quote! { + if let Some(#name) = &self.#name { + account_metas.extend(#name.to_account_metas(#is_signer)); + } else { + account_metas.push(AccountMeta::new_readonly(ID, false)); + } + } + } else { + quote! { + account_metas.extend(self.#name.to_account_metas(#is_signer)); + } } }) .collect(); From 6e71942d92ce9e8f4d8d29eff237cff69c3d1fba Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 2 Aug 2022 00:03:40 -0500 Subject: [PATCH 009/109] add test program --- tests/optional/Anchor.toml | 9 ++++++ tests/optional/Cargo.toml | 4 +++ tests/optional/package.json | 19 ++++++++++++ tests/optional/programs/optional/Cargo.toml | 16 +++++++++++ tests/optional/programs/optional/Xargo.toml | 2 ++ tests/optional/programs/optional/src/lib.rs | 32 +++++++++++++++++++++ tests/optional/tests/optional.js | 15 ++++++++++ 7 files changed, 97 insertions(+) create mode 100644 tests/optional/Anchor.toml create mode 100644 tests/optional/Cargo.toml create mode 100644 tests/optional/package.json create mode 100644 tests/optional/programs/optional/Cargo.toml create mode 100644 tests/optional/programs/optional/Xargo.toml create mode 100644 tests/optional/programs/optional/src/lib.rs create mode 100644 tests/optional/tests/optional.js diff --git a/tests/optional/Anchor.toml b/tests/optional/Anchor.toml new file mode 100644 index 0000000000..3b3c0c56a7 --- /dev/null +++ b/tests/optional/Anchor.toml @@ -0,0 +1,9 @@ +[provider] +cluster = "localnet" +wallet = "~/.config/solana/id.json" + +[programs.localnet] +optional = "EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU" + +[scripts] +test = "yarn run mocha -t 1000000 tests/" diff --git a/tests/optional/Cargo.toml b/tests/optional/Cargo.toml new file mode 100644 index 0000000000..a60de986d3 --- /dev/null +++ b/tests/optional/Cargo.toml @@ -0,0 +1,4 @@ +[workspace] +members = [ + "programs/*" +] diff --git a/tests/optional/package.json b/tests/optional/package.json new file mode 100644 index 0000000000..1eb47965ec --- /dev/null +++ b/tests/optional/package.json @@ -0,0 +1,19 @@ +{ + "name": "optional", + "version": "0.25.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/optional/programs/optional/Cargo.toml b/tests/optional/programs/optional/Cargo.toml new file mode 100644 index 0000000000..090174508c --- /dev/null +++ b/tests/optional/programs/optional/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "optional" +version = "0.1.0" +description = "Created with Anchor" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "optional" + +[features] +no-entrypoint = [] +cpi = ["no-entrypoint"] + +[dependencies] +anchor-lang = { path = "../../../../lang" } diff --git a/tests/optional/programs/optional/Xargo.toml b/tests/optional/programs/optional/Xargo.toml new file mode 100644 index 0000000000..1744f098ae --- /dev/null +++ b/tests/optional/programs/optional/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] \ No newline at end of file diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs new file mode 100644 index 0000000000..518f2d0108 --- /dev/null +++ b/tests/optional/programs/optional/src/lib.rs @@ -0,0 +1,32 @@ +//! This example demonstrates the ability to use optional accounts in +//! structs deriving `Accounts`. + +use anchor_lang::prelude::*; + +declare_id!("EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU"); + +#[program] +mod optional { + use super::*; + pub fn initialize(ctx: Context, value: u64) -> Result<()> { + // let dummy = &mut ctx.accounts.dummy + // todo!() + Ok(()) + } +} + +#[derive(Accounts)] +pub struct Initialize<'info> { + #[account(mut)] + pub payer: Signer<'info>, + #[account(init, payer=payer, space=16)] + pub dummy: Option>, + #[account()] + pub dummy2: Account<'info, Dummy>, + pub system_program: Program<'info, System>, +} + +#[account] +pub struct Dummy { + pub data: u64, +} diff --git a/tests/optional/tests/optional.js b/tests/optional/tests/optional.js new file mode 100644 index 0000000000..d42cec24b3 --- /dev/null +++ b/tests/optional/tests/optional.js @@ -0,0 +1,15 @@ +const { assert } = require("chai"); +const anchor = require("@project-serum/anchor"); + +describe("optional", () => { + const provider = anchor.AnchorProvider.local(); + + // Configure the client to use the local cluster. + anchor.setProvider(provider); + + it("Is initialized!", async () => { + const program = anchor.workspace.Optional; + + const dummy = anchor.web3.Keypair.generate(); + }); +}); From 5cc6a5f17fa280618f1739b28e729f4a6c34846b Mon Sep 17 00:00:00 2001 From: febo Date: Tue, 2 Aug 2022 11:05:01 +0100 Subject: [PATCH 010/109] Rename optional to is_optional --- lang/syn/src/codegen/accounts/__client_accounts.rs | 4 ++-- lang/syn/src/codegen/accounts/__cpi_client_accounts.rs | 6 +++--- lang/syn/src/codegen/accounts/constraints.rs | 2 +- lang/syn/src/codegen/accounts/to_account_infos.rs | 6 +++--- lang/syn/src/codegen/accounts/to_account_metas.rs | 6 +++--- lang/syn/src/codegen/accounts/try_accounts.rs | 2 +- lang/syn/src/idl/file.rs | 2 +- lang/syn/src/idl/mod.rs | 2 +- lang/syn/src/lib.rs | 8 ++++---- lang/syn/src/parser/accounts/mod.rs | 4 ++-- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lang/syn/src/codegen/accounts/__client_accounts.rs b/lang/syn/src/codegen/accounts/__client_accounts.rs index 8f7a40e3b3..206e1ab27e 100644 --- a/lang/syn/src/codegen/accounts/__client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__client_accounts.rs @@ -61,7 +61,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { } else { quote!() }; - if f.optional { + if f.is_optional { quote! { #docs pub #name: Option @@ -100,7 +100,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new }, }; let name = &f.ident; - if f.optional { + if f.is_optional { quote! { if let Some(#name) = &self.#name { account_metas.push(#meta(*#name, #is_signer)); diff --git a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs index 66b9066ce6..210dcd1507 100644 --- a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs @@ -62,7 +62,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { } else { quote!() }; - if f.optional { + if f.is_optional { quote! { #docs pub #name: Option> @@ -101,7 +101,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new }, }; let name = &f.ident; - if f.optional { + if f.is_optional { quote! { if let Some(#name) = &self.#name { account_metas.push(#meta(anchor_lang::Key::key(#name), #is_signer)); @@ -131,7 +131,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { AccountField::Field(f) => { let name = &f.ident; //TODO: figure out how to handle None gen - if f.optional { + if f.is_optional { quote! { if let Some(account) = &self.#name { account_infos.push(anchor_lang::ToAccountInfo::to_account_info(account)); diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 282bbbc95d..bf6fcfe01c 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -136,7 +136,7 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream { Constraint::Mint(c) => generate_constraint_mint(f, c), Constraint::Realloc(c) => generate_constraint_realloc(f, c), }; - if f.optional { + if f.is_optional { let ident = &f.ident; match c { Constraint::Init(_) | Constraint::Zeroed(_) => { diff --git a/lang/syn/src/codegen/accounts/to_account_infos.rs b/lang/syn/src/codegen/accounts/to_account_infos.rs index 84fad58034..7d248f9391 100644 --- a/lang/syn/src/codegen/accounts/to_account_infos.rs +++ b/lang/syn/src/codegen/accounts/to_account_infos.rs @@ -16,12 +16,12 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { .fields .iter() .map(|f: &AccountField| { - let (name, optional) = match f { + let (name, is_optional) = match f { AccountField::CompositeField(s) => (&s.ident, false), - AccountField::Field(f) => (&f.ident, f.optional), + AccountField::Field(f) => (&f.ident, f.is_optional), }; //TODO: figure out how to handle None - if !optional { + if !is_optional { quote! { account_infos.extend(self.#name.to_account_infos()); } diff --git a/lang/syn/src/codegen/accounts/to_account_metas.rs b/lang/syn/src/codegen/accounts/to_account_metas.rs index d33f16b543..208148a99f 100644 --- a/lang/syn/src/codegen/accounts/to_account_metas.rs +++ b/lang/syn/src/codegen/accounts/to_account_metas.rs @@ -9,17 +9,17 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { .fields .iter() .map(|f: &AccountField| { - let (name, is_signer, optional) = match f { + let (name, is_signer, is_optional) = match f { AccountField::CompositeField(s) => (&s.ident, quote! {None}, false), AccountField::Field(f) => { let is_signer = match f.constraints.is_signer() { false => quote! {None}, true => quote! {Some(true)}, }; - (&f.ident, is_signer, f.optional) + (&f.ident, is_signer, f.is_optional) } }; - if optional { + if is_optional { quote! { if let Some(#name) = &self.#name { account_metas.extend(#name.to_account_metas(#is_signer)); diff --git a/lang/syn/src/codegen/accounts/try_accounts.rs b/lang/syn/src/codegen/accounts/try_accounts.rs index ccd4869ffc..26b5f45669 100644 --- a/lang/syn/src/codegen/accounts/try_accounts.rs +++ b/lang/syn/src/codegen/accounts/try_accounts.rs @@ -34,7 +34,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { // AccountInfo for later use at constraint validation time. if is_init(af) || f.constraints.zeroed.is_some() { let name = &f.ident; - if f.optional { + if f.is_optional { quote! { let #name = if accounts.is_empty() || accounts[0].key == program_id { None diff --git a/lang/syn/src/idl/file.rs b/lang/syn/src/idl/file.rs index 76aefe3fd2..959f70c9ae 100644 --- a/lang/syn/src/idl/file.rs +++ b/lang/syn/src/idl/file.rs @@ -643,7 +643,7 @@ fn idl_accounts( Ty::Signer => true, _ => acc.constraints.is_signer(), }, - optional: if acc.optional { Some(true) } else { None }, + is_optional: if acc.is_optional { Some(true) } else { None }, docs: if !no_docs { acc.docs.clone() } else { None }, pda: pda::parse(ctx, accounts, acc, seeds_feature), }), diff --git a/lang/syn/src/idl/mod.rs b/lang/syn/src/idl/mod.rs index 7de15ed27d..e96ee85fe8 100644 --- a/lang/syn/src/idl/mod.rs +++ b/lang/syn/src/idl/mod.rs @@ -74,7 +74,7 @@ pub struct IdlAccount { pub is_mut: bool, pub is_signer: bool, #[serde(skip_serializing_if = "Option::is_none")] - pub optional: Option, + pub is_optional: Option, #[serde(skip_serializing_if = "Option::is_none")] pub docs: Option>, #[serde(skip_serializing_if = "Option::is_none", default)] diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index d4a9fe55c7..1b6945e22d 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -183,7 +183,7 @@ impl AccountsStruct { pub fn has_optional(&self) -> bool { for field in &self.fields { if let AccountField::Field(field) = field { - if field.optional { + if field.is_optional { return true; } } @@ -226,7 +226,7 @@ pub struct Field { pub ident: Ident, pub constraints: ConstraintGroup, pub ty: Ty, - pub optional: bool, + pub is_optional: bool, /// IDL Doc comment pub docs: Option>, } @@ -240,7 +240,7 @@ impl Field { } } - pub fn ty_decl(&self, ignore_option: bool) -> proc_macro2::TokenStream { + pub fn ty_decl(&self, option_inner_ty: bool) -> proc_macro2::TokenStream { let account_ty = self.account_ty(); let container_ty = self.container_ty(); let inner_ty = match &self.ty { @@ -291,7 +291,7 @@ impl Field { #container_ty<#account_ty> }, }; - if self.optional && !ignore_option { + if self.is_optional && !option_inner_ty { quote! { Option<#inner_ty> } diff --git a/lang/syn/src/parser/accounts/mod.rs b/lang/syn/src/parser/accounts/mod.rs index 96b62f64cb..06663c67c2 100644 --- a/lang/syn/src/parser/accounts/mod.rs +++ b/lang/syn/src/parser/accounts/mod.rs @@ -205,12 +205,12 @@ pub fn parse_account_field(f: &syn::Field) -> ParseResult { let docs = docs::parse(&f.attrs); let account_field = match is_field_primitive(f)? { true => { - let (ty, optional) = parse_ty(f)?; + let (ty, is_optional) = parse_ty(f)?; let account_constraints = constraints::parse(f, Some(&ty))?; AccountField::Field(Field { ident, ty, - optional, + is_optional, constraints: account_constraints, docs, }) From 33571997ae854cf8b07418222592ab8de63cea69 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 2 Aug 2022 12:41:17 -0500 Subject: [PATCH 011/109] added more traits --- lang/src/accounts/option.rs | 22 ++++++++- lang/src/lib.rs | 19 ++++++++ lang/syn/src/codegen/accounts/mod.rs | 9 ++++ .../accounts/to_optional_account_infos.rs | 46 +++++++++++++++++++ 4 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 lang/syn/src/codegen/accounts/to_optional_account_infos.rs diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index 0659b55c6a..ee7cf48ff5 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -14,7 +14,10 @@ use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; -use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas}; +use crate::{ + Accounts, AccountsClose, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, + ToOptionalAccountInfos, TryKey, +}; impl<'info, T: Accounts<'info>> Accounts<'info> for Option { fn try_accounts( @@ -45,6 +48,15 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option { } } +impl<'info, T: ToAccountInfos<'info>> ToOptionalAccountInfos<'info> for Option { + fn to_optional_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { + match self.as_ref() { + None => program.to_account_infos(), + Some(account) => account.to_account_infos(), + } + } +} + impl ToAccountMetas for Option { fn to_account_metas(&self, is_signer: Option) -> Vec { self.as_ref() @@ -62,6 +74,12 @@ impl<'info, T: AccountsClose<'info>> AccountsClose<'info> for Option { impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Option { fn exit(&self, program_id: &Pubkey) -> Result<()> { - self.as_ref().map_or(Ok(()), |t| T::exit(t, program_id)) + self.as_ref().map_or(Ok(()), |t| t.exit(program_id)) + } +} + +impl TryKey for Option { + fn try_key(&self) -> Result { + self.as_ref().map_or(err!(""), |t| t.try_key()) } } diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 2d795a42c2..126897d24f 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -121,6 +121,14 @@ pub trait ToAccountInfos<'info> { fn to_account_infos(&self) -> Vec>; } +/// Transformation to +/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html) +/// structs. Indended for use for `Accounts` structs with optional accounts in order to +/// pass in the program `AccountInfo`. +pub trait ToOptionalAccountInfos<'info> { + fn to_optional_account_infos(&self, program: &AccountInfo<'info>) -> Vec>; +} + /// Transformation to an `AccountInfo` struct. pub trait ToAccountInfo<'info> { fn to_account_info(&self) -> AccountInfo<'info>; @@ -230,6 +238,17 @@ impl Key for Pubkey { } } +/// Defines the Pubkey of an account +pub trait TryKey { + fn try_key(&self) -> Result; +} + +impl TryKey for T { + fn try_key(&self) -> Result { + Ok(self.key()) + } +} + /// The prelude contains all commonly used components of the crate. /// All programs should include it via `anchor_lang::prelude::*;`. pub mod prelude { diff --git a/lang/syn/src/codegen/accounts/mod.rs b/lang/syn/src/codegen/accounts/mod.rs index 3a239cbe33..0cea985181 100644 --- a/lang/syn/src/codegen/accounts/mod.rs +++ b/lang/syn/src/codegen/accounts/mod.rs @@ -11,20 +11,29 @@ mod constraints; mod exit; mod to_account_infos; mod to_account_metas; +mod to_optional_account_infos; mod try_accounts; pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { let impl_try_accounts = try_accounts::generate(accs); let impl_to_account_infos = to_account_infos::generate(accs); + //TODO: figure out whether to implement that... let impl_to_account_metas = to_account_metas::generate(accs); let impl_exit = exit::generate(accs); let __client_accounts_mod = __client_accounts::generate(accs); let __cpi_client_accounts_mod = __cpi_client_accounts::generate(accs); + let impl_to_optional_account_infos = if accs.has_optional() { + to_optional_account_infos::generate(accs) + } else { + quote! {} + }; + quote! { #impl_try_accounts #impl_to_account_infos + #impl_to_optional_account_infos #impl_to_account_metas #impl_exit diff --git a/lang/syn/src/codegen/accounts/to_optional_account_infos.rs b/lang/syn/src/codegen/accounts/to_optional_account_infos.rs new file mode 100644 index 0000000000..3d8479a707 --- /dev/null +++ b/lang/syn/src/codegen/accounts/to_optional_account_infos.rs @@ -0,0 +1,46 @@ +use crate::codegen::accounts::{generics, ParsedGenerics}; +use crate::{AccountField, AccountsStruct}; +use quote::quote; + +// Generates the `ToOptionalAccountInfos` trait implementation. +pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { + let name = &accs.ident; + let ParsedGenerics { + combined_generics, + trait_generics, + struct_generics, + where_clause, + } = generics(accs); + + let to_optional_acc_infos: Vec = accs + .fields + .iter() + .map(|f: &AccountField| { + let (name, optional) = match f { + AccountField::CompositeField(s) => (&s.ident, false), + AccountField::Field(f) => (&f.ident, f.optional), + }; + if optional { + quote! { + optional_account_infos.extend(self.#name.to_optional_account_infos(program)); + } + } else { + quote! { + optional_account_infos.extend(self.#name.to_account_infos()); + } + } + }) + .collect(); + quote! { + #[automatically_derived] + impl<#combined_generics> anchor_lang::ToOptionalAccountInfos<#trait_generics> for #name <#struct_generics> #where_clause{ + fn to_optional_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { + let mut optional_account_infos = vec![]; + + #(#to_optional_acc_infos)* + + optional_account_infos + } + } + } +} From e945e50e03b5000cd90843acf7fdf3b48034c470 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 2 Aug 2022 14:43:12 -0500 Subject: [PATCH 012/109] added TryKey error --- lang/src/accounts/option.rs | 7 ++++--- lang/src/error.rs | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index ee7cf48ff5..040ba83742 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -15,8 +15,8 @@ use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; use crate::{ - Accounts, AccountsClose, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, - ToOptionalAccountInfos, TryKey, + error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, + ToAccountMetas, ToOptionalAccountInfos, TryKey, }; impl<'info, T: Accounts<'info>> Accounts<'info> for Option { @@ -80,6 +80,7 @@ impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Option { impl TryKey for Option { fn try_key(&self) -> Result { - self.as_ref().map_or(err!(""), |t| t.try_key()) + self.as_ref() + .map_or(Err(ErrorCode::TryKeyOnNone.into()), |t| t.try_key()) } } diff --git a/lang/src/error.rs b/lang/src/error.rs index 30d5a2d2f6..50b58ee608 100644 --- a/lang/src/error.rs +++ b/lang/src/error.rs @@ -184,6 +184,9 @@ pub enum ErrorCode { /// 3017 - The account was duplicated for more than one reallocation #[msg("The account was duplicated for more than one reallocation")] AccountDuplicateReallocs, + /// 3018 - Tried to get the key from None + #[msg("Tried to get the key from None")] + TryKeyOnNone, // State. /// 4000 - The given state account does not have the correct address From e77b82776fcb514e4c7cdb662aefa771e57e4376 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 2 Aug 2022 14:45:53 -0500 Subject: [PATCH 013/109] fix has_one --- lang/src/lib.rs | 2 +- lang/syn/src/codegen/accounts/constraints.rs | 70 +++++++++++++++---- lang/syn/src/codegen/accounts/try_accounts.rs | 5 +- 3 files changed, 59 insertions(+), 18 deletions(-) diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 126897d24f..1976ac4004 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -238,7 +238,7 @@ impl Key for Pubkey { } } -/// Defines the Pubkey of an account +/// Defines the Pubkey of an account in an operation that may fail pub trait TryKey { fn try_key(&self) -> Result; } diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 282bbbc95d..e07e9b421d 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -3,7 +3,7 @@ use proc_macro2_diagnostics::SpanDiagnosticExt; use quote::quote; use syn::Expr; -pub fn generate(f: &Field) -> proc_macro2::TokenStream { +pub fn generate(f: &Field, has_optional: bool) -> proc_macro2::TokenStream { let constraints = linearize(&f.constraints); let rent = constraints @@ -14,7 +14,7 @@ pub fn generate(f: &Field) -> proc_macro2::TokenStream { let checks: Vec = constraints .iter() - .map(|c| generate_constraint(f, c)) + .map(|c| generate_constraint(f, c, has_optional)) .collect(); quote! { @@ -115,12 +115,12 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { constraints } -fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream { +fn generate_constraint(f: &Field, c: &Constraint, has_optional: bool) -> proc_macro2::TokenStream { let stream = match c { Constraint::Init(c) => generate_constraint_init(f, c), Constraint::Zeroed(c) => generate_constraint_zeroed(f, c), Constraint::Mut(c) => generate_constraint_mut(f, c), - Constraint::HasOne(c) => generate_constraint_has_one(f, c), + Constraint::HasOne(c) => generate_constraint_has_one(f, c, has_optional), Constraint::Signer(c) => generate_constraint_signer(f, c), Constraint::Literal(c) => generate_constraint_literal(&f.ident, c), Constraint::Raw(c) => generate_constraint_raw(&f.ident, c), @@ -178,6 +178,7 @@ fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2: &c.error, quote! { ConstraintAddress }, &Some(&(quote! { actual }, quote! { expected })), + true, ); quote! { { @@ -226,7 +227,7 @@ pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2: pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::TokenStream { let ident = &f.ident; - let error = generate_custom_error(ident, &c.error, quote! { ConstraintMut }, &None); + let error = generate_custom_error(ident, &c.error, quote! { ConstraintMut }, &None, true); quote! { if !#ident.to_account_info().is_writable { return #error; @@ -234,7 +235,11 @@ pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::Tok } } -pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macro2::TokenStream { +pub fn generate_constraint_has_one( + f: &Field, + c: &ConstraintHasOne, + has_optional: bool, +) -> proc_macro2::TokenStream { let target = c.join_target.clone(); let ident = &f.ident; let field = match &f.ty { @@ -242,18 +247,45 @@ pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macr Ty::AccountLoader(_) => quote! {#ident.load()?}, _ => quote! {#ident}, }; + let error = generate_custom_error( ident, &c.error, quote! { ConstraintHasOne }, &Some(&(quote! { my_key }, quote! { target_key })), + true, ); - quote! { - { - let my_key = #field.#target; - let target_key = #target.key(); - if my_key != target_key { - return #error; + + let none_error = generate_custom_error( + ident, + &c.error, + quote! { ConstraintHasOne }, + &Some(&(quote! { my_key }, quote! { "None" })), + false, + ); + + if has_optional { + quote! { + { + let my_key = #field.#target; + let target_key = #target.try_key(); + if let Ok(target_key) = target_key { + if my_key != target_key { + return #error; + } + } else { + return #none_error; + } + } + } + } else { + quote! { + { + let my_key = #field.#target; + let target_key = #target.try_key()?; + if my_key != target_key { + return #error; + } } } } @@ -270,7 +302,7 @@ pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro Ty::CpiAccount(_) => quote! { #ident.to_account_info() }, _ => panic!("Invalid syntax: signer cannot be specified."), }; - let error = generate_custom_error(ident, &c.error, quote! { ConstraintSigner }, &None); + let error = generate_custom_error(ident, &c.error, quote! { ConstraintSigner }, &None, true); quote! { if !#info.is_signer { return #error; @@ -302,7 +334,7 @@ pub fn generate_constraint_literal( pub fn generate_constraint_raw(ident: &Ident, c: &ConstraintRaw) -> proc_macro2::TokenStream { let raw = &c.raw; - let error = generate_custom_error(ident, &c.error, quote! { ConstraintRaw }, &None); + let error = generate_custom_error(ident, &c.error, quote! { ConstraintRaw }, &None, true); quote! { if !(#raw) { return #error; @@ -318,6 +350,7 @@ pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2: &c.error, quote! { ConstraintOwner }, &Some(&(quote! { *my_owner }, quote! { owner_address })), + true, ); quote! { { @@ -929,6 +962,7 @@ fn generate_custom_error( custom_error: &Option, error: proc_macro2::TokenStream, compared_values: &Option<&(proc_macro2::TokenStream, proc_macro2::TokenStream)>, + with_pubkeys: bool, ) -> proc_macro2::TokenStream { let account_name = account_name.to_string(); let mut error = match custom_error { @@ -941,7 +975,13 @@ fn generate_custom_error( }; let compared_values = match compared_values { - Some((left, right)) => quote! { .with_pubkeys((#left, #right)) }, + Some((left, right)) => { + if with_pubkeys { + quote! { .with_pubkeys((#left, #right)) } + } else { + quote! { .with_values((#left, #right)) } + } + } None => quote! {}, }; diff --git a/lang/syn/src/codegen/accounts/try_accounts.rs b/lang/syn/src/codegen/accounts/try_accounts.rs index ccd4869ffc..992e12a245 100644 --- a/lang/syn/src/codegen/accounts/try_accounts.rs +++ b/lang/syn/src/codegen/accounts/try_accounts.rs @@ -131,6 +131,7 @@ pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream { // Deserialization for each pda init field. This must be after // the inital extraction from the accounts slice and before access_checks. + let has_optional = accs.has_optional(); let init_fields: Vec = accs .fields .iter() @@ -141,14 +142,14 @@ pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream { true => Some(f), }, }) - .map(constraints::generate) + .map(|f| constraints::generate(f, has_optional)) .collect(); // Constraint checks for each account fields. let access_checks: Vec = non_init_fields .iter() .map(|af: &&AccountField| match af { - AccountField::Field(f) => constraints::generate(f), + AccountField::Field(f) => constraints::generate(f, has_optional), AccountField::CompositeField(s) => constraints::generate_composite(s), }) .collect(); From 74a472a0f6cc4add6c7444cd472014024c17c597 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 2 Aug 2022 14:49:45 -0500 Subject: [PATCH 014/109] update prelude --- lang/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 1976ac4004..e0e283840a 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -262,7 +262,7 @@ pub mod prelude { require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state, system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result, - ToAccountInfo, ToAccountInfos, ToAccountMetas, + ToAccountInfo, ToAccountInfos, ToAccountMetas, ToOptionalAccountInfos, TryKey, }; pub use anchor_attribute_error::*; pub use borsh; From 8cfb071a0b84a8b9e3974e2644c67cb90a6b4735 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 2 Aug 2022 18:59:07 -0500 Subject: [PATCH 015/109] is_optional --- lang/syn/src/codegen/accounts/to_optional_account_infos.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/syn/src/codegen/accounts/to_optional_account_infos.rs b/lang/syn/src/codegen/accounts/to_optional_account_infos.rs index 3d8479a707..7ad72b5536 100644 --- a/lang/syn/src/codegen/accounts/to_optional_account_infos.rs +++ b/lang/syn/src/codegen/accounts/to_optional_account_infos.rs @@ -18,7 +18,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { .map(|f: &AccountField| { let (name, optional) = match f { AccountField::CompositeField(s) => (&s.ident, false), - AccountField::Field(f) => (&f.ident, f.optional), + AccountField::Field(f) => (&f.ident, f.is_optional), }; if optional { quote! { From 5c564e721ce69ad291ab8a2e8738b0ae4be4c032 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 2 Aug 2022 18:59:36 -0500 Subject: [PATCH 016/109] add is_optional helper method --- lang/syn/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 1b6945e22d..5789b1de90 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -207,6 +207,13 @@ impl AccountField { } } + fn is_optional(&self) -> bool { + match self { + AccountField::Field(field) => field.is_optional, + AccountField::CompositeField(_) => false, + } + } + pub fn ty_name(&self) -> Option { match self { AccountField::Field(field) => match &field.ty { From d7c10ab834ba071d567c0bd6060b14c80ce63dae Mon Sep 17 00:00:00 2001 From: febo Date: Thu, 4 Aug 2022 00:03:54 +0100 Subject: [PATCH 017/109] Add TryAccountInfos trait --- lang/attribute/interface/src/lib.rs | 2 +- lang/src/accounts/account.rs | 10 +++- lang/src/accounts/account_info.rs | 8 +++- lang/src/accounts/account_loader.rs | 8 +++- lang/src/accounts/boxed.rs | 8 +++- lang/src/accounts/cpi_account.rs | 7 +++ lang/src/accounts/cpi_state.rs | 11 ++++- lang/src/accounts/loader.rs | 9 +++- lang/src/accounts/option.rs | 22 +++++---- lang/src/accounts/program.rs | 7 +++ lang/src/accounts/program_account.rs | 11 ++++- lang/src/accounts/signer.rs | 8 +++- lang/src/accounts/state.rs | 11 ++++- lang/src/accounts/system_account.rs | 6 +++ lang/src/accounts/sysvar.rs | 8 +++- lang/src/accounts/unchecked_account.rs | 8 +++- lang/src/context.rs | 35 +++++++++++--- lang/src/lib.rs | 13 ++++-- lang/src/vec.rs | 10 +++- .../codegen/accounts/__cpi_client_accounts.rs | 43 +++++++++-------- lang/syn/src/codegen/accounts/mod.rs | 12 ++--- .../src/codegen/accounts/to_account_infos.rs | 14 +----- .../accounts/to_optional_account_infos.rs | 46 ------------------- .../src/codegen/accounts/try_account_infos.rs | 35 ++++++++++++++ lang/syn/src/codegen/program/cpi.rs | 6 ++- 25 files changed, 234 insertions(+), 124 deletions(-) delete mode 100644 lang/syn/src/codegen/accounts/to_optional_account_infos.rs create mode 100644 lang/syn/src/codegen/accounts/try_account_infos.rs diff --git a/lang/attribute/interface/src/lib.rs b/lang/attribute/interface/src/lib.rs index c2656eb554..109712b000 100644 --- a/lang/attribute/interface/src/lib.rs +++ b/lang/attribute/interface/src/lib.rs @@ -218,7 +218,7 @@ pub fn interface( data, } }; - let mut acc_infos = ctx.to_account_infos(); + let mut acc_infos = ctx.to_account_infos(&ctx.program); acc_infos.push(ctx.program.clone()); anchor_lang::solana_program::program::invoke_signed( &ix, diff --git a/lang/src/accounts/account.rs b/lang/src/accounts/account.rs index 9e53452cd6..f679f06eb3 100644 --- a/lang/src/accounts/account.rs +++ b/lang/src/accounts/account.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Owner, - Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, + Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, TryAccountInfos, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -384,6 +384,14 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountI } } +impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> TryAccountInfos<'info> + for Account<'info, T> +{ + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AsRef> for Account<'info, T> { diff --git a/lang/src/accounts/account_info.rs b/lang/src/accounts/account_info.rs index 54d6cc80b5..ce94ac5f28 100644 --- a/lang/src/accounts/account_info.rs +++ b/lang/src/accounts/account_info.rs @@ -3,7 +3,7 @@ //! should be used instead. use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; +use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -43,6 +43,12 @@ impl<'info> ToAccountInfos<'info> for AccountInfo<'info> { } } +impl<'info> TryAccountInfos<'info> for AccountInfo<'info> { + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + impl<'info> AccountsExit<'info> for AccountInfo<'info> {} impl<'info> Key for AccountInfo<'info> { diff --git a/lang/src/accounts/account_loader.rs b/lang/src/accounts/account_loader.rs index 73d8c22a03..661651d9d8 100644 --- a/lang/src/accounts/account_loader.rs +++ b/lang/src/accounts/account_loader.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ Accounts, AccountsClose, AccountsExit, Key, Owner, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, ZeroCopy, + ToAccountMetas, TryAccountInfos, ZeroCopy, }; use arrayref::array_ref; use solana_program::account_info::AccountInfo; @@ -280,6 +280,12 @@ impl<'info, T: ZeroCopy + Owner> ToAccountInfos<'info> for AccountLoader<'info, } } +impl<'info, T: ZeroCopy + Owner> TryAccountInfos<'info> for AccountLoader<'info, T> { + fn try_account_infos(&self,_program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + impl<'info, T: ZeroCopy + Owner> Key for AccountLoader<'info, T> { fn key(&self) -> Pubkey { *self.acc_info.key diff --git a/lang/src/accounts/boxed.rs b/lang/src/accounts/boxed.rs index b143024ead..357591ce36 100644 --- a/lang/src/accounts/boxed.rs +++ b/lang/src/accounts/boxed.rs @@ -13,7 +13,7 @@ //! } //! ``` -use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas}; +use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -44,6 +44,12 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Box { } } +impl<'info, T: ToAccountInfos<'info>> TryAccountInfos<'info> for Box { + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + impl ToAccountMetas for Box { fn to_account_metas(&self, is_signer: Option) -> Vec { T::to_account_metas(self, is_signer) diff --git a/lang/src/accounts/cpi_account.rs b/lang/src/accounts/cpi_account.rs index ee7c907615..22a62d67bf 100644 --- a/lang/src/accounts/cpi_account.rs +++ b/lang/src/accounts/cpi_account.rs @@ -85,6 +85,13 @@ impl<'info, T: AccountDeserialize + Clone> ToAccountInfos<'info> for CpiAccount< } } +#[allow(deprecated)] +impl<'info, T: AccountDeserialize + Clone> TryAccountInfos<'info> for CpiAccount<'info, T> { + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + #[allow(deprecated)] impl<'info, T: AccountDeserialize + Clone> AsRef> for CpiAccount<'info, T> { fn as_ref(&self) -> &AccountInfo<'info> { diff --git a/lang/src/accounts/cpi_state.rs b/lang/src/accounts/cpi_state.rs index 42150d2c89..a3c80dd345 100644 --- a/lang/src/accounts/cpi_state.rs +++ b/lang/src/accounts/cpi_state.rs @@ -3,7 +3,7 @@ use crate::error::ErrorCode; use crate::{accounts::state::ProgramState, context::CpiStateContext}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfos, - ToAccountMetas, + ToAccountMetas, TryAccountInfos, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -110,6 +110,15 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } +#[allow(deprecated)] +impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryAccountInfos<'info> + for CpiState<'info, T> +{ + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + #[allow(deprecated)] impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> for CpiState<'info, T> diff --git a/lang/src/accounts/loader.rs b/lang/src/accounts/loader.rs index 1d52207d58..5606227e9f 100644 --- a/lang/src/accounts/loader.rs +++ b/lang/src/accounts/loader.rs @@ -2,7 +2,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ Accounts, AccountsClose, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, ZeroCopy, + ToAccountMetas, TryAccountInfos, ZeroCopy, }; use arrayref::array_ref; use solana_program::account_info::AccountInfo; @@ -227,6 +227,13 @@ impl<'info, T: ZeroCopy> ToAccountInfos<'info> for Loader<'info, T> { } } +#[allow(deprecated)] +impl<'info, T: ZeroCopy> TryAccountInfos<'info> for Loader<'info, T> { + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + #[allow(deprecated)] impl<'info, T: ZeroCopy> Key for Loader<'info, T> { fn key(&self) -> Pubkey { diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index ee7cf48ff5..c65d60cc8b 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -16,7 +16,7 @@ use solana_program::pubkey::Pubkey; use crate::{ Accounts, AccountsClose, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, - ToOptionalAccountInfos, TryKey, + TryAccountInfos, TryKey, }; impl<'info, T: Accounts<'info>> Accounts<'info> for Option { @@ -42,17 +42,18 @@ impl<'info, T: Accounts<'info>> Accounts<'info> for Option { impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option { fn to_account_infos(&self) -> Vec> { - self.as_ref() - .expect("Cannot run `to_account_infos` on None") - .to_account_infos() + match self { + Some(account) => account.to_account_infos(), + None => panic!("Cannot run `to_account_infos` on None"), + } } } -impl<'info, T: ToAccountInfos<'info>> ToOptionalAccountInfos<'info> for Option { - fn to_optional_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { - match self.as_ref() { - None => program.to_account_infos(), - Some(account) => account.to_account_infos(), +impl<'info, T: ToAccountInfos<'info>> TryAccountInfos<'info> for Option { + fn try_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { + match self { + Some(_) => self.to_account_infos(), + None => vec![program.clone()], } } } @@ -80,6 +81,7 @@ impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Option { impl TryKey for Option { fn try_key(&self) -> Result { - self.as_ref().map_or(err!(""), |t| t.try_key()) + //self.as_ref().map_or(err!(""), |t| t.try_key()) + unimplemented!(); } } diff --git a/lang/src/accounts/program.rs b/lang/src/accounts/program.rs index d7e9975f89..826ac0e74b 100644 --- a/lang/src/accounts/program.rs +++ b/lang/src/accounts/program.rs @@ -3,6 +3,7 @@ use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, Accounts, AccountsExit, Id, Key, Result, ToAccountInfos, ToAccountMetas, + TryAccountInfos, }; use solana_program::account_info::AccountInfo; use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState}; @@ -175,6 +176,12 @@ impl<'info, T: Id + Clone> ToAccountInfos<'info> for Program<'info, T> { } } +impl<'info, T: Id + Clone> TryAccountInfos<'info> for Program<'info, T> { + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + impl<'info, T: Id + Clone> AsRef> for Program<'info, T> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/program_account.rs b/lang/src/accounts/program_account.rs index db6baffc72..488294fb06 100644 --- a/lang/src/accounts/program_account.rs +++ b/lang/src/accounts/program_account.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Result, - ToAccountInfo, ToAccountInfos, ToAccountMetas, + ToAccountInfo, ToAccountInfos, ToAccountMetas, TryAccountInfos, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -147,6 +147,15 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } +#[allow(deprecated)] +impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryAccountInfos<'info> + for ProgramAccount<'info, T> +{ + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + #[allow(deprecated)] impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> for ProgramAccount<'info, T> diff --git a/lang/src/accounts/signer.rs b/lang/src/accounts/signer.rs index 7d757024a3..b91c89e012 100644 --- a/lang/src/accounts/signer.rs +++ b/lang/src/accounts/signer.rs @@ -1,6 +1,6 @@ //! Type validating that the account signed the transaction use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; +use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -91,6 +91,12 @@ impl<'info> ToAccountInfos<'info> for Signer<'info> { } } +impl<'info> TryAccountInfos<'info> for Signer<'info> { + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + impl<'info> AsRef> for Signer<'info> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/state.rs b/lang/src/accounts/state.rs index 563409fdb5..de06254d08 100644 --- a/lang/src/accounts/state.rs +++ b/lang/src/accounts/state.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfo, - ToAccountInfos, ToAccountMetas, + ToAccountInfos, ToAccountMetas, TryAccountInfos, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -108,6 +108,15 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } +#[allow(deprecated)] +impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryAccountInfos<'info> + for ProgramState<'info, T> +{ + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + #[allow(deprecated)] impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> for ProgramState<'info, T> diff --git a/lang/src/accounts/system_account.rs b/lang/src/accounts/system_account.rs index 8c90fd537c..fa29fcc5c0 100644 --- a/lang/src/accounts/system_account.rs +++ b/lang/src/accounts/system_account.rs @@ -70,6 +70,12 @@ impl<'info> ToAccountInfos<'info> for SystemAccount<'info> { } } +impl<'info> TryAccountInfos<'info> for SystemAccount<'info> { + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + impl<'info> AsRef> for SystemAccount<'info> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/sysvar.rs b/lang/src/accounts/sysvar.rs index c955ff9c15..adc6e21efb 100644 --- a/lang/src/accounts/sysvar.rs +++ b/lang/src/accounts/sysvar.rs @@ -1,7 +1,7 @@ //! Type validating that the account is a sysvar and deserializing it use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; +use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -94,6 +94,12 @@ impl<'info, T: solana_program::sysvar::Sysvar> ToAccountInfos<'info> for Sysvar< } } +impl<'info, T: solana_program::sysvar::Sysvar> TryAccountInfos<'info> for Sysvar<'info, T> { + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + impl<'info, T: solana_program::sysvar::Sysvar> AsRef> for Sysvar<'info, T> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/unchecked_account.rs b/lang/src/accounts/unchecked_account.rs index 5a00127579..77eda866c6 100644 --- a/lang/src/accounts/unchecked_account.rs +++ b/lang/src/accounts/unchecked_account.rs @@ -2,7 +2,7 @@ //! that no checks are performed use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; +use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -54,6 +54,12 @@ impl<'info> ToAccountInfos<'info> for UncheckedAccount<'info> { } } +impl<'info> TryAccountInfos<'info> for UncheckedAccount<'info> { + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + impl<'info> AccountsExit<'info> for UncheckedAccount<'info> {} impl<'info> AsRef> for UncheckedAccount<'info> { diff --git a/lang/src/context.rs b/lang/src/context.rs index f007bc5102..3aae4bc425 100644 --- a/lang/src/context.rs +++ b/lang/src/context.rs @@ -1,6 +1,6 @@ //! Data structures that are used to provide non-argument inputs to program endpoints -use crate::{Accounts, ToAccountInfos, ToAccountMetas}; +use crate::{Accounts, ToAccountInfos, ToAccountMetas, TryAccountInfos}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -164,7 +164,7 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> Context<'a, 'b, 'c, 'info, T> { /// ``` pub struct CpiContext<'a, 'b, 'c, 'info, T> where - T: ToAccountMetas + ToAccountInfos<'info>, + T: ToAccountMetas + ToAccountInfos<'info> + TryAccountInfos<'info>, { pub accounts: T, pub remaining_accounts: Vec>, @@ -174,7 +174,7 @@ where impl<'a, 'b, 'c, 'info, T> CpiContext<'a, 'b, 'c, 'info, T> where - T: ToAccountMetas + ToAccountInfos<'info>, + T: ToAccountMetas + ToAccountInfos<'info> + TryAccountInfos<'info>, { pub fn new(program: AccountInfo<'info>, accounts: T) -> Self { Self { @@ -212,18 +212,27 @@ where } } -impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountInfos<'info> +impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryAccountInfos<'info>> ToAccountInfos<'info> for CpiContext<'_, '_, '_, 'info, T> { fn to_account_infos(&self) -> Vec> { - let mut infos = self.accounts.to_account_infos(); + // always uses try_account_infos in case there are optional accounts + let mut infos = self.accounts.try_account_infos(&self.program); infos.extend_from_slice(&self.remaining_accounts); infos.push(self.program.clone()); infos } } -impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountMetas +impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryAccountInfos<'info>> TryAccountInfos<'info> + for CpiContext<'_, '_, '_, 'info, T> +{ + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } +} + +impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryAccountInfos<'info>> ToAccountMetas for CpiContext<'_, '_, '_, 'info, T> { fn to_account_metas(&self, is_signer: Option) -> Vec { @@ -317,7 +326,19 @@ 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(); + let mut infos = self.cpi_ctx.accounts.try_account_infos(&self.cpi_ctx.program); + infos.push(self.state.clone()); + infos.push(self.cpi_ctx.program.clone()); + infos + } +} + +#[allow(deprecated)] +impl<'a, 'b, 'c, 'info, T: Accounts<'info>> TryAccountInfos<'info> + for CpiStateContext<'a, 'b, 'c, 'info, T> +{ + fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + let mut infos = self.cpi_ctx.accounts.try_account_infos(&self.cpi_ctx.program); infos.push(self.state.clone()); infos.push(self.cpi_ctx.program.clone()); infos diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 126897d24f..67196b8236 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -65,7 +65,9 @@ pub type Result = std::result::Result; /// maintain any invariants required for the program to run securely. In most /// cases, it's recommended to use the [`Accounts`](./derive.Accounts.html) /// derive macro to implement this trait. -pub trait Accounts<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized { +pub trait Accounts<'info>: + ToAccountMetas + ToAccountInfos<'info> + TryAccountInfos<'info> + Sized +{ /// Returns the validated accounts struct. What constitutes "valid" is /// program dependent. However, users of these types should never have to /// worry about account substitution attacks. For example, if a program @@ -123,10 +125,11 @@ pub trait ToAccountInfos<'info> { /// Transformation to /// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html) -/// structs. Indended for use for `Accounts` structs with optional accounts in order to -/// pass in the program `AccountInfo`. -pub trait ToOptionalAccountInfos<'info> { - fn to_optional_account_infos(&self, program: &AccountInfo<'info>) -> Vec>; +/// structs. Intended for `Accounts` structs with optional accounts in order to +/// pass in the program `AccountInfo` as a sentinel value. When the account +/// is not present, it returns a copy of the program's `AccountInfo` instead. +pub trait TryAccountInfos<'info> { + fn try_account_infos(&self, program: &AccountInfo<'info>) -> Vec>; } /// Transformation to an `AccountInfo` struct. diff --git a/lang/src/vec.rs b/lang/src/vec.rs index 44c132be6b..7e6af57864 100644 --- a/lang/src/vec.rs +++ b/lang/src/vec.rs @@ -1,4 +1,4 @@ -use crate::{Accounts, Result, ToAccountInfos, ToAccountMetas}; +use crate::{Accounts, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -12,6 +12,14 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Vec { } } +impl<'info, T: TryAccountInfos<'info>> TryAccountInfos<'info> for Vec { + fn try_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { + self.iter() + .flat_map(|item| item.try_account_infos(program)) + .collect() + } +} + impl ToAccountMetas for Vec { fn to_account_metas(&self, is_signer: Option) -> Vec { self.iter() diff --git a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs index 210dcd1507..68d137e453 100644 --- a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs @@ -121,27 +121,21 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { let account_struct_infos: Vec = accs .fields .iter() - .map(|f: &AccountField| match f { - AccountField::CompositeField(s) => { - let name = &s.ident; - quote! { - account_infos.extend(anchor_lang::ToAccountInfos::to_account_infos(&self.#name)); - } + .map(|f: &AccountField| { + let name = &f.ident(); + quote! { + account_infos.extend(anchor_lang::ToAccountInfos::to_account_infos(&self.#name)); } - AccountField::Field(f) => { - let name = &f.ident; - //TODO: figure out how to handle None gen - if f.is_optional { - quote! { - if let Some(account) = &self.#name { - account_infos.push(anchor_lang::ToAccountInfo::to_account_info(account)); - } - } - } else { - quote! { - account_infos.push(anchor_lang::ToAccountInfo::to_account_info(&self.#name)); - } - } + }) + .collect(); + + let account_struct_try_infos: Vec = accs + .fields + .iter() + .map(|f: &AccountField| { + let name = &f.ident(); + quote! { + try_account_infos.extend(anchor_lang::TryAccountInfos::try_account_infos(&self.#name, program)); } }) .collect(); @@ -220,6 +214,15 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { account_infos } } + + #[automatically_derived] + impl<'info> anchor_lang::TryAccountInfos<'info> for #name #generics { + fn try_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { + let mut try_account_infos = vec![]; + #(#account_struct_try_infos)* + try_account_infos + } + } } } } diff --git a/lang/syn/src/codegen/accounts/mod.rs b/lang/syn/src/codegen/accounts/mod.rs index 0cea985181..0a03505218 100644 --- a/lang/syn/src/codegen/accounts/mod.rs +++ b/lang/syn/src/codegen/accounts/mod.rs @@ -11,29 +11,23 @@ mod constraints; mod exit; mod to_account_infos; mod to_account_metas; -mod to_optional_account_infos; +mod try_account_infos; mod try_accounts; pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { let impl_try_accounts = try_accounts::generate(accs); let impl_to_account_infos = to_account_infos::generate(accs); - //TODO: figure out whether to implement that... + let impl_try_account_infos = try_account_infos::generate(accs); let impl_to_account_metas = to_account_metas::generate(accs); let impl_exit = exit::generate(accs); let __client_accounts_mod = __client_accounts::generate(accs); let __cpi_client_accounts_mod = __cpi_client_accounts::generate(accs); - let impl_to_optional_account_infos = if accs.has_optional() { - to_optional_account_infos::generate(accs) - } else { - quote! {} - }; - quote! { #impl_try_accounts #impl_to_account_infos - #impl_to_optional_account_infos + #impl_try_account_infos #impl_to_account_metas #impl_exit diff --git a/lang/syn/src/codegen/accounts/to_account_infos.rs b/lang/syn/src/codegen/accounts/to_account_infos.rs index 7d248f9391..938b22fc38 100644 --- a/lang/syn/src/codegen/accounts/to_account_infos.rs +++ b/lang/syn/src/codegen/accounts/to_account_infos.rs @@ -16,18 +16,8 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { .fields .iter() .map(|f: &AccountField| { - let (name, is_optional) = match f { - AccountField::CompositeField(s) => (&s.ident, false), - AccountField::Field(f) => (&f.ident, f.is_optional), - }; - //TODO: figure out how to handle None - if !is_optional { - quote! { - account_infos.extend(self.#name.to_account_infos()); - } - } else { - quote! {} - } + let name = &f.ident(); + quote! { account_infos.extend(self.#name.to_account_infos()); } }) .collect(); quote! { diff --git a/lang/syn/src/codegen/accounts/to_optional_account_infos.rs b/lang/syn/src/codegen/accounts/to_optional_account_infos.rs deleted file mode 100644 index 3d8479a707..0000000000 --- a/lang/syn/src/codegen/accounts/to_optional_account_infos.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::codegen::accounts::{generics, ParsedGenerics}; -use crate::{AccountField, AccountsStruct}; -use quote::quote; - -// Generates the `ToOptionalAccountInfos` trait implementation. -pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { - let name = &accs.ident; - let ParsedGenerics { - combined_generics, - trait_generics, - struct_generics, - where_clause, - } = generics(accs); - - let to_optional_acc_infos: Vec = accs - .fields - .iter() - .map(|f: &AccountField| { - let (name, optional) = match f { - AccountField::CompositeField(s) => (&s.ident, false), - AccountField::Field(f) => (&f.ident, f.optional), - }; - if optional { - quote! { - optional_account_infos.extend(self.#name.to_optional_account_infos(program)); - } - } else { - quote! { - optional_account_infos.extend(self.#name.to_account_infos()); - } - } - }) - .collect(); - quote! { - #[automatically_derived] - impl<#combined_generics> anchor_lang::ToOptionalAccountInfos<#trait_generics> for #name <#struct_generics> #where_clause{ - fn to_optional_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { - let mut optional_account_infos = vec![]; - - #(#to_optional_acc_infos)* - - optional_account_infos - } - } - } -} diff --git a/lang/syn/src/codegen/accounts/try_account_infos.rs b/lang/syn/src/codegen/accounts/try_account_infos.rs new file mode 100644 index 0000000000..b35510bec2 --- /dev/null +++ b/lang/syn/src/codegen/accounts/try_account_infos.rs @@ -0,0 +1,35 @@ +use crate::codegen::accounts::{generics, ParsedGenerics}; +use crate::{AccountField, AccountsStruct}; +use quote::quote; + +// Generates the `TryAccountInfos` trait implementation. +pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { + let name = &accs.ident; + let ParsedGenerics { + combined_generics, + trait_generics, + struct_generics, + where_clause, + } = generics(accs); + + let try_acc_infos: Vec = accs + .fields + .iter() + .map(|f: &AccountField| { + let name = &f.ident(); + quote! { account_infos.extend(self.#name.try_account_infos(program)); } + }) + .collect(); + quote! { + #[automatically_derived] + impl<#combined_generics> anchor_lang::TryAccountInfos<#trait_generics> for #name <#struct_generics> #where_clause{ + fn try_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { + let mut account_infos = vec![]; + + #(#try_acc_infos)* + + account_infos + } + } + } +} diff --git a/lang/syn/src/codegen/program/cpi.rs b/lang/syn/src/codegen/program/cpi.rs index aa2aec5631..207a835d3a 100644 --- a/lang/syn/src/codegen/program/cpi.rs +++ b/lang/syn/src/codegen/program/cpi.rs @@ -42,7 +42,8 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { data, } }; - let mut acc_infos = ctx.to_account_infos(); + // always use try_account_info in case there are optional accounts + let mut acc_infos = ctx.try_account_infos(&ctx.program); anchor_lang::solana_program::program::invoke_signed( &ix, &acc_infos, @@ -97,7 +98,8 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { data, } }; - let mut acc_infos = ctx.to_account_infos(); + // always use try_account_info in case there are optional accounts + let mut acc_infos = ctx.try_account_infos(&ctx.program); anchor_lang::solana_program::program::invoke_signed( &ix, &acc_infos, From 4398f3661122f1fa79ad8d586a44642d60c6ae9a Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Wed, 3 Aug 2022 18:07:59 -0500 Subject: [PATCH 018/109] improve constraint parser --- lang/syn/src/parser/accounts/mod.rs | 44 ++++++++++++++++++----------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/lang/syn/src/parser/accounts/mod.rs b/lang/syn/src/parser/accounts/mod.rs index 06663c67c2..a33523315b 100644 --- a/lang/syn/src/parser/accounts/mod.rs +++ b/lang/syn/src/parser/accounts/mod.rs @@ -40,17 +40,38 @@ pub fn parse(strct: &syn::ItemStruct) -> ParseResult { } fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { + let mut required_init = false; // INIT let init_fields: Vec<&Field> = fields .iter() .filter_map(|f| match f { - AccountField::Field(field) if field.constraints.init.is_some() => Some(field), + AccountField::Field(field) if field.constraints.init.is_some() => { + if !field.is_optional { + required_init = true + } + Some(field) + } _ => None, }) .collect(); if !init_fields.is_empty() { // init needs system program. + //TODO: WIP + if !fields + .iter() + // ensures that a non optional `system_program` is present with non optional `init` + .any(|f| f.ident() == "system_program" && !(required_init && f.is_optional())) + { + return Err(ParseError::new( + init_fields[0].ident.span(), + "the init constraint requires \ + the system_program field to exist in the account \ + validation struct. Use the Program type to add \ + the system_program field to your validation struct.", + )); + } + if fields.iter().all(|f| f.ident() != "system_program") { return Err(ParseError::new( init_fields[0].ident.span(), @@ -251,11 +272,7 @@ fn is_field_primitive(f: &syn::Field) -> ParseResult { } fn parse_ty(f: &syn::Field) -> ParseResult<(Ty, bool)> { - let path = match &f.ty { - syn::Type::Path(ty_path) => ty_path.path.clone(), - _ => return Err(ParseError::new(f.ty.span(), "invalid account type given")), - }; - let (ident, optional) = ident_string(f)?; + 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)?), @@ -302,10 +319,10 @@ fn option_to_inner_path(path: &Path) -> ParseResult { } } -fn ident_string(f: &syn::Field) -> ParseResult<(String, bool)> { +fn ident_string(f: &syn::Field) -> ParseResult<(String, bool, Path)> { let mut path = match &f.ty { syn::Type::Path(ty_path) => ty_path.path.clone(), - _ => return Err(ParseError::new(f.ty.span(), "invalid type")), + _ => return Err(ParseError::new(f.ty.span(), "invalid account type given")), }; let mut optional = false; if parser::tts_to_string(&path) @@ -319,7 +336,7 @@ fn ident_string(f: &syn::Field) -> ParseResult<(String, bool)> { .replace(' ', "") .starts_with("Box ParseResult<(String, bool)> { } let segments = &path.segments[0]; - Ok((segments.ident.to_string(), optional)) + Ok((segments.ident.to_string(), optional, path)) } fn parse_program_state(path: &syn::Path) -> ParseResult { @@ -426,13 +443,6 @@ fn parse_account(mut path: &syn::Path) -> ParseResult { } } } - if parser::tts_to_string(path) - .replace(' ', "") - .starts_with("Option<") - { - let inner_path = option_to_inner_path(path)?; - return parse_account(&inner_path); - } let segments = &path.segments[0]; match &segments.arguments { From c50d9fcc00a70f2b9419265854ee8b2e20288bc5 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Wed, 3 Aug 2022 18:08:16 -0500 Subject: [PATCH 019/109] initial work on TryToAccountInfo --- lang/src/accounts/option.rs | 13 +++++++++++-- lang/src/lib.rs | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index 040ba83742..b2beac7090 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -15,8 +15,8 @@ use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; use crate::{ - error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, - ToAccountMetas, ToOptionalAccountInfos, TryKey, + error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfo, ToAccountInfos, + ToAccountMetas, ToOptionalAccountInfos, TryKey, TryToAccountInfo, }; impl<'info, T: Accounts<'info>> Accounts<'info> for Option { @@ -84,3 +84,12 @@ impl TryKey for Option { .map_or(Err(ErrorCode::TryKeyOnNone.into()), |t| t.try_key()) } } + +impl<'info, T: ToAccountInfo<'info>> TryToAccountInfo<'info> for Option { + fn try_to_account_info(&self) -> Result> { + self.as_ref() + .map_or(Err(ErrorCode::TryKeyOnNone.into()), |t| { + Ok(t.to_account_info()) + }) + } +} diff --git a/lang/src/lib.rs b/lang/src/lib.rs index e0e283840a..bd640732a2 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -143,6 +143,10 @@ where } } +pub trait TryToAccountInfo<'info> { + fn try_to_account_info(&self) -> Result>; +} + /// A data structure that can be serialized and stored into account storage, /// i.e. an /// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html#structfield.data)'s @@ -263,6 +267,7 @@ pub mod prelude { system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, ToOptionalAccountInfos, TryKey, + TryToAccountInfo, }; pub use anchor_attribute_error::*; pub use borsh; From ee1bf5dca4ff6378994436e84802420335ad958f Mon Sep 17 00:00:00 2001 From: febo Date: Thu, 4 Aug 2022 00:56:08 +0100 Subject: [PATCH 020/109] Rename to TryToAccountInfo --- lang/attribute/interface/src/lib.rs | 4 +-- lang/src/accounts/account.rs | 6 ++-- lang/src/accounts/account_info.rs | 8 +++-- lang/src/accounts/account_loader.rs | 6 ++-- lang/src/accounts/boxed.rs | 9 +++-- lang/src/accounts/cpi_account.rs | 4 +-- lang/src/accounts/cpi_state.rs | 6 ++-- lang/src/accounts/loader.rs | 6 ++-- lang/src/accounts/option.rs | 8 ++--- lang/src/accounts/program.rs | 6 ++-- lang/src/accounts/program_account.rs | 6 ++-- lang/src/accounts/signer.rs | 8 +++-- lang/src/accounts/state.rs | 6 ++-- lang/src/accounts/system_account.rs | 4 +-- lang/src/accounts/sysvar.rs | 8 +++-- lang/src/accounts/unchecked_account.rs | 8 +++-- lang/src/context.rs | 34 +++++++++++-------- lang/src/lib.rs | 9 +++-- lang/src/vec.rs | 8 ++--- .../codegen/accounts/__cpi_client_accounts.rs | 6 ++-- lang/syn/src/codegen/accounts/mod.rs | 6 ++-- ...count_infos.rs => try_to_account_infos.rs} | 8 ++--- lang/syn/src/codegen/program/cpi.rs | 4 +-- 23 files changed, 97 insertions(+), 81 deletions(-) rename lang/syn/src/codegen/accounts/{try_account_infos.rs => try_to_account_infos.rs} (62%) diff --git a/lang/attribute/interface/src/lib.rs b/lang/attribute/interface/src/lib.rs index 109712b000..0fcee2ffda 100644 --- a/lang/attribute/interface/src/lib.rs +++ b/lang/attribute/interface/src/lib.rs @@ -197,7 +197,7 @@ pub fn interface( 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>>( + pub fn #method_name<'a,'b, 'c, 'info, T: anchor_lang::Accounts<'info> + anchor_lang::ToAccountMetas + anchor_lang::ToAccountInfos<'info> + anchor_lang::TryToAccountInfos<'info>>( ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, T>, #(#args),* ) -> anchor_lang::Result<()> { @@ -218,7 +218,7 @@ pub fn interface( data, } }; - let mut acc_infos = ctx.to_account_infos(&ctx.program); + let mut acc_infos = ctx.try_to_account_infos(&ctx.program); acc_infos.push(ctx.program.clone()); anchor_lang::solana_program::program::invoke_signed( &ix, diff --git a/lang/src/accounts/account.rs b/lang/src/accounts/account.rs index f679f06eb3..004764c0b1 100644 --- a/lang/src/accounts/account.rs +++ b/lang/src/accounts/account.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Owner, - Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, TryAccountInfos, + Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, TryToAccountInfos, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -384,10 +384,10 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountI } } -impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> TryAccountInfos<'info> +impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> TryToAccountInfos<'info> for Account<'info, T> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/account_info.rs b/lang/src/accounts/account_info.rs index ce94ac5f28..66a5d68107 100644 --- a/lang/src/accounts/account_info.rs +++ b/lang/src/accounts/account_info.rs @@ -3,7 +3,9 @@ //! should be used instead. use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; +use crate::{ + Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryToAccountInfos, +}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -43,8 +45,8 @@ impl<'info> ToAccountInfos<'info> for AccountInfo<'info> { } } -impl<'info> TryAccountInfos<'info> for AccountInfo<'info> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { +impl<'info> TryToAccountInfos<'info> for AccountInfo<'info> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/account_loader.rs b/lang/src/accounts/account_loader.rs index 661651d9d8..af35449d5a 100644 --- a/lang/src/accounts/account_loader.rs +++ b/lang/src/accounts/account_loader.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ Accounts, AccountsClose, AccountsExit, Key, Owner, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, TryAccountInfos, ZeroCopy, + ToAccountMetas, TryToAccountInfos, ZeroCopy, }; use arrayref::array_ref; use solana_program::account_info::AccountInfo; @@ -280,8 +280,8 @@ impl<'info, T: ZeroCopy + Owner> ToAccountInfos<'info> for AccountLoader<'info, } } -impl<'info, T: ZeroCopy + Owner> TryAccountInfos<'info> for AccountLoader<'info, T> { - fn try_account_infos(&self,_program: &AccountInfo<'info>) -> Vec> { +impl<'info, T: ZeroCopy + Owner> TryToAccountInfos<'info> for AccountLoader<'info, T> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/boxed.rs b/lang/src/accounts/boxed.rs index 357591ce36..99b56c17c3 100644 --- a/lang/src/accounts/boxed.rs +++ b/lang/src/accounts/boxed.rs @@ -13,7 +13,10 @@ //! } //! ``` -use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; +use crate::{ + Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas, + TryToAccountInfos, +}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -44,8 +47,8 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Box { } } -impl<'info, T: ToAccountInfos<'info>> TryAccountInfos<'info> for Box { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { +impl<'info, T: ToAccountInfos<'info>> TryToAccountInfos<'info> for Box { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/cpi_account.rs b/lang/src/accounts/cpi_account.rs index 22a62d67bf..68f70b8bdc 100644 --- a/lang/src/accounts/cpi_account.rs +++ b/lang/src/accounts/cpi_account.rs @@ -86,8 +86,8 @@ impl<'info, T: AccountDeserialize + Clone> ToAccountInfos<'info> for CpiAccount< } #[allow(deprecated)] -impl<'info, T: AccountDeserialize + Clone> TryAccountInfos<'info> for CpiAccount<'info, T> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { +impl<'info, T: AccountDeserialize + Clone> TryToAccountInfos<'info> for CpiAccount<'info, T> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/cpi_state.rs b/lang/src/accounts/cpi_state.rs index a3c80dd345..9a96e471ee 100644 --- a/lang/src/accounts/cpi_state.rs +++ b/lang/src/accounts/cpi_state.rs @@ -3,7 +3,7 @@ use crate::error::ErrorCode; use crate::{accounts::state::ProgramState, context::CpiStateContext}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfos, - ToAccountMetas, TryAccountInfos, + ToAccountMetas, TryToAccountInfos, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -111,10 +111,10 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } #[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryAccountInfos<'info> +impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfos<'info> for CpiState<'info, T> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/loader.rs b/lang/src/accounts/loader.rs index 5606227e9f..09207c1740 100644 --- a/lang/src/accounts/loader.rs +++ b/lang/src/accounts/loader.rs @@ -2,7 +2,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ Accounts, AccountsClose, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, TryAccountInfos, ZeroCopy, + ToAccountMetas, TryToAccountInfos, ZeroCopy, }; use arrayref::array_ref; use solana_program::account_info::AccountInfo; @@ -228,8 +228,8 @@ impl<'info, T: ZeroCopy> ToAccountInfos<'info> for Loader<'info, T> { } #[allow(deprecated)] -impl<'info, T: ZeroCopy> TryAccountInfos<'info> for Loader<'info, T> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { +impl<'info, T: ZeroCopy> TryToAccountInfos<'info> for Loader<'info, T> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index 6ba5d5a4ec..e11dbca1f1 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -15,8 +15,8 @@ use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; use crate::{ - Accounts, AccountsClose, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, - TryToAccountInfo, TryAccountInfos, TryKey, + error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfo, ToAccountInfos, + ToAccountMetas, TryKey, TryToAccountInfo, TryToAccountInfos, }; impl<'info, T: Accounts<'info>> Accounts<'info> for Option { @@ -49,8 +49,8 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option { } } -impl<'info, T: ToAccountInfos<'info>> TryAccountInfos<'info> for Option { - fn try_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { +impl<'info, T: ToAccountInfos<'info>> TryToAccountInfos<'info> for Option { + fn try_to_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { match self { Some(_) => self.to_account_infos(), None => vec![program.clone()], diff --git a/lang/src/accounts/program.rs b/lang/src/accounts/program.rs index 826ac0e74b..d0851cce9b 100644 --- a/lang/src/accounts/program.rs +++ b/lang/src/accounts/program.rs @@ -3,7 +3,7 @@ use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, Accounts, AccountsExit, Id, Key, Result, ToAccountInfos, ToAccountMetas, - TryAccountInfos, + TryToAccountInfos, }; use solana_program::account_info::AccountInfo; use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState}; @@ -176,8 +176,8 @@ impl<'info, T: Id + Clone> ToAccountInfos<'info> for Program<'info, T> { } } -impl<'info, T: Id + Clone> TryAccountInfos<'info> for Program<'info, T> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { +impl<'info, T: Id + Clone> TryToAccountInfos<'info> for Program<'info, T> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/program_account.rs b/lang/src/accounts/program_account.rs index 488294fb06..54da85f07c 100644 --- a/lang/src/accounts/program_account.rs +++ b/lang/src/accounts/program_account.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Result, - ToAccountInfo, ToAccountInfos, ToAccountMetas, TryAccountInfos, + ToAccountInfo, ToAccountInfos, ToAccountMetas, TryToAccountInfos, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -148,10 +148,10 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } #[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryAccountInfos<'info> +impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfos<'info> for ProgramAccount<'info, T> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/signer.rs b/lang/src/accounts/signer.rs index b91c89e012..02e65e8ef1 100644 --- a/lang/src/accounts/signer.rs +++ b/lang/src/accounts/signer.rs @@ -1,6 +1,8 @@ //! Type validating that the account signed the transaction use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; +use crate::{ + Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryToAccountInfos, +}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -91,8 +93,8 @@ impl<'info> ToAccountInfos<'info> for Signer<'info> { } } -impl<'info> TryAccountInfos<'info> for Signer<'info> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { +impl<'info> TryToAccountInfos<'info> for Signer<'info> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/state.rs b/lang/src/accounts/state.rs index de06254d08..622d1e25ee 100644 --- a/lang/src/accounts/state.rs +++ b/lang/src/accounts/state.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfo, - ToAccountInfos, ToAccountMetas, TryAccountInfos, + ToAccountInfos, ToAccountMetas, TryToAccountInfos, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -109,10 +109,10 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } #[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryAccountInfos<'info> +impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfos<'info> for ProgramState<'info, T> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/system_account.rs b/lang/src/accounts/system_account.rs index fa29fcc5c0..a533834e9e 100644 --- a/lang/src/accounts/system_account.rs +++ b/lang/src/accounts/system_account.rs @@ -70,8 +70,8 @@ impl<'info> ToAccountInfos<'info> for SystemAccount<'info> { } } -impl<'info> TryAccountInfos<'info> for SystemAccount<'info> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { +impl<'info> TryToAccountInfos<'info> for SystemAccount<'info> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/sysvar.rs b/lang/src/accounts/sysvar.rs index adc6e21efb..aa3352d56e 100644 --- a/lang/src/accounts/sysvar.rs +++ b/lang/src/accounts/sysvar.rs @@ -1,7 +1,9 @@ //! Type validating that the account is a sysvar and deserializing it use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; +use crate::{ + Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryToAccountInfos, +}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -94,8 +96,8 @@ impl<'info, T: solana_program::sysvar::Sysvar> ToAccountInfos<'info> for Sysvar< } } -impl<'info, T: solana_program::sysvar::Sysvar> TryAccountInfos<'info> for Sysvar<'info, T> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { +impl<'info, T: solana_program::sysvar::Sysvar> TryToAccountInfos<'info> for Sysvar<'info, T> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/accounts/unchecked_account.rs b/lang/src/accounts/unchecked_account.rs index 77eda866c6..910f61ec02 100644 --- a/lang/src/accounts/unchecked_account.rs +++ b/lang/src/accounts/unchecked_account.rs @@ -2,7 +2,9 @@ //! that no checks are performed use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; +use crate::{ + Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas, TryToAccountInfos, +}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -54,8 +56,8 @@ impl<'info> ToAccountInfos<'info> for UncheckedAccount<'info> { } } -impl<'info> TryAccountInfos<'info> for UncheckedAccount<'info> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { +impl<'info> TryToAccountInfos<'info> for UncheckedAccount<'info> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } diff --git a/lang/src/context.rs b/lang/src/context.rs index 3aae4bc425..8d72b22af1 100644 --- a/lang/src/context.rs +++ b/lang/src/context.rs @@ -1,6 +1,6 @@ //! Data structures that are used to provide non-argument inputs to program endpoints -use crate::{Accounts, ToAccountInfos, ToAccountMetas, TryAccountInfos}; +use crate::{Accounts, ToAccountInfos, ToAccountMetas, TryToAccountInfos}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -164,7 +164,7 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> Context<'a, 'b, 'c, 'info, T> { /// ``` pub struct CpiContext<'a, 'b, 'c, 'info, T> where - T: ToAccountMetas + ToAccountInfos<'info> + TryAccountInfos<'info>, + T: ToAccountMetas + ToAccountInfos<'info> + TryToAccountInfos<'info>, { pub accounts: T, pub remaining_accounts: Vec>, @@ -174,7 +174,7 @@ where impl<'a, 'b, 'c, 'info, T> CpiContext<'a, 'b, 'c, 'info, T> where - T: ToAccountMetas + ToAccountInfos<'info> + TryAccountInfos<'info>, + T: ToAccountMetas + ToAccountInfos<'info> + TryToAccountInfos<'info>, { pub fn new(program: AccountInfo<'info>, accounts: T) -> Self { Self { @@ -212,27 +212,27 @@ where } } -impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryAccountInfos<'info>> ToAccountInfos<'info> - for CpiContext<'_, '_, '_, 'info, T> +impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryToAccountInfos<'info>> + ToAccountInfos<'info> for CpiContext<'_, '_, '_, 'info, T> { fn to_account_infos(&self) -> Vec> { // always uses try_account_infos in case there are optional accounts - let mut infos = self.accounts.try_account_infos(&self.program); + let mut infos = self.accounts.try_to_account_infos(&self.program); infos.extend_from_slice(&self.remaining_accounts); infos.push(self.program.clone()); infos } } -impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryAccountInfos<'info>> TryAccountInfos<'info> - for CpiContext<'_, '_, '_, 'info, T> +impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryToAccountInfos<'info>> + TryToAccountInfos<'info> for CpiContext<'_, '_, '_, 'info, T> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } } -impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryAccountInfos<'info>> ToAccountMetas +impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryToAccountInfos<'info>> ToAccountMetas for CpiContext<'_, '_, '_, 'info, T> { fn to_account_metas(&self, is_signer: Option) -> Vec { @@ -326,7 +326,10 @@ 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.try_account_infos(&self.cpi_ctx.program); + let mut infos = self + .cpi_ctx + .accounts + .try_to_account_infos(&self.cpi_ctx.program); infos.push(self.state.clone()); infos.push(self.cpi_ctx.program.clone()); infos @@ -334,11 +337,14 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> ToAccountInfos<'info> } #[allow(deprecated)] -impl<'a, 'b, 'c, 'info, T: Accounts<'info>> TryAccountInfos<'info> +impl<'a, 'b, 'c, 'info, T: Accounts<'info>> TryToAccountInfos<'info> for CpiStateContext<'a, 'b, 'c, 'info, T> { - fn try_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - let mut infos = self.cpi_ctx.accounts.try_account_infos(&self.cpi_ctx.program); + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + let mut infos = self + .cpi_ctx + .accounts + .try_to_account_infos(&self.cpi_ctx.program); infos.push(self.state.clone()); infos.push(self.cpi_ctx.program.clone()); infos diff --git a/lang/src/lib.rs b/lang/src/lib.rs index f20394879e..991a35d527 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -66,7 +66,7 @@ pub type Result = std::result::Result; /// cases, it's recommended to use the [`Accounts`](./derive.Accounts.html) /// derive macro to implement this trait. pub trait Accounts<'info>: - ToAccountMetas + ToAccountInfos<'info> + TryAccountInfos<'info> + Sized + ToAccountMetas + ToAccountInfos<'info> + TryToAccountInfos<'info> + Sized { /// Returns the validated accounts struct. What constitutes "valid" is /// program dependent. However, users of these types should never have to @@ -128,8 +128,8 @@ pub trait ToAccountInfos<'info> { /// structs. Intended for `Accounts` structs with optional accounts in order to /// pass in the program `AccountInfo` as a sentinel value. When the account /// is not present, it returns a copy of the program's `AccountInfo` instead. -pub trait TryAccountInfos<'info> { - fn try_account_infos(&self, program: &AccountInfo<'info>) -> Vec>; +pub trait TryToAccountInfos<'info> { + fn try_to_account_infos(&self, program: &AccountInfo<'info>) -> Vec>; } /// Transformation to an `AccountInfo` struct. @@ -269,8 +269,7 @@ pub mod prelude { require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state, system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result, - ToAccountInfo, ToAccountInfos, ToAccountMetas, ToOptionalAccountInfos, TryKey, - TryToAccountInfo, + ToAccountInfo, ToAccountInfos, ToAccountMetas, TryKey, TryToAccountInfo, TryToAccountInfos, }; pub use anchor_attribute_error::*; pub use borsh; diff --git a/lang/src/vec.rs b/lang/src/vec.rs index 7e6af57864..1f657340ca 100644 --- a/lang/src/vec.rs +++ b/lang/src/vec.rs @@ -1,4 +1,4 @@ -use crate::{Accounts, Result, ToAccountInfos, ToAccountMetas, TryAccountInfos}; +use crate::{Accounts, Result, ToAccountInfos, ToAccountMetas, TryToAccountInfos}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -12,10 +12,10 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Vec { } } -impl<'info, T: TryAccountInfos<'info>> TryAccountInfos<'info> for Vec { - fn try_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { +impl<'info, T: TryToAccountInfos<'info>> TryToAccountInfos<'info> for Vec { + fn try_to_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { self.iter() - .flat_map(|item| item.try_account_infos(program)) + .flat_map(|item| item.try_to_account_infos(program)) .collect() } } diff --git a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs index 68d137e453..333a532403 100644 --- a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs @@ -135,7 +135,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { .map(|f: &AccountField| { let name = &f.ident(); quote! { - try_account_infos.extend(anchor_lang::TryAccountInfos::try_account_infos(&self.#name, program)); + try_account_infos.extend(anchor_lang::TryToAccountInfos::try_to_account_infos(&self.#name, program)); } }) .collect(); @@ -216,8 +216,8 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { } #[automatically_derived] - impl<'info> anchor_lang::TryAccountInfos<'info> for #name #generics { - fn try_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { + impl<'info> anchor_lang::TryToAccountInfos<'info> for #name #generics { + fn try_to_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { let mut try_account_infos = vec![]; #(#account_struct_try_infos)* try_account_infos diff --git a/lang/syn/src/codegen/accounts/mod.rs b/lang/syn/src/codegen/accounts/mod.rs index 0a03505218..fd06a674ec 100644 --- a/lang/syn/src/codegen/accounts/mod.rs +++ b/lang/syn/src/codegen/accounts/mod.rs @@ -11,13 +11,13 @@ mod constraints; mod exit; mod to_account_infos; mod to_account_metas; -mod try_account_infos; mod try_accounts; +mod try_to_account_infos; pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { let impl_try_accounts = try_accounts::generate(accs); let impl_to_account_infos = to_account_infos::generate(accs); - let impl_try_account_infos = try_account_infos::generate(accs); + let impl_try_to_account_infos = try_to_account_infos::generate(accs); let impl_to_account_metas = to_account_metas::generate(accs); let impl_exit = exit::generate(accs); @@ -27,7 +27,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { quote! { #impl_try_accounts #impl_to_account_infos - #impl_try_account_infos + #impl_try_to_account_infos #impl_to_account_metas #impl_exit diff --git a/lang/syn/src/codegen/accounts/try_account_infos.rs b/lang/syn/src/codegen/accounts/try_to_account_infos.rs similarity index 62% rename from lang/syn/src/codegen/accounts/try_account_infos.rs rename to lang/syn/src/codegen/accounts/try_to_account_infos.rs index b35510bec2..4e8be523cc 100644 --- a/lang/syn/src/codegen/accounts/try_account_infos.rs +++ b/lang/syn/src/codegen/accounts/try_to_account_infos.rs @@ -2,7 +2,7 @@ use crate::codegen::accounts::{generics, ParsedGenerics}; use crate::{AccountField, AccountsStruct}; use quote::quote; -// Generates the `TryAccountInfos` trait implementation. +// Generates the `TryToAccountInfos` trait implementation. pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { let name = &accs.ident; let ParsedGenerics { @@ -17,13 +17,13 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { .iter() .map(|f: &AccountField| { let name = &f.ident(); - quote! { account_infos.extend(self.#name.try_account_infos(program)); } + quote! { account_infos.extend(self.#name.try_to_account_infos(program)); } }) .collect(); quote! { #[automatically_derived] - impl<#combined_generics> anchor_lang::TryAccountInfos<#trait_generics> for #name <#struct_generics> #where_clause{ - fn try_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { + impl<#combined_generics> anchor_lang::TryToAccountInfos<#trait_generics> for #name <#struct_generics> #where_clause{ + fn try_to_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { let mut account_infos = vec![]; #(#try_acc_infos)* diff --git a/lang/syn/src/codegen/program/cpi.rs b/lang/syn/src/codegen/program/cpi.rs index 207a835d3a..fddda958ba 100644 --- a/lang/syn/src/codegen/program/cpi.rs +++ b/lang/syn/src/codegen/program/cpi.rs @@ -43,7 +43,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { } }; // always use try_account_info in case there are optional accounts - let mut acc_infos = ctx.try_account_infos(&ctx.program); + let mut acc_infos = ctx.try_to_account_infos(&ctx.program); anchor_lang::solana_program::program::invoke_signed( &ix, &acc_infos, @@ -99,7 +99,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { } }; // always use try_account_info in case there are optional accounts - let mut acc_infos = ctx.try_account_infos(&ctx.program); + let mut acc_infos = ctx.try_to_account_infos(&ctx.program); anchor_lang::solana_program::program::invoke_signed( &ix, &acc_infos, From e21e0baefe5b2f4db6c584bf2e0d38ff30924d60 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Wed, 3 Aug 2022 19:14:29 -0500 Subject: [PATCH 021/109] finished implementing tryToAccountInfo --- lang/src/accounts/account.rs | 10 ++- lang/src/accounts/account_info.rs | 11 ++- lang/src/accounts/account_loader.rs | 8 +- lang/src/accounts/boxed.rs | 11 ++- lang/src/accounts/cpi_account.rs | 7 ++ lang/src/accounts/cpi_state.rs | 13 ++- lang/src/accounts/loader.rs | 9 ++- lang/src/accounts/option.rs | 2 +- lang/src/accounts/program.rs | 9 ++- lang/src/accounts/program_account.rs | 11 ++- lang/src/accounts/signer.rs | 11 ++- lang/src/accounts/state.rs | 11 ++- lang/src/accounts/system_account.rs | 6 ++ lang/src/accounts/sysvar.rs | 11 ++- lang/src/accounts/unchecked_account.rs | 11 ++- lang/src/ctor.rs | 2 +- lang/src/error.rs | 3 + lang/src/lib.rs | 2 + lang/syn/src/codegen/accounts/constraints.rs | 84 ++++++++++---------- tests/optional/programs/optional/src/lib.rs | 2 +- 20 files changed, 176 insertions(+), 58 deletions(-) diff --git a/lang/src/accounts/account.rs b/lang/src/accounts/account.rs index 9e53452cd6..7c53276ae7 100644 --- a/lang/src/accounts/account.rs +++ b/lang/src/accounts/account.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Owner, - Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, + Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -392,6 +392,14 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AsRef TryToAccountInfo<'info> + for Account<'info, T> +{ + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AsRef for Account<'info, T> { diff --git a/lang/src/accounts/account_info.rs b/lang/src/accounts/account_info.rs index 54d6cc80b5..43abf7ba1a 100644 --- a/lang/src/accounts/account_info.rs +++ b/lang/src/accounts/account_info.rs @@ -3,7 +3,10 @@ //! should be used instead. use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; +use crate::{ + Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, + TryToAccountInfo, +}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -43,6 +46,12 @@ impl<'info> ToAccountInfos<'info> for AccountInfo<'info> { } } +impl<'info> TryToAccountInfo<'info> for AccountInfo<'info> { + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + impl<'info> AccountsExit<'info> for AccountInfo<'info> {} impl<'info> Key for AccountInfo<'info> { diff --git a/lang/src/accounts/account_loader.rs b/lang/src/accounts/account_loader.rs index 73d8c22a03..5580f58466 100644 --- a/lang/src/accounts/account_loader.rs +++ b/lang/src/accounts/account_loader.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ Accounts, AccountsClose, AccountsExit, Key, Owner, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, ZeroCopy, + ToAccountMetas, TryToAccountInfo, ZeroCopy, }; use arrayref::array_ref; use solana_program::account_info::AccountInfo; @@ -280,6 +280,12 @@ impl<'info, T: ZeroCopy + Owner> ToAccountInfos<'info> for AccountLoader<'info, } } +impl<'info, T: ZeroCopy + Owner> TryToAccountInfo<'info> for AccountLoader<'info, T> { + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + impl<'info, T: ZeroCopy + Owner> Key for AccountLoader<'info, T> { fn key(&self) -> Pubkey { *self.acc_info.key diff --git a/lang/src/accounts/boxed.rs b/lang/src/accounts/boxed.rs index b143024ead..4a683757c9 100644 --- a/lang/src/accounts/boxed.rs +++ b/lang/src/accounts/boxed.rs @@ -13,7 +13,10 @@ //! } //! ``` -use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas}; +use crate::{ + Accounts, AccountsClose, AccountsExit, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, + TryToAccountInfo, +}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -50,6 +53,12 @@ impl ToAccountMetas for Box { } } +impl<'info, T: ToAccountInfo<'info>> TryToAccountInfo<'info> for Box { + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + impl<'info, T: AccountsClose<'info>> AccountsClose<'info> for Box { fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> { T::close(self, sol_destination) diff --git a/lang/src/accounts/cpi_account.rs b/lang/src/accounts/cpi_account.rs index ee7c907615..ebe9c7aa81 100644 --- a/lang/src/accounts/cpi_account.rs +++ b/lang/src/accounts/cpi_account.rs @@ -85,6 +85,13 @@ impl<'info, T: AccountDeserialize + Clone> ToAccountInfos<'info> for CpiAccount< } } +#[allow(deprecated)] +impl<'info, T: AccountDeserialize + Clone> TryToAccountInfo<'info> for CpiAccount<'info, T> { + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + #[allow(deprecated)] impl<'info, T: AccountDeserialize + Clone> AsRef> for CpiAccount<'info, T> { fn as_ref(&self) -> &AccountInfo<'info> { diff --git a/lang/src/accounts/cpi_state.rs b/lang/src/accounts/cpi_state.rs index 42150d2c89..69e02f3c7a 100644 --- a/lang/src/accounts/cpi_state.rs +++ b/lang/src/accounts/cpi_state.rs @@ -2,8 +2,8 @@ use crate::error::ErrorCode; #[allow(deprecated)] use crate::{accounts::state::ProgramState, context::CpiStateContext}; use crate::{ - AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfos, - ToAccountMetas, + AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfo, + ToAccountInfos, ToAccountMetas, TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -110,6 +110,15 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } +#[allow(deprecated)] +impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfo<'info> + for CpiState<'info, T> +{ + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + #[allow(deprecated)] impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> for CpiState<'info, T> diff --git a/lang/src/accounts/loader.rs b/lang/src/accounts/loader.rs index 1d52207d58..80ed7d0a23 100644 --- a/lang/src/accounts/loader.rs +++ b/lang/src/accounts/loader.rs @@ -2,7 +2,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ Accounts, AccountsClose, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, ZeroCopy, + ToAccountMetas, TryToAccountInfo, ZeroCopy, }; use arrayref::array_ref; use solana_program::account_info::AccountInfo; @@ -227,6 +227,13 @@ impl<'info, T: ZeroCopy> ToAccountInfos<'info> for Loader<'info, T> { } } +#[allow(deprecated)] +impl<'info, T: ZeroCopy> TryToAccountInfo<'info> for Loader<'info, T> { + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + #[allow(deprecated)] impl<'info, T: ZeroCopy> Key for Loader<'info, T> { fn key(&self) -> Pubkey { diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index b2beac7090..898d8826ad 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -88,7 +88,7 @@ impl TryKey for Option { impl<'info, T: ToAccountInfo<'info>> TryToAccountInfo<'info> for Option { fn try_to_account_info(&self) -> Result> { self.as_ref() - .map_or(Err(ErrorCode::TryKeyOnNone.into()), |t| { + .map_or(Err(ErrorCode::TryToAccountInfoOnNone.into()), |t| { Ok(t.to_account_info()) }) } diff --git a/lang/src/accounts/program.rs b/lang/src/accounts/program.rs index d7e9975f89..093d73a362 100644 --- a/lang/src/accounts/program.rs +++ b/lang/src/accounts/program.rs @@ -2,7 +2,8 @@ use crate::error::{Error, ErrorCode}; use crate::{ - AccountDeserialize, Accounts, AccountsExit, Id, Key, Result, ToAccountInfos, ToAccountMetas, + AccountDeserialize, Accounts, AccountsExit, Id, Key, Result, ToAccountInfo, ToAccountInfos, + ToAccountMetas, TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState}; @@ -175,6 +176,12 @@ impl<'info, T: Id + Clone> ToAccountInfos<'info> for Program<'info, T> { } } +impl<'info, T: Id + Clone> TryToAccountInfo<'info> for Program<'info, T> { + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + impl<'info, T: Id + Clone> AsRef> for Program<'info, T> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/program_account.rs b/lang/src/accounts/program_account.rs index db6baffc72..174d6b0dd0 100644 --- a/lang/src/accounts/program_account.rs +++ b/lang/src/accounts/program_account.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Result, - ToAccountInfo, ToAccountInfos, ToAccountMetas, + ToAccountInfo, ToAccountInfos, ToAccountMetas, TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -147,6 +147,15 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } +#[allow(deprecated)] +impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfo<'info> + for ProgramAccount<'info, T> +{ + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + #[allow(deprecated)] impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> for ProgramAccount<'info, T> diff --git a/lang/src/accounts/signer.rs b/lang/src/accounts/signer.rs index 7d757024a3..62d1d27e7f 100644 --- a/lang/src/accounts/signer.rs +++ b/lang/src/accounts/signer.rs @@ -1,6 +1,9 @@ //! Type validating that the account signed the transaction use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; +use crate::{ + Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, + TryToAccountInfo, +}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -91,6 +94,12 @@ impl<'info> ToAccountInfos<'info> for Signer<'info> { } } +impl<'info> TryToAccountInfo<'info> for Signer<'info> { + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + impl<'info> AsRef> for Signer<'info> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/state.rs b/lang/src/accounts/state.rs index 563409fdb5..c3dd61572e 100644 --- a/lang/src/accounts/state.rs +++ b/lang/src/accounts/state.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfo, - ToAccountInfos, ToAccountMetas, + ToAccountInfos, ToAccountMetas, TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -108,6 +108,15 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } +#[allow(deprecated)] +impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfo<'info> + for ProgramState<'info, T> +{ + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + #[allow(deprecated)] impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> for ProgramState<'info, T> diff --git a/lang/src/accounts/system_account.rs b/lang/src/accounts/system_account.rs index 8c90fd537c..832a0bd101 100644 --- a/lang/src/accounts/system_account.rs +++ b/lang/src/accounts/system_account.rs @@ -70,6 +70,12 @@ impl<'info> ToAccountInfos<'info> for SystemAccount<'info> { } } +impl<'info> TryToAccountInfo<'info> for SystemAccount<'info> { + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + impl<'info> AsRef> for SystemAccount<'info> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/sysvar.rs b/lang/src/accounts/sysvar.rs index c955ff9c15..23ad21aea5 100644 --- a/lang/src/accounts/sysvar.rs +++ b/lang/src/accounts/sysvar.rs @@ -1,7 +1,10 @@ //! Type validating that the account is a sysvar and deserializing it use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; +use crate::{ + Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, + TryToAccountInfo, +}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -94,6 +97,12 @@ impl<'info, T: solana_program::sysvar::Sysvar> ToAccountInfos<'info> for Sysvar< } } +impl<'info, T: solana_program::sysvar::Sysvar> TryToAccountInfo<'info> for Sysvar<'info, T> { + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + impl<'info, T: solana_program::sysvar::Sysvar> AsRef> for Sysvar<'info, T> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/unchecked_account.rs b/lang/src/accounts/unchecked_account.rs index 5a00127579..a9800ca1c5 100644 --- a/lang/src/accounts/unchecked_account.rs +++ b/lang/src/accounts/unchecked_account.rs @@ -2,7 +2,10 @@ //! that no checks are performed use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; +use crate::{ + Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, + TryToAccountInfo, +}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -54,6 +57,12 @@ impl<'info> ToAccountInfos<'info> for UncheckedAccount<'info> { } } +impl<'info> TryToAccountInfo<'info> for UncheckedAccount<'info> { + fn try_to_account_info(&self) -> Result> { + Ok(self.to_account_info()) + } +} + impl<'info> AccountsExit<'info> for UncheckedAccount<'info> {} impl<'info> AsRef> for UncheckedAccount<'info> { diff --git a/lang/src/ctor.rs b/lang/src/ctor.rs index c24d392e9f..22794b8686 100644 --- a/lang/src/ctor.rs +++ b/lang/src/ctor.rs @@ -1,4 +1,4 @@ -use crate::{Accounts, ToAccountInfo}; +use crate::{Accounts, TryToAccountInfo}; use solana_program::account_info::AccountInfo; /// The Ctor accounts that can be used to create any account within the program diff --git a/lang/src/error.rs b/lang/src/error.rs index 50b58ee608..2ba342765f 100644 --- a/lang/src/error.rs +++ b/lang/src/error.rs @@ -187,6 +187,9 @@ pub enum ErrorCode { /// 3018 - Tried to get the key from None #[msg("Tried to get the key from None")] TryKeyOnNone, + /// 3019 - Tried to get `to_account_info` from None + #[msg("Tried to get the account info from None")] + TryToAccountInfoOnNone, // State. /// 4000 - The given state account does not have the correct address diff --git a/lang/src/lib.rs b/lang/src/lib.rs index bd640732a2..1ccdafda5d 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -143,6 +143,8 @@ where } } +/// Transformation to an AccountInfo struct. This operation may fail. Intended for use with +/// Optional accounts in order to bubble up Errors more easily pub trait TryToAccountInfo<'info> { fn try_to_account_info(&self) -> Result>; } diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 0376a5d33c..0e469cee9c 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -229,7 +229,7 @@ pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::Tok let ident = &f.ident; let error = generate_custom_error(ident, &c.error, quote! { ConstraintMut }, &None, true); quote! { - if !#ident.to_account_info().is_writable { + if !#ident.try_to_account_info()?.is_writable { return #error; } } @@ -295,11 +295,11 @@ pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro let ident = &f.ident; let info = match f.ty { Ty::AccountInfo => quote! { #ident }, - Ty::ProgramAccount(_) => quote! { #ident.to_account_info() }, - Ty::Account(_) => quote! { #ident.to_account_info() }, - Ty::Loader(_) => quote! { #ident.to_account_info() }, - Ty::AccountLoader(_) => quote! { #ident.to_account_info() }, - Ty::CpiAccount(_) => quote! { #ident.to_account_info() }, + Ty::ProgramAccount(_) => quote! { #ident.try_to_account_info()? }, + Ty::Account(_) => quote! { #ident.try_to_account_info()? }, + Ty::Loader(_) => quote! { #ident.try_to_account_info()? }, + Ty::AccountLoader(_) => quote! { #ident.try_to_account_info()? }, + Ty::CpiAccount(_) => quote! { #ident.try_to_account_info()? }, _ => panic!("Invalid syntax: signer cannot be specified."), }; let error = generate_custom_error(ident, &c.error, quote! { ConstraintSigner }, &None, true); @@ -370,7 +370,7 @@ pub fn generate_constraint_rent_exempt( let ident = &f.ident; let name_str = ident.to_string(); let info = quote! { - #ident.to_account_info() + #ident.try_to_account_info()? }; match c { ConstraintRentExempt::Skip => quote! {}, @@ -398,7 +398,7 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma } let __anchor_rent = anchor_lang::prelude::Rent::get()?; - let __field_info = #field.to_account_info(); + let __field_info = #field.try_to_account_info()?; let __new_rent_minimum = __anchor_rent.minimum_balance(#new_space); let __delta_space = (::std::convert::TryInto::::try_into(#new_space).unwrap()) @@ -414,9 +414,9 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma if __new_rent_minimum > __field_info.lamports() { anchor_lang::system_program::transfer( anchor_lang::context::CpiContext::new( - system_program.to_account_info(), + system_program.try_to_account_info()?, anchor_lang::system_program::Transfer { - from: #payer.to_account_info(), + from: #payer.try_to_account_info()?, to: __field_info.clone(), }, ), @@ -425,11 +425,11 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma } } else { let __lamport_amt = __field_info.lamports().checked_sub(__new_rent_minimum).unwrap(); - **#payer.to_account_info().lamports.borrow_mut() = #payer.to_account_info().lamports().checked_add(__lamport_amt).unwrap(); + **#payer.try_to_account_info()?.lamports.borrow_mut() = #payer.try_to_account_info()?.lamports().checked_add(__lamport_amt).unwrap(); **__field_info.lamports.borrow_mut() = __field_info.lamports().checked_sub(__lamport_amt).unwrap(); } - #field.to_account_info().realloc(#new_space, #zero)?; + #field.try_to_account_info()?.realloc(#new_space, #zero)?; __reallocs.insert(#field.key()); } } @@ -450,7 +450,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma let payer = { let p = &c.payer; quote! { - let payer = #p.to_account_info(); + let payer = #p.try_to_account_info()?; } }; @@ -513,12 +513,12 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma #create_account // Initialize the token account. - let cpi_program = token_program.to_account_info(); + let cpi_program = token_program.try_to_account_info()?; let accounts = anchor_spl::token::InitializeAccount { - account: #field.to_account_info(), - mint: #mint.to_account_info(), - authority: #owner.to_account_info(), - rent: rent.to_account_info(), + account: #field.try_to_account_info()?, + mint: #mint.try_to_account_info()?, + authority: #owner.try_to_account_info()?, + rent: rent.try_to_account_info()?, }; let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts); anchor_spl::token::initialize_account(cpi_ctx)?; @@ -546,15 +546,15 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma if !#if_needed || AsRef::::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID { #payer - let cpi_program = associated_token_program.to_account_info(); + let cpi_program = associated_token_program.try_to_account_info()?; let cpi_accounts = anchor_spl::associated_token::Create { - payer: payer.to_account_info(), - associated_token: #field.to_account_info(), - authority: #owner.to_account_info(), - mint: #mint.to_account_info(), - system_program: system_program.to_account_info(), - token_program: token_program.to_account_info(), - rent: rent.to_account_info(), + payer: payer.try_to_account_info()?, + associated_token: #field.try_to_account_info()?, + authority: #owner.try_to_account_info()?, + mint: #mint.try_to_account_info()?, + system_program: system_program.try_to_account_info()?, + token_program: token_program.try_to_account_info()?, + rent: rent.try_to_account_info()?, }; let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, cpi_accounts); anchor_spl::associated_token::create(cpi_ctx)?; @@ -604,10 +604,10 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma #create_account // Initialize the mint account. - let cpi_program = token_program.to_account_info(); + let cpi_program = token_program.try_to_account_info()?; let accounts = anchor_spl::token::InitializeMint { - mint: #field.to_account_info(), - rent: rent.to_account_info(), + mint: #field.try_to_account_info()?, + rent: rent.try_to_account_info()?, }; let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts); anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.key(), #freeze_authority)?; @@ -656,7 +656,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma #find_pda let #field = { - let actual_field = #field.to_account_info(); + let actual_field = #field.try_to_account_info()?; let actual_owner = actual_field.owner; // Define the account space variable. @@ -690,7 +690,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma { let required_lamports = __anchor_rent.minimum_balance(space); - if pa.to_account_info().lamports() < required_lamports { + if pa.try_to_account_info()?.lamports() < required_lamports { return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintRentExempt).with_account_name(#name_str)); } } @@ -889,10 +889,10 @@ pub fn generate_create_account( // Create the token account with right amount of lamports and space, and the correct owner. let lamports = __anchor_rent.minimum_balance(#space); let cpi_accounts = anchor_lang::system_program::CreateAccount { - from: payer.to_account_info(), - to: #field.to_account_info() + from: payer.try_to_account_info()?, + to: #field.try_to_account_info()? }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); + let cpi_context = anchor_lang::context::CpiContext::new(system_program.try_to_account_info()?, cpi_accounts); anchor_lang::system_program::create_account(cpi_context.with_signer(&[#seeds_with_nonce]), lamports, #space as u64, #owner)?; } else { // Fund the account for rent exemption. @@ -902,23 +902,23 @@ pub fn generate_create_account( .saturating_sub(__current_lamports); if required_lamports > 0 { let cpi_accounts = anchor_lang::system_program::Transfer { - from: payer.to_account_info(), - to: #field.to_account_info(), + from: payer.try_to_account_info()?, + to: #field.try_to_account_info()?, }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); + let cpi_context = anchor_lang::context::CpiContext::new(system_program.try_to_account_info()?, cpi_accounts); anchor_lang::system_program::transfer(cpi_context, required_lamports)?; } // Allocate space. let cpi_accounts = anchor_lang::system_program::Allocate { - account_to_allocate: #field.to_account_info() + account_to_allocate: #field.try_to_account_info()? }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); + let cpi_context = anchor_lang::context::CpiContext::new(system_program.try_to_account_info()?, cpi_accounts); anchor_lang::system_program::allocate(cpi_context.with_signer(&[#seeds_with_nonce]), #space as u64)?; // Assign to the spl token program. let cpi_accounts = anchor_lang::system_program::Assign { - account_to_assign: #field.to_account_info() + account_to_assign: #field.try_to_account_info()? }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); + let cpi_context = anchor_lang::context::CpiContext::new(system_program.try_to_account_info()?, cpi_accounts); anchor_lang::system_program::assign(cpi_context.with_signer(&[#seeds_with_nonce]), #owner)?; } } @@ -931,7 +931,7 @@ pub fn generate_constraint_executable( let name = &f.ident; let name_str = name.to_string(); quote! { - if !#name.to_account_info().executable { + if !#name.try_to_account_info()?.executable { return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintExecutable).with_account_name(#name_str)); } } diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index 518f2d0108..e109460be2 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -18,7 +18,7 @@ mod optional { #[derive(Accounts)] pub struct Initialize<'info> { #[account(mut)] - pub payer: Signer<'info>, + pub payer: Option>, #[account(init, payer=payer, space=16)] pub dummy: Option>, #[account()] From 46da864ba27285894696e9cb8b83e7f09303f6fb Mon Sep 17 00:00:00 2001 From: febo Date: Thu, 4 Aug 2022 01:19:50 +0100 Subject: [PATCH 022/109] Using program method --- lang/src/context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/src/context.rs b/lang/src/context.rs index 8d72b22af1..578d772404 100644 --- a/lang/src/context.rs +++ b/lang/src/context.rs @@ -329,7 +329,7 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> ToAccountInfos<'info> let mut infos = self .cpi_ctx .accounts - .try_to_account_infos(&self.cpi_ctx.program); + .try_to_account_infos(self.program()); infos.push(self.state.clone()); infos.push(self.cpi_ctx.program.clone()); infos @@ -344,7 +344,7 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> TryToAccountInfos<'info> let mut infos = self .cpi_ctx .accounts - .try_to_account_infos(&self.cpi_ctx.program); + .try_to_account_infos(self.program()); infos.push(self.state.clone()); infos.push(self.cpi_ctx.program.clone()); infos From 9a29dcaca75e8008de55aca0fa4e6d997cfcc1eb Mon Sep 17 00:00:00 2001 From: febo Date: Thu, 4 Aug 2022 01:26:15 +0100 Subject: [PATCH 023/109] Formatting --- lang/src/context.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lang/src/context.rs b/lang/src/context.rs index 578d772404..86368a2ef5 100644 --- a/lang/src/context.rs +++ b/lang/src/context.rs @@ -326,10 +326,7 @@ 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 - .try_to_account_infos(self.program()); + let mut infos = self.cpi_ctx.accounts.try_to_account_infos(self.program()); infos.push(self.state.clone()); infos.push(self.cpi_ctx.program.clone()); infos @@ -341,10 +338,7 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> TryToAccountInfos<'info> for CpiStateContext<'a, 'b, 'c, 'info, T> { fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - let mut infos = self - .cpi_ctx - .accounts - .try_to_account_infos(self.program()); + let mut infos = self.cpi_ctx.accounts.try_to_account_infos(self.program()); infos.push(self.state.clone()); infos.push(self.cpi_ctx.program.clone()); infos From 13ff1b7e8691e57fa8aa664400adcd63a341be35 Mon Sep 17 00:00:00 2001 From: febo Date: Thu, 4 Aug 2022 11:00:18 +0100 Subject: [PATCH 024/109] Fix program function call --- lang/syn/src/codegen/program/cpi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/syn/src/codegen/program/cpi.rs b/lang/syn/src/codegen/program/cpi.rs index fddda958ba..262c9549d1 100644 --- a/lang/syn/src/codegen/program/cpi.rs +++ b/lang/syn/src/codegen/program/cpi.rs @@ -43,7 +43,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { } }; // always use try_account_info in case there are optional accounts - let mut acc_infos = ctx.try_to_account_infos(&ctx.program); + let mut acc_infos = ctx.try_to_account_infos(&ctx.program()); anchor_lang::solana_program::program::invoke_signed( &ix, &acc_infos, @@ -99,7 +99,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { } }; // always use try_account_info in case there are optional accounts - let mut acc_infos = ctx.try_to_account_infos(&ctx.program); + let mut acc_infos = ctx.try_to_account_infos(&ctx.program()); anchor_lang::solana_program::program::invoke_signed( &ix, &acc_infos, From a9a8d1d42f02c2beba6bd29c2ebc79c21cd6afcf Mon Sep 17 00:00:00 2001 From: febo Date: Thu, 4 Aug 2022 11:16:16 +0100 Subject: [PATCH 025/109] Remove function return borrow --- lang/syn/src/codegen/program/cpi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/syn/src/codegen/program/cpi.rs b/lang/syn/src/codegen/program/cpi.rs index 262c9549d1..c8254c8271 100644 --- a/lang/syn/src/codegen/program/cpi.rs +++ b/lang/syn/src/codegen/program/cpi.rs @@ -43,7 +43,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { } }; // always use try_account_info in case there are optional accounts - let mut acc_infos = ctx.try_to_account_infos(&ctx.program()); + let mut acc_infos = ctx.try_to_account_infos(ctx.program()); anchor_lang::solana_program::program::invoke_signed( &ix, &acc_infos, @@ -99,7 +99,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { } }; // always use try_account_info in case there are optional accounts - let mut acc_infos = ctx.try_to_account_infos(&ctx.program()); + let mut acc_infos = ctx.try_to_account_infos(ctx.program()); anchor_lang::solana_program::program::invoke_signed( &ix, &acc_infos, From e19f47592940c1ef77b7d762256d4b34ed3a1e37 Mon Sep 17 00:00:00 2001 From: febo Date: Thu, 4 Aug 2022 11:31:11 +0100 Subject: [PATCH 026/109] Fix access to program field --- lang/syn/src/codegen/program/cpi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/syn/src/codegen/program/cpi.rs b/lang/syn/src/codegen/program/cpi.rs index c8254c8271..2b6be402c1 100644 --- a/lang/syn/src/codegen/program/cpi.rs +++ b/lang/syn/src/codegen/program/cpi.rs @@ -99,7 +99,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { } }; // always use try_account_info in case there are optional accounts - let mut acc_infos = ctx.try_to_account_infos(ctx.program()); + let mut acc_infos = ctx.try_to_account_infos(&ctx.program); anchor_lang::solana_program::program::invoke_signed( &ix, &acc_infos, From 11d58fba0559a6c279c119f47938219a11e9d56d Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 4 Aug 2022 09:07:06 -0400 Subject: [PATCH 027/109] finished implementing tryToAccountInfo --- lang/syn/src/parser/accounts/mod.rs | 89 +++++++++++++++++------------ 1 file changed, 54 insertions(+), 35 deletions(-) diff --git a/lang/syn/src/parser/accounts/mod.rs b/lang/syn/src/parser/accounts/mod.rs index a33523315b..201d195de4 100644 --- a/lang/syn/src/parser/accounts/mod.rs +++ b/lang/syn/src/parser/accounts/mod.rs @@ -40,8 +40,27 @@ pub fn parse(strct: &syn::ItemStruct) -> ParseResult { } fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { - let mut required_init = false; + // COMMON ERROR MESSAGE + let message = |constraint: &str, field: &str, required: bool| { + if required { + format! { + "the {} constraint requires \ + the {} field to exist in the account \ + validation struct. Use the Program type to add \ + the {} field to your validation struct.", constraint, field, field + } + } else { + format! { + "an optional {} constraint requires \ + an optional or required {} field to exist \ + in the account validation struct. Use the Program type \ + to add the {} field to your validation struct.", constraint, field, field + } + } + }; + // INIT + let mut required_init = false; let init_fields: Vec<&Field> = fields .iter() .filter_map(|f| match f { @@ -57,7 +76,7 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { if !init_fields.is_empty() { // init needs system program. - //TODO: WIP + if !fields .iter() // ensures that a non optional `system_program` is present with non optional `init` @@ -65,20 +84,7 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { { return Err(ParseError::new( init_fields[0].ident.span(), - "the init constraint requires \ - the system_program field to exist in the account \ - validation struct. Use the Program type to add \ - the system_program field to your validation struct.", - )); - } - - if fields.iter().all(|f| f.ident() != "system_program") { - return Err(ParseError::new( - init_fields[0].ident.span(), - "the init constraint requires \ - the system_program field to exist in the account \ - validation struct. Use the Program type to add \ - the system_program field to your validation struct.", + message("init", "system_program", required_init), )); } @@ -87,29 +93,26 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { match kind { InitKind::Program { .. } => (), InitKind::Token { .. } | InitKind::AssociatedToken { .. } | InitKind::Mint { .. } => { - if fields.iter().all(|f| f.ident() != "token_program") { + if !fields + .iter() + .any(|f| f.ident() == "token_program" && !(required_init && f.is_optional())) + { return Err(ParseError::new( init_fields[0].ident.span(), - "the init constraint requires \ - the token_program field to exist in the account \ - validation struct. Use the Program type to add \ - the token_program field to your validation struct.", + message("init", "token_program", required_init), )); } } } + // a_token needs associated token program. if let InitKind::AssociatedToken { .. } = kind { - if fields - .iter() - .all(|f| f.ident() != "associated_token_program") - { + if !fields.iter().any(|f| { + f.ident() == "associated_token_program" && !(required_init && f.is_optional()) + }) { return Err(ParseError::new( init_fields[0].ident.span(), - "the init constraint requires \ - the associated_token_program field to exist in the account \ - validation struct. Use the Program type to add \ - the associated_token_program field to your validation struct.", + message("init", "associated_token_program", required_init), )); } } @@ -134,6 +137,11 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { field.ident.span(), "the payer specified for an init constraint must be mutable.", )); + } else if associated_payer_field.is_optional && required_init { + return Err(ParseError::new( + field.ident.span(), + "the payer specified for a required init constraint must be required.", + )); } } _ => { @@ -165,23 +173,29 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { } // REALLOC + let mut required_realloc = false; let realloc_fields: Vec<&Field> = fields .iter() .filter_map(|f| match f { - AccountField::Field(field) if field.constraints.realloc.is_some() => Some(field), + AccountField::Field(field) if field.constraints.realloc.is_some() => { + if !field.is_optional { + required_realloc = true + } + Some(field) + } _ => None, }) .collect(); if !realloc_fields.is_empty() { // realloc needs system program. - if fields.iter().all(|f| f.ident() != "system_program") { + if !fields + .iter() + .any(|f| f.ident() == "system_program" && !(required_realloc && f.is_optional())) + { return Err(ParseError::new( realloc_fields[0].ident.span(), - "the realloc constraint requires \ - the system_program field to exist in the account \ - validation struct. Use the Program type to add \ - the system_program field to your validation struct.", + message("realloc", "system_program", required_realloc), )); } @@ -206,6 +220,11 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { field.ident.span(), "the realloc::payer specified for an realloc constraint must be mutable.", )); + } else if associated_payer_field.is_optional && required_realloc { + return Err(ParseError::new( + field.ident.span(), + "the realloc::payer specified for a required realloc constraint must be required.", + )); } } _ => { From d9bd0f3837ed9fba12328d2a07028fee7ea53379 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 4 Aug 2022 19:25:46 -0400 Subject: [PATCH 028/109] add exit try_to_account_infos --- lang/syn/src/codegen/accounts/exit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/syn/src/codegen/accounts/exit.rs b/lang/syn/src/codegen/accounts/exit.rs index 735f225631..e59da85ef5 100644 --- a/lang/syn/src/codegen/accounts/exit.rs +++ b/lang/syn/src/codegen/accounts/exit.rs @@ -32,7 +32,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { quote! { anchor_lang::AccountsClose::close( &self.#ident, - self.#close_target.to_account_info(), + self.#close_target.try_to_account_info()?, ).map_err(|e| e.with_account_name(#name_str))?; } } else { From 33ac27b35acd265a695bfc0133aeb92a76c16bc3 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 4 Aug 2022 19:27:42 -0400 Subject: [PATCH 029/109] descriptive ID path --- lang/syn/src/codegen/accounts/__client_accounts.rs | 2 +- lang/syn/src/codegen/accounts/__cpi_client_accounts.rs | 2 +- lang/syn/src/codegen/accounts/to_account_metas.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lang/syn/src/codegen/accounts/__client_accounts.rs b/lang/syn/src/codegen/accounts/__client_accounts.rs index 206e1ab27e..0cfec58c21 100644 --- a/lang/syn/src/codegen/accounts/__client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__client_accounts.rs @@ -105,7 +105,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { if let Some(#name) = &self.#name { account_metas.push(#meta(*#name, #is_signer)); } else { - account_metas.push(#meta(ID, #is_signer)); + account_metas.push(#meta(crate::ID, #is_signer)); } } } else { diff --git a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs index 333a532403..ba98d06e8e 100644 --- a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs @@ -106,7 +106,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { if let Some(#name) = &self.#name { account_metas.push(#meta(anchor_lang::Key::key(#name), #is_signer)); } else { - account_metas.push(#meta(ID, #is_signer)); + account_metas.push(#meta(crate::ID, #is_signer)); } } } else { diff --git a/lang/syn/src/codegen/accounts/to_account_metas.rs b/lang/syn/src/codegen/accounts/to_account_metas.rs index 208148a99f..d467fdfbe2 100644 --- a/lang/syn/src/codegen/accounts/to_account_metas.rs +++ b/lang/syn/src/codegen/accounts/to_account_metas.rs @@ -24,7 +24,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { if let Some(#name) = &self.#name { account_metas.extend(#name.to_account_metas(#is_signer)); } else { - account_metas.push(AccountMeta::new_readonly(ID, false)); + account_metas.push(AccountMeta::new_readonly(crate::ID, false)); } } } else { From 8ecb81fccd4192e512baeb8c275de1eb07d1fc9e Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 4 Aug 2022 19:28:00 -0400 Subject: [PATCH 030/109] try_to_account_info --- lang/syn/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 5789b1de90..437f2af17e 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -329,9 +329,9 @@ impl Field { }, }; match &self.ty { - Ty::AccountInfo => quote! { #field.to_account_info() }, + Ty::AccountInfo => quote! { #field.try_to_account_info()? }, Ty::UncheckedAccount => { - quote! { UncheckedAccount::try_from(#field.to_account_info()) } + quote! { UncheckedAccount::try_from(#field.try_to_account_info()?) } } Ty::Account(AccountTy { boxed, .. }) => { let stream = if checked { From c9c504aa9689349c680924285fae52d0d941fe47 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 4 Aug 2022 20:40:37 -0400 Subject: [PATCH 031/109] fix close constraint --- lang/syn/src/codegen/accounts/constraints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 0e469cee9c..5316b6edca 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -219,7 +219,7 @@ pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2: let name_str = field.to_string(); let target = &c.sol_dest; quote! { - if #field.key() == #target.key() { + if #field.key() == #target.try_key()? { return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintClose).with_account_name(#name_str)); } } From b6840e8cd8e401e5f84330f09e893f41e73e5380 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 4 Aug 2022 21:06:12 -0400 Subject: [PATCH 032/109] update test files --- .github/workflows/tests.yaml | 2 + tests/optional/Anchor.toml | 5 +- tests/optional/programs/optional/src/lib.rs | 86 +++++++++++++++++++-- tests/optional/tests/optional.js | 15 ---- tests/optional/tests/optional.ts | 49 ++++++++++++ tests/optional/tsconfig.json | 11 +++ tests/package.json | 1 + 7 files changed, 145 insertions(+), 24 deletions(-) delete mode 100644 tests/optional/tests/optional.js create mode 100644 tests/optional/tests/optional.ts create mode 100644 tests/optional/tsconfig.json diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 860e786ea5..842d97be1b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -396,6 +396,8 @@ jobs: path: tests/cpi-returns - cmd: cd tests/multiple-suites && anchor test --skip-lint && npx tsc --noEmit path: tests/multiple-suites + - cmd: cd tests/optional && anchor test --skip-lint && npx tsc --noEmit + path: tests/optional - cmd: cd tests/pda-derivation && anchor test --skip-lint && npx tsc --noEmit path: tests/pda-derivation - cmd: cd tests/anchor-cli-idl && ./test.sh diff --git a/tests/optional/Anchor.toml b/tests/optional/Anchor.toml index 3b3c0c56a7..03380831bd 100644 --- a/tests/optional/Anchor.toml +++ b/tests/optional/Anchor.toml @@ -5,5 +5,8 @@ wallet = "~/.config/solana/id.json" [programs.localnet] optional = "EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU" +[workspace] +members = ["programs/optional"] + [scripts] -test = "yarn run mocha -t 1000000 tests/" +test = "yarn run ts-mocha -t 1000000 -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index e109460be2..cb05463a8c 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -8,9 +8,42 @@ declare_id!("EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU"); #[program] mod optional { use super::*; - pub fn initialize(ctx: Context, value: u64) -> Result<()> { - // let dummy = &mut ctx.accounts.dummy - // todo!() + + pub fn initialize(ctx: Context, value: u64, key: Pubkey) -> Result<()> { + let optional = &mut ctx.accounts.optional; + if let Some(data) = optional { + data.data = value; + } + if let Some(data2) = &mut ctx.accounts.optional2 { + if let Ok(optional_key) = optional.try_key() { + data2.optional = optional_key; + } else { + data2.optional = key; + } + } + Ok(()) + } + + pub fn update(ctx: Context, value: u64, key: Pubkey) -> Result<()> { + if let Some(data) = &mut ctx.accounts.optional { + data.data = value; + }; + if let Some(data2) = &mut ctx.accounts.optional2 { + data2.optional = key; + }; + Ok(()) + } + + pub fn realloc(ctx: Context) -> Result<()> { + let optional = &ctx.accounts.optional; + if let Some(acc) = optional { + let len = acc.to_account_info().data_len(); + msg!("Len: {}", len); + } + Ok(()) + } + + pub fn close(_ctx: Context) -> Result<()> { Ok(()) } } @@ -19,14 +52,51 @@ mod optional { pub struct Initialize<'info> { #[account(mut)] pub payer: Option>, - #[account(init, payer=payer, space=16)] - pub dummy: Option>, - #[account()] - pub dummy2: Account<'info, Dummy>, + #[account(init, payer=payer, space=16, constraint = payer.is_some() && system_program.is_some())] + pub optional: Option>, + #[account(init, payer=payer, space=16, constraint = payer.is_some() && system_program.is_some())] + pub optional2: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct Update<'info> { + #[account(mut)] + pub payer: Option>, + #[account(mut, constraint = payer.is_some())] + pub optional: Option>, + #[account(mut, constraint = payer.is_some())] + pub optional2: Option>, +} + +#[derive(Accounts)] +pub struct Realloc<'info> { + #[account(mut)] + pub payer: Signer<'info>, + #[account(mut, realloc = 20, realloc::payer = payer, realloc::zero = false)] + pub optional: Option>, + #[account(has_one = optional)] + pub optional2: Option>, pub system_program: Program<'info, System>, } +#[derive(Accounts)] +pub struct Close<'info> { + #[account(mut)] + pub payer: Option>, + #[account(mut, close = payer, constraint = payer.is_some() && system_program.is_some())] + pub optional: Option>, + #[account(mut, close = payer, has_one = optional, constraint = payer.is_some() && system_program.is_some())] + pub optional2: Option>, + pub system_program: Option>, +} + #[account] -pub struct Dummy { +pub struct Data { pub data: u64, } + +#[account] +pub struct Data2 { + pub optional: Pubkey, +} diff --git a/tests/optional/tests/optional.js b/tests/optional/tests/optional.js deleted file mode 100644 index d42cec24b3..0000000000 --- a/tests/optional/tests/optional.js +++ /dev/null @@ -1,15 +0,0 @@ -const { assert } = require("chai"); -const anchor = require("@project-serum/anchor"); - -describe("optional", () => { - const provider = anchor.AnchorProvider.local(); - - // Configure the client to use the local cluster. - anchor.setProvider(provider); - - it("Is initialized!", async () => { - const program = anchor.workspace.Optional; - - const dummy = anchor.web3.Keypair.generate(); - }); -}); diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts new file mode 100644 index 0000000000..4a25d638aa --- /dev/null +++ b/tests/optional/tests/optional.ts @@ -0,0 +1,49 @@ +import * as anchor from "@project-serum/anchor"; +import { BN, Program } from "@project-serum/anchor"; +import { expect } from 'chai'; +import { Optional } from "../target/types/optional"; + +describe("Optional", () => { + // configure the client to use the local cluster + anchor.setProvider(anchor.AnchorProvider.env()); + // candy guard for the tests + const keypair = anchor.web3.Keypair.generate(); + // candy guard program + const program = anchor.workspace.Optional as Program; + // payer of the transactions + const payer = (program.provider as anchor.AnchorProvider).wallet; + + it("initialize", async () => { + await program.methods + .initialize() + .accounts({ + candyGuard: keypair.publicKey, + authority: payer.publicKey, + payer: payer.publicKey, + }) + .signers([keypair]) + .rpc(); + let data = await program.account.candyGuard.fetch(keypair.publicKey); + + expect(candy_guard.features.toNumber()).to.equal(0); + }); + + it("update", async () => { + let candy_guard = await program.account.candyGuard.fetch(keypair.publicKey); + expect(candy_guard.features.toNumber()).to.equal(0); + // console.log(program.) + + + await program.methods.update(settings).accounts({ + // candyGuard: null, + authority: null, + candyGuard: keypair.publicKey, + // authority: payer.publicKey, + }).rpc(); + + candy_guard = await program.account.candyGuard.fetch(keypair.publicKey); + // bot_tax (1) + live_date (2) + lamports_charge (8) + console.log(candy_guard.features.toNumber()); + expect(candy_guard.features.toNumber()).to.equal(11); + }); +}); diff --git a/tests/optional/tsconfig.json b/tests/optional/tsconfig.json new file mode 100644 index 0000000000..b3b6656d38 --- /dev/null +++ b/tests/optional/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "types": ["mocha", "chai"], + "typeRoots": ["./node_modules/@types"], + "lib": ["es2015"], + "module": "commonjs", + "target": "es6", + "esModuleInterop": true, + "skipLibCheck": true + } +} diff --git a/tests/package.json b/tests/package.json index f280dcc642..921f7ae5be 100644 --- a/tests/package.json +++ b/tests/package.json @@ -21,6 +21,7 @@ "lockup", "misc", "multisig", + "optional", "permissioned-markets", "pda-derivation", "pyth", From a55701fb9d7e51adff9c9077aca832ac9ddfc525 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 00:36:27 -0400 Subject: [PATCH 033/109] completed typescript optional accounts implementation --- tests/optional/Anchor.toml | 2 +- tests/yarn.lock | 150 +++++++++++++++++++++--- ts/src/idl.ts | 1 + ts/src/program/accounts-resolver.ts | 4 + ts/src/program/namespace/instruction.ts | 14 ++- ts/src/program/namespace/state.ts | 1 + 6 files changed, 148 insertions(+), 24 deletions(-) diff --git a/tests/optional/Anchor.toml b/tests/optional/Anchor.toml index 03380831bd..6e8d0d3f6d 100644 --- a/tests/optional/Anchor.toml +++ b/tests/optional/Anchor.toml @@ -3,7 +3,7 @@ cluster = "localnet" wallet = "~/.config/solana/id.json" [programs.localnet] -optional = "EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU" +optional = "FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG" [workspace] members = ["programs/optional"] diff --git a/tests/yarn.lock b/tests/yarn.lock index 1853d720b8..cbec94b783 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -9,6 +9,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.17.2": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" + integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== + dependencies: + regenerator-runtime "^0.13.4" + "@ethersproject/bytes@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" @@ -51,7 +58,7 @@ toml "^3.0.0" "@project-serum/anchor@file:../ts": - version "0.24.2" + version "0.25.0" dependencies: "@project-serum/borsh" "^0.2.5" "@solana/web3.js" "^1.36.0" @@ -112,6 +119,13 @@ dependencies: buffer "~6.0.3" +"@solana/buffer-layout@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.0.tgz#75b1b11adc487234821c81dfae3119b73a5fd734" + integrity sha512-lR0EMP2HC3+Mxwd4YcnZb0smnaDw7Bl2IQWZiTevRH5ZZBZn6VRWn3/92E3qdU4SSImJkA6IDHawOHAnx/qUvQ== + dependencies: + buffer "~6.0.3" + "@solana/spl-token@^0.1.6", "@solana/spl-token@^0.1.8": version "0.1.8" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" @@ -145,21 +159,24 @@ tweetnacl "^1.0.0" "@solana/web3.js@^1.36.0": - version "1.36.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.36.0.tgz#79d7d5217b49b80139f4de68953adc5b9a9a264f" - integrity sha512-RNT1451iRR7TyW7EJKMCrH/0OXawIe4zVm0DWQASwXlR/u1jmW6FrmH0lujIh7cGTlfOVbH+2ZU9AVUPLBFzwA== + version "1.50.1" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.50.1.tgz#dae726a06267d1bcd88b1e3cd8ae44c709302dcf" + integrity sha512-1l9N/nS8pJEA2YibNT8wa072718O0/A1eKWE0+pdWC5wDGQgBNxZSLuv7Cq5Dcn46WsZ5J5ZstK89q8J/ZZaQA== dependencies: "@babel/runtime" "^7.12.5" "@ethersproject/sha2" "^5.5.0" - "@solana/buffer-layout" "^3.0.0" + "@solana/buffer-layout" "^4.0.0" + bigint-buffer "^1.1.5" bn.js "^5.0.0" - borsh "^0.4.0" + borsh "^0.7.0" bs58 "^4.0.1" buffer "6.0.1" - cross-fetch "^3.1.4" + fast-stable-stringify "^1.0.0" jayson "^3.4.4" js-sha3 "^0.8.0" - rpc-websockets "^7.4.2" + node-fetch "2" + react-native-url-polyfill "^1.3.0" + rpc-websockets "^7.5.0" secp256k1 "^4.0.2" superstruct "^0.14.2" tweetnacl "^1.0.0" @@ -222,6 +239,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.33.tgz#011ee28e38dc7aee1be032ceadf6332a0ab15b12" integrity sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g== +"@types/node@^18.6.4": + version "18.6.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.4.tgz#fd26723a8a3f8f46729812a7f9b4fc2d1608ed39" + integrity sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg== + "@types/qs@*": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" @@ -309,11 +331,25 @@ base64-js@^1.3.1, base64-js@^1.5.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bigint-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" + integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA== + dependencies: + bindings "^1.3.0" + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bindings@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" @@ -324,6 +360,11 @@ bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== +bn.js@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + borsh@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.4.0.tgz#9dd6defe741627f1315eac2a73df61421f6ddb9f" @@ -334,6 +375,15 @@ borsh@^0.4.0: bs58 "^4.0.0" text-encoding-utf-8 "^1.0.2" +borsh@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" + integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA== + dependencies: + bn.js "^5.2.0" + bs58 "^4.0.0" + text-encoding-utf-8 "^1.0.2" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -399,6 +449,14 @@ buffer@6.0.3, buffer@~6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +buffer@^5.4.3: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + bufferutil@^4.0.1: version "4.0.5" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" @@ -630,6 +688,16 @@ eyes@^0.1.8: resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= +fast-stable-stringify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" + integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag== + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -740,7 +808,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -1026,18 +1094,18 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-fetch@2.6.7: +node-fetch@2, node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" @@ -1099,6 +1167,11 @@ prettier@^2.5.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== +punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -1106,6 +1179,13 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +react-native-url-polyfill@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz#c1763de0f2a8c22cc3e959b654c8790622b6ef6a" + integrity sha512-w9JfSkvpqqlix9UjDvJjm1EjSt652zVQ6iwCIj1cVVkwXf4jQhQgTNXY6EVTwuAmUjg6BC6k9RHCBynoLFo3IQ== + dependencies: + whatwg-url-without-unicode "8.0.0-3" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -1137,6 +1217,19 @@ rpc-websockets@^7.4.2: bufferutil "^4.0.1" utf-8-validate "^5.0.2" +rpc-websockets@^7.5.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.0.tgz#bbeb87572e66703ff151e50af1658f98098e2748" + integrity sha512-9tIRi1uZGy7YmDjErf1Ax3wtqdSSLIlnmL5OtOzgd5eqPKbsPpwDP5whUDO2LQay3Xp0CcHlcNSGzacNRluBaQ== + dependencies: + "@babel/runtime" "^7.17.2" + eventemitter3 "^4.0.7" + uuid "^8.3.2" + ws "^8.5.0" + optionalDependencies: + bufferutil "^4.0.1" + utf-8-validate "^5.0.2" + safe-buffer@^5.0.1, safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -1267,7 +1360,7 @@ toml@^3.0.0: tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== traverse-chain@~0.1.0: version "0.1.0" @@ -1344,7 +1437,7 @@ uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.0: +uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -1352,12 +1445,26 @@ uuid@^8.3.0: webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +whatwg-url-without-unicode@8.0.0-3: + version "8.0.0-3" + resolved "https://registry.yarnpkg.com/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz#ab6df4bf6caaa6c85a59f6e82c026151d4bb376b" + integrity sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig== + dependencies: + buffer "^5.4.3" + punycode "^2.1.1" + webidl-conversions "^5.0.0" whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" @@ -1398,6 +1505,11 @@ ws@^7.4.5: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== +ws@^8.5.0: + version "8.8.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" + integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" diff --git a/ts/src/idl.ts b/ts/src/idl.ts index b87968814b..64a5a27176 100644 --- a/ts/src/idl.ts +++ b/ts/src/idl.ts @@ -56,6 +56,7 @@ export type IdlAccount = { name: string; isMut: boolean; isSigner: boolean; + isOptional?: boolean; docs?: string[]; pda?: IdlPda; }; diff --git a/ts/src/program/accounts-resolver.ts b/ts/src/program/accounts-resolver.ts index 455542c4b3..e538538fe7 100644 --- a/ts/src/program/accounts-resolver.ts +++ b/ts/src/program/accounts-resolver.ts @@ -46,6 +46,10 @@ export class AccountsResolver> { const accountDesc = this._idlIx.accounts[k] as IdlAccount; const accountDescName = camelCase(accountDesc.name); + if (accountDesc.isOptional && !this._accounts[accountDescName]) { + this._accounts[accountDescName] = this._programId; + continue; + } // Signers default to the provider. if (accountDesc.isSigner && !this._accounts[accountDescName]) { // @ts-expect-error diff --git a/ts/src/program/namespace/instruction.ts b/ts/src/program/namespace/instruction.ts index 34929f84fa..d65dcf535b 100644 --- a/ts/src/program/namespace/instruction.ts +++ b/ts/src/program/namespace/instruction.ts @@ -12,10 +12,10 @@ import { } from "../../idl.js"; import { IdlError } from "../../error.js"; import { + Address, toInstruction, - validateAccounts, translateAddress, - Address, + validateAccounts, } from "../common.js"; import { Accounts, splitArgsAndCtx } from "../context.js"; import * as features from "../../utils/features.js"; @@ -66,6 +66,7 @@ export default class InstructionNamespaceFactory { return InstructionNamespaceFactory.accountsArray( accs, idlIx.accounts, + programId, idlIx.name ); }; @@ -76,6 +77,7 @@ export default class InstructionNamespaceFactory { public static accountsArray( ctx: Accounts | undefined, accounts: readonly IdlAccountItem[], + programId: PublicKey, ixName?: string ): AccountMeta[] { if (!ctx) { @@ -92,6 +94,7 @@ export default class InstructionNamespaceFactory { return InstructionNamespaceFactory.accountsArray( rpcAccs, (acc as IdlAccounts).accounts, + programId, ixName ).flat(); } else { @@ -108,10 +111,13 @@ export default class InstructionNamespaceFactory { }. Expected PublicKey or string.` ); } + const optionalFalse = account.isOptional && pubkey === programId; + const isWritable = account.isMut && !optionalFalse; + const isSigner = account.isSigner && !optionalFalse; return { pubkey, - isWritable: account.isMut, - isSigner: account.isSigner, + isWritable, + isSigner, }; } }) diff --git a/ts/src/program/namespace/state.ts b/ts/src/program/namespace/state.ts index 3d7430e23c..805cc516b2 100644 --- a/ts/src/program/namespace/state.ts +++ b/ts/src/program/namespace/state.ts @@ -118,6 +118,7 @@ export class StateClient { InstructionNamespaceFactory.accountsArray( accounts, m.accounts, + programId, m.name ) ); From f5fed0a7ae9c9897788f865834e7e0bf961cd14c Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 00:51:02 -0400 Subject: [PATCH 034/109] fix try accounts for init --- lang/syn/src/codegen/accounts/try_accounts.rs | 5 +- tests/optional/programs/optional/src/lib.rs | 46 +++++++++++-------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/lang/syn/src/codegen/accounts/try_accounts.rs b/lang/syn/src/codegen/accounts/try_accounts.rs index 3609826384..aeb527e954 100644 --- a/lang/syn/src/codegen/accounts/try_accounts.rs +++ b/lang/syn/src/codegen/accounts/try_accounts.rs @@ -36,7 +36,10 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { let name = &f.ident; if f.is_optional { quote! { - let #name = if accounts.is_empty() || accounts[0].key == program_id { + let #name = if accounts.is_empty() { + None + } else if accounts[0].key == program_id { + *accounts = &accounts[1..]; None } else { let account = &accounts[0]; diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index cb05463a8c..6666e3100f 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -3,39 +3,41 @@ use anchor_lang::prelude::*; -declare_id!("EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU"); +declare_id!("FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG"); #[program] mod optional { use super::*; pub fn initialize(ctx: Context, value: u64, key: Pubkey) -> Result<()> { - let optional = &mut ctx.accounts.optional; + let optional = &mut ctx.accounts.optional1; + let optional2 = &mut ctx.accounts.optional2; + if let Some(data) = optional { data.data = value; } - if let Some(data2) = &mut ctx.accounts.optional2 { + if let Some(data2) = optional2 { if let Ok(optional_key) = optional.try_key() { - data2.optional = optional_key; + data2.optional1 = optional_key; } else { - data2.optional = key; + data2.optional1 = key; } } Ok(()) } pub fn update(ctx: Context, value: u64, key: Pubkey) -> Result<()> { - if let Some(data) = &mut ctx.accounts.optional { + if let Some(data) = &mut ctx.accounts.optional1 { data.data = value; }; if let Some(data2) = &mut ctx.accounts.optional2 { - data2.optional = key; + data2.optional1 = key; }; Ok(()) } pub fn realloc(ctx: Context) -> Result<()> { - let optional = &ctx.accounts.optional; + let optional = &ctx.accounts.optional1; if let Some(acc) = optional { let len = acc.to_account_info().data_len(); msg!("Len: {}", len); @@ -52,9 +54,9 @@ mod optional { pub struct Initialize<'info> { #[account(mut)] pub payer: Option>, - #[account(init, payer=payer, space=16, constraint = payer.is_some() && system_program.is_some())] - pub optional: Option>, - #[account(init, payer=payer, space=16, constraint = payer.is_some() && system_program.is_some())] + #[account(init, payer=payer, space=Data1::LEN, constraint = payer.is_some() && system_program.is_some())] + pub optional1: Option>, + #[account(init, payer=payer, space=Data2::LEN, constraint = payer.is_some() && system_program.is_some())] pub optional2: Option>, pub system_program: Option>, } @@ -64,7 +66,7 @@ pub struct Update<'info> { #[account(mut)] pub payer: Option>, #[account(mut, constraint = payer.is_some())] - pub optional: Option>, + pub optional1: Option>, #[account(mut, constraint = payer.is_some())] pub optional2: Option>, } @@ -74,8 +76,8 @@ pub struct Realloc<'info> { #[account(mut)] pub payer: Signer<'info>, #[account(mut, realloc = 20, realloc::payer = payer, realloc::zero = false)] - pub optional: Option>, - #[account(has_one = optional)] + pub optional1: Option>, + #[account(has_one = optional1)] pub optional2: Option>, pub system_program: Program<'info, System>, } @@ -85,18 +87,26 @@ pub struct Close<'info> { #[account(mut)] pub payer: Option>, #[account(mut, close = payer, constraint = payer.is_some() && system_program.is_some())] - pub optional: Option>, - #[account(mut, close = payer, has_one = optional, constraint = payer.is_some() && system_program.is_some())] + pub optional1: Option>, + #[account(mut, close = payer, has_one = optional1, constraint = payer.is_some() && system_program.is_some())] pub optional2: Option>, pub system_program: Option>, } #[account] -pub struct Data { +pub struct Data1 { pub data: u64, } +impl Data1 { + const LEN: usize = 8 + 8; +} + #[account] pub struct Data2 { - pub optional: Pubkey, + pub optional1: Pubkey, +} + +impl Data2 { + const LEN: usize = 8 + 32; } From 71d848e8cc53716c047e2401cf7ece7a77d94d12 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 00:51:14 -0400 Subject: [PATCH 035/109] update tests --- tests/optional/package.json | 3 ++ tests/optional/tests/optional.ts | 89 ++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 38 deletions(-) diff --git a/tests/optional/package.json b/tests/optional/package.json index 1eb47965ec..0aa3a8af9a 100644 --- a/tests/optional/package.json +++ b/tests/optional/package.json @@ -15,5 +15,8 @@ }, "scripts": { "test": "anchor test" + }, + "devDependencies": { + "@types/node": "^18.6.4" } } diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index 4a25d638aa..bba179e7ff 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -1,49 +1,62 @@ import * as anchor from "@project-serum/anchor"; -import { BN, Program } from "@project-serum/anchor"; +import {Program} from "@project-serum/anchor"; +import {Optional, IDL} from "../target/types/optional"; import { expect } from 'chai'; -import { Optional } from "../target/types/optional"; +import {SystemProgram} from "@solana/web3.js"; describe("Optional", () => { - // configure the client to use the local cluster - anchor.setProvider(anchor.AnchorProvider.env()); - // candy guard for the tests - const keypair = anchor.web3.Keypair.generate(); - // candy guard program - const program = anchor.workspace.Optional as Program; - // payer of the transactions - const payer = (program.provider as anchor.AnchorProvider).wallet; + // configure the client to use the local cluster + anchor.setProvider(anchor.AnchorProvider.env()); - it("initialize", async () => { - await program.methods - .initialize() - .accounts({ - candyGuard: keypair.publicKey, - authority: payer.publicKey, - payer: payer.publicKey, - }) - .signers([keypair]) - .rpc(); - let data = await program.account.candyGuard.fetch(keypair.publicKey); + // let signer = anchor.web3.Keypair.generate(); + const optional1 = anchor.web3.Keypair.generate(); + const optional2 = anchor.web3.Keypair.generate(); - expect(candy_guard.features.toNumber()).to.equal(0); - }); + // optional program + const program = anchor.workspace.Optional as Program; + // payer of the transactions + const payer = (program.provider as anchor.AnchorProvider).wallet; - it("update", async () => { - let candy_guard = await program.account.candyGuard.fetch(keypair.publicKey); - expect(candy_guard.features.toNumber()).to.equal(0); - // console.log(program.) + it("initialize", async () => { + console.log(); + let txn = await program.methods + .initialize( + new anchor.BN(10), optional1.publicKey + ) + .accounts({ + payer: payer.publicKey, + // optional1: optional1.publicKey, + systemProgram: SystemProgram.programId, + optional2: optional2.publicKey, + }) + // .signers([optional1, optional2]) + .signers([optional2]) + console.log(JSON.stringify((await txn.transaction()).instructions.map(ix => ix.keys), null, " ")); + console.log(JSON.stringify(txn._txFn)) + await txn.rpc(); + let txn2 = await program.methods + .initialize( + new anchor.BN(10), optional1.publicKey + ) + .accounts({ + payer: payer.publicKey, + optional1: optional1.publicKey, + systemProgram: SystemProgram.programId, + // optional2: optional2.publicKey, + }) + .signers([optional1]) + console.log(JSON.stringify((await txn2.transaction()).instructions.map(ix => ix.keys), null, " ")); - await program.methods.update(settings).accounts({ - // candyGuard: null, - authority: null, - candyGuard: keypair.publicKey, - // authority: payer.publicKey, - }).rpc(); + // .signers([optional1, optional2]) + // .signers([optional2]) + // let data1 = await program.account.data1.fetch(optional1.publicKey); + // console.log(data1) + let data2 = await program.account.data2.fetch(optional2.publicKey); + console.log(data2) + // + // // expect(data1.data.toNumber()).to.equal(10); + expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); + }); - candy_guard = await program.account.candyGuard.fetch(keypair.publicKey); - // bot_tax (1) + live_date (2) + lamports_charge (8) - console.log(candy_guard.features.toNumber()); - expect(candy_guard.features.toNumber()).to.equal(11); - }); }); From 185948ad21082d2467fd63c3a8a768d08023f580 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 02:20:23 -0400 Subject: [PATCH 036/109] fix to_account_metas --- lang/syn/src/codegen/accounts/__client_accounts.rs | 2 +- lang/syn/src/codegen/accounts/__cpi_client_accounts.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/syn/src/codegen/accounts/__client_accounts.rs b/lang/syn/src/codegen/accounts/__client_accounts.rs index 0cfec58c21..aa1545f465 100644 --- a/lang/syn/src/codegen/accounts/__client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__client_accounts.rs @@ -105,7 +105,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { if let Some(#name) = &self.#name { account_metas.push(#meta(*#name, #is_signer)); } else { - account_metas.push(#meta(crate::ID, #is_signer)); + account_metas.push(anchor_lang::solana_program::instruction::AccountMeta::new_readonly(crate::ID, false)); } } } else { diff --git a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs index ba98d06e8e..7b391912bd 100644 --- a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs @@ -106,7 +106,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { if let Some(#name) = &self.#name { account_metas.push(#meta(anchor_lang::Key::key(#name), #is_signer)); } else { - account_metas.push(#meta(crate::ID, #is_signer)); + account_metas.push(anchor_lang::solana_program::instruction::AccountMeta::new_readonly(crate::ID, false)); } } } else { From cc15495512ba3b5ac124da842357f311b336a3c3 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 03:48:38 -0400 Subject: [PATCH 037/109] update tests --- client/example/Cargo.toml | 1 + client/example/src/main.rs | 71 +++++++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/client/example/Cargo.toml b/client/example/Cargo.toml index 087a788c92..04e2a030d0 100644 --- a/client/example/Cargo.toml +++ b/client/example/Cargo.toml @@ -12,6 +12,7 @@ anchor-client = { path = "../", features = ["debug"] } basic-2 = { path = "../../examples/tutorial/basic-2/programs/basic-2", features = ["no-entrypoint"] } basic-4 = { path = "../../examples/tutorial/basic-4/programs/basic-4", features = ["no-entrypoint"] } composite = { path = "../../tests/composite/programs/composite", features = ["no-entrypoint"] } +optional = { path = "../../tests/optional/programs/optional", features = ["no-entrypoint"] } events = { path = "../../tests/events/programs/events", features = ["no-entrypoint"] } shellexpand = "2.1.0" anyhow = "1.0.32" diff --git a/client/example/src/main.rs b/client/example/src/main.rs index baa58a8d90..26fdbdd781 100644 --- a/client/example/src/main.rs +++ b/client/example/src/main.rs @@ -21,6 +21,11 @@ use clap::Parser; use composite::accounts::{Bar, CompositeUpdate, Foo, Initialize}; use composite::instruction as composite_instruction; use composite::{DummyA, DummyB}; +// The `accounts` and `instructions` modules are generated by the framework. +use anchor_client::anchor_lang::ToAccountMetas; +use optional::accounts::{Close, Initialize as OptionalInitialize, Realloc, Update}; +use optional::instruction as optional_instruction; +use optional::{Data1, Data2}; use rand::rngs::OsRng; use std::rc::Rc; use std::time::Duration; @@ -34,7 +39,7 @@ pub struct Opts { #[clap(long)] basic_4_pid: Pubkey, #[clap(long)] - events_pid: Pubkey, + optional_pid: Pubkey, } // This example assumes a local validator is running with the programs @@ -46,6 +51,8 @@ fn main() -> Result<()> { // Wallet and cluster params. let payer = read_keypair_file(&*shellexpand::tilde("~/.config/solana/id.json")) .expect("Example requires a keypair file"); + let payer_clone = read_keypair_file(&*shellexpand::tilde("~/.config/solana/id.json")) + .expect("Example requires a keypair file"); let url = Cluster::Custom( "http://localhost:8899".to_string(), "ws://127.0.0.1:8900".to_string(), @@ -55,10 +62,11 @@ fn main() -> Result<()> { let client = Client::new_with_options(url, Rc::new(payer), CommitmentConfig::processed()); // Run tests. - composite(&client, opts.composite_pid)?; - basic_2(&client, opts.basic_2_pid)?; - basic_4(&client, opts.basic_4_pid)?; - events(&client, opts.events_pid)?; + // composite(&client, opts.composite_pid)?; + // basic_2(&client, opts.basic_2_pid)?; + // basic_4(&client, opts.basic_4_pid)?; + // events(&client, opts.events_pid)?; + optional(&client, opts.optional_pid, payer_clone)?; // Success. Ok(()) @@ -227,3 +235,56 @@ pub fn basic_4(client: &Client, pid: Pubkey) -> Result<()> { Ok(()) } + +// Runs a client for examples/tutorial/composite. +// +// Make sure to run a localnet with the program deploy to run this example. +fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { + // Program client. + let program = client.program(pid); + + // `Initialize` parameters. + let optional1 = Keypair::generate(&mut OsRng); + let optional2 = Keypair::generate(&mut OsRng); + + let initialize = OptionalInitialize { + payer: Some(program.payer()), + optional1: Some(optional1.pubkey()), + optional2: None, + system_program: Some(system_program::ID), + }; + let metas = initialize.to_account_metas(None); + println!("{metas:#?}"); + + // Build and send a transaction. + let builder = program + .request() + .signer(&optional1) + .signer(&signer) + // .signer(&optional2) + .accounts(OptionalInitialize { + payer: Some(program.payer()), + optional1: Some(optional1.pubkey()), + optional2: None, + system_program: Some(system_program::ID), + }) + .args(optional_instruction::Initialize { + value: 10, + key: optional1.pubkey(), + }); + + let txn = builder.transaction()?; + println!("{txn:#?}"); + + let sent = builder.send()?; + + // Assert the transaction worked. + let optional1_account: Data1 = program.account(optional1.pubkey())?; + // let optional2_account: Result = program.account(optional2.pubkey()); + + assert_eq!(optional1_account.data, 10); + + println!("Optional success!"); + + Ok(()) +} From 201925c6454bd21cdbd031050b1ec2d8f6b0951b Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 11:42:11 -0400 Subject: [PATCH 038/109] fix linting --- tests/optional/tests/optional.ts | 113 ++++++++++++++++--------------- 1 file changed, 60 insertions(+), 53 deletions(-) diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index bba179e7ff..618b2affcf 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -1,62 +1,69 @@ import * as anchor from "@project-serum/anchor"; -import {Program} from "@project-serum/anchor"; -import {Optional, IDL} from "../target/types/optional"; -import { expect } from 'chai'; -import {SystemProgram} from "@solana/web3.js"; +import { Program } from "@project-serum/anchor"; +import { Optional, IDL } from "../target/types/optional"; +import { expect } from "chai"; +import { SystemProgram } from "@solana/web3.js"; describe("Optional", () => { - // configure the client to use the local cluster - anchor.setProvider(anchor.AnchorProvider.env()); + // configure the client to use the local cluster + anchor.setProvider(anchor.AnchorProvider.env()); - // let signer = anchor.web3.Keypair.generate(); - const optional1 = anchor.web3.Keypair.generate(); - const optional2 = anchor.web3.Keypair.generate(); + // let signer = anchor.web3.Keypair.generate(); + const optional1 = anchor.web3.Keypair.generate(); + const optional2 = anchor.web3.Keypair.generate(); - // optional program - const program = anchor.workspace.Optional as Program; - // payer of the transactions - const payer = (program.provider as anchor.AnchorProvider).wallet; + // optional program + const program = anchor.workspace.Optional as Program; + // payer of the transactions + const payer = (program.provider as anchor.AnchorProvider).wallet; - it("initialize", async () => { - console.log(); - let txn = await program.methods - .initialize( - new anchor.BN(10), optional1.publicKey - ) - .accounts({ - payer: payer.publicKey, - // optional1: optional1.publicKey, - systemProgram: SystemProgram.programId, - optional2: optional2.publicKey, - }) - // .signers([optional1, optional2]) - .signers([optional2]) - console.log(JSON.stringify((await txn.transaction()).instructions.map(ix => ix.keys), null, " ")); - console.log(JSON.stringify(txn._txFn)) - await txn.rpc(); + it("initialize", async () => { + console.log(); + let txn = await program.methods + .initialize(new anchor.BN(10), optional1.publicKey) + .accounts({ + payer: payer.publicKey, + // optional1: optional1.publicKey, + systemProgram: SystemProgram.programId, + optional2: optional2.publicKey, + }) + // .signers([optional1, optional2]) + .signers([optional2]); + console.log( + JSON.stringify( + (await txn.transaction()).instructions.map((ix) => ix.keys), + null, + " " + ) + ); + console.log(JSON.stringify(txn._txFn)); + await txn.rpc(); - let txn2 = await program.methods - .initialize( - new anchor.BN(10), optional1.publicKey - ) - .accounts({ - payer: payer.publicKey, - optional1: optional1.publicKey, - systemProgram: SystemProgram.programId, - // optional2: optional2.publicKey, - }) - .signers([optional1]) - console.log(JSON.stringify((await txn2.transaction()).instructions.map(ix => ix.keys), null, " ")); - - // .signers([optional1, optional2]) - // .signers([optional2]) - // let data1 = await program.account.data1.fetch(optional1.publicKey); - // console.log(data1) - let data2 = await program.account.data2.fetch(optional2.publicKey); - console.log(data2) - // - // // expect(data1.data.toNumber()).to.equal(10); - expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); - }); + let txn2 = await program.methods + .initialize(new anchor.BN(10), optional1.publicKey) + .accounts({ + payer: payer.publicKey, + optional1: optional1.publicKey, + systemProgram: SystemProgram.programId, + // optional2: optional2.publicKey, + }) + .signers([optional1]); + console.log( + JSON.stringify( + (await txn2.transaction()).instructions.map((ix) => ix.keys), + null, + " " + ) + ); + // .signers([optional1, optional2]) + // .signers([optional2]) + // let data1 = await program.account.data1.fetch(optional1.publicKey); + // console.log(data1) + let data2 = await program.account.data2.fetch(optional2.publicKey); + console.log(data2); + // + // // expect(data1.data.toNumber()).to.equal(10); + expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); + }); }); From af164001f6a4c76b5ae6caececc75df8735e57ab Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 12:01:56 -0400 Subject: [PATCH 039/109] remove types/node --- tests/optional/package.json | 3 --- tests/yarn.lock | 5 ----- 2 files changed, 8 deletions(-) diff --git a/tests/optional/package.json b/tests/optional/package.json index 0aa3a8af9a..1eb47965ec 100644 --- a/tests/optional/package.json +++ b/tests/optional/package.json @@ -15,8 +15,5 @@ }, "scripts": { "test": "anchor test" - }, - "devDependencies": { - "@types/node": "^18.6.4" } } diff --git a/tests/yarn.lock b/tests/yarn.lock index cbec94b783..048fbccfb9 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -239,11 +239,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.33.tgz#011ee28e38dc7aee1be032ceadf6332a0ab15b12" integrity sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g== -"@types/node@^18.6.4": - version "18.6.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.4.tgz#fd26723a8a3f8f46729812a7f9b4fc2d1608ed39" - integrity sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg== - "@types/qs@*": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" From b24108312c599e6d4dce8f41c2583bbfea4577b4 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 12:44:09 -0400 Subject: [PATCH 040/109] update yarn.lock maybe? --- tests/yarn.lock | 145 +++++++----------------------------------------- 1 file changed, 19 insertions(+), 126 deletions(-) diff --git a/tests/yarn.lock b/tests/yarn.lock index 048fbccfb9..1853d720b8 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -9,13 +9,6 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.17.2": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== - dependencies: - regenerator-runtime "^0.13.4" - "@ethersproject/bytes@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" @@ -58,7 +51,7 @@ toml "^3.0.0" "@project-serum/anchor@file:../ts": - version "0.25.0" + version "0.24.2" dependencies: "@project-serum/borsh" "^0.2.5" "@solana/web3.js" "^1.36.0" @@ -119,13 +112,6 @@ dependencies: buffer "~6.0.3" -"@solana/buffer-layout@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.0.tgz#75b1b11adc487234821c81dfae3119b73a5fd734" - integrity sha512-lR0EMP2HC3+Mxwd4YcnZb0smnaDw7Bl2IQWZiTevRH5ZZBZn6VRWn3/92E3qdU4SSImJkA6IDHawOHAnx/qUvQ== - dependencies: - buffer "~6.0.3" - "@solana/spl-token@^0.1.6", "@solana/spl-token@^0.1.8": version "0.1.8" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" @@ -159,24 +145,21 @@ tweetnacl "^1.0.0" "@solana/web3.js@^1.36.0": - version "1.50.1" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.50.1.tgz#dae726a06267d1bcd88b1e3cd8ae44c709302dcf" - integrity sha512-1l9N/nS8pJEA2YibNT8wa072718O0/A1eKWE0+pdWC5wDGQgBNxZSLuv7Cq5Dcn46WsZ5J5ZstK89q8J/ZZaQA== + version "1.36.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.36.0.tgz#79d7d5217b49b80139f4de68953adc5b9a9a264f" + integrity sha512-RNT1451iRR7TyW7EJKMCrH/0OXawIe4zVm0DWQASwXlR/u1jmW6FrmH0lujIh7cGTlfOVbH+2ZU9AVUPLBFzwA== dependencies: "@babel/runtime" "^7.12.5" "@ethersproject/sha2" "^5.5.0" - "@solana/buffer-layout" "^4.0.0" - bigint-buffer "^1.1.5" + "@solana/buffer-layout" "^3.0.0" bn.js "^5.0.0" - borsh "^0.7.0" + borsh "^0.4.0" bs58 "^4.0.1" buffer "6.0.1" - fast-stable-stringify "^1.0.0" + cross-fetch "^3.1.4" jayson "^3.4.4" js-sha3 "^0.8.0" - node-fetch "2" - react-native-url-polyfill "^1.3.0" - rpc-websockets "^7.5.0" + rpc-websockets "^7.4.2" secp256k1 "^4.0.2" superstruct "^0.14.2" tweetnacl "^1.0.0" @@ -326,25 +309,11 @@ base64-js@^1.3.1, base64-js@^1.5.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -bigint-buffer@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" - integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA== - dependencies: - bindings "^1.3.0" - binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bindings@^1.3.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" @@ -355,11 +324,6 @@ bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== -bn.js@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - borsh@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.4.0.tgz#9dd6defe741627f1315eac2a73df61421f6ddb9f" @@ -370,15 +334,6 @@ borsh@^0.4.0: bs58 "^4.0.0" text-encoding-utf-8 "^1.0.2" -borsh@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" - integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA== - dependencies: - bn.js "^5.2.0" - bs58 "^4.0.0" - text-encoding-utf-8 "^1.0.2" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -444,14 +399,6 @@ buffer@6.0.3, buffer@~6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -buffer@^5.4.3: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - bufferutil@^4.0.1: version "4.0.5" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" @@ -683,16 +630,6 @@ eyes@^0.1.8: resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= -fast-stable-stringify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" - integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag== - -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -803,7 +740,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -ieee754@^1.1.13, ieee754@^1.2.1: +ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -1089,18 +1026,18 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== -node-fetch@2, node-fetch@2.6.7: +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" @@ -1162,11 +1099,6 @@ prettier@^2.5.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== -punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -1174,13 +1106,6 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -react-native-url-polyfill@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz#c1763de0f2a8c22cc3e959b654c8790622b6ef6a" - integrity sha512-w9JfSkvpqqlix9UjDvJjm1EjSt652zVQ6iwCIj1cVVkwXf4jQhQgTNXY6EVTwuAmUjg6BC6k9RHCBynoLFo3IQ== - dependencies: - whatwg-url-without-unicode "8.0.0-3" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -1212,19 +1137,6 @@ rpc-websockets@^7.4.2: bufferutil "^4.0.1" utf-8-validate "^5.0.2" -rpc-websockets@^7.5.0: - version "7.5.0" - resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.0.tgz#bbeb87572e66703ff151e50af1658f98098e2748" - integrity sha512-9tIRi1uZGy7YmDjErf1Ax3wtqdSSLIlnmL5OtOzgd5eqPKbsPpwDP5whUDO2LQay3Xp0CcHlcNSGzacNRluBaQ== - dependencies: - "@babel/runtime" "^7.17.2" - eventemitter3 "^4.0.7" - uuid "^8.3.2" - ws "^8.5.0" - optionalDependencies: - bufferutil "^4.0.1" - utf-8-validate "^5.0.2" - safe-buffer@^5.0.1, safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -1355,7 +1267,7 @@ toml@^3.0.0: tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= traverse-chain@~0.1.0: version "0.1.0" @@ -1432,7 +1344,7 @@ uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.0, uuid@^8.3.2: +uuid@^8.3.0: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -1440,26 +1352,12 @@ uuid@^8.3.0, uuid@^8.3.2: webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -whatwg-url-without-unicode@8.0.0-3: - version "8.0.0-3" - resolved "https://registry.yarnpkg.com/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz#ab6df4bf6caaa6c85a59f6e82c026151d4bb376b" - integrity sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig== - dependencies: - buffer "^5.4.3" - punycode "^2.1.1" - webidl-conversions "^5.0.0" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" @@ -1500,11 +1398,6 @@ ws@^7.4.5: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== -ws@^8.5.0: - version "8.8.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" - integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== - y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" From e0568a04224f6903fd6a9b18bb80be4e51af6748 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 13:10:48 -0400 Subject: [PATCH 041/109] update optional test --- tests/optional/tests/optional.ts | 123 ++++++++++++++++--------------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index 618b2affcf..97bce1008d 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -1,69 +1,72 @@ import * as anchor from "@project-serum/anchor"; -import { Program } from "@project-serum/anchor"; -import { Optional, IDL } from "../target/types/optional"; -import { expect } from "chai"; -import { SystemProgram } from "@solana/web3.js"; +import {Program} from "@project-serum/anchor"; +import {Optional} from "../target/types/optional"; +import {expect} from "chai"; +import {SystemProgram} from "@solana/web3.js"; describe("Optional", () => { - // configure the client to use the local cluster - anchor.setProvider(anchor.AnchorProvider.env()); + // configure the client to use the local cluster + anchor.setProvider(anchor.AnchorProvider.env()); + let optional1 = anchor.web3.Keypair.generate(); + let optional2 = anchor.web3.Keypair.generate(); - // let signer = anchor.web3.Keypair.generate(); - const optional1 = anchor.web3.Keypair.generate(); - const optional2 = anchor.web3.Keypair.generate(); + // optional program + const program = anchor.workspace.Optional as Program; + // payer of the transactions + const payer = (program.provider as anchor.AnchorProvider).wallet; - // optional program - const program = anchor.workspace.Optional as Program; - // payer of the transactions - const payer = (program.provider as anchor.AnchorProvider).wallet; + it("initialize", async () => { + await program.methods + .initialize(new anchor.BN(10), optional2.publicKey) + .accounts({ + payer: payer.publicKey, + systemProgram: SystemProgram.programId, + optional2: optional2.publicKey, + }) + .signers([optional2]).rpc(); - it("initialize", async () => { - console.log(); - let txn = await program.methods - .initialize(new anchor.BN(10), optional1.publicKey) - .accounts({ - payer: payer.publicKey, - // optional1: optional1.publicKey, - systemProgram: SystemProgram.programId, - optional2: optional2.publicKey, - }) - // .signers([optional1, optional2]) - .signers([optional2]); - console.log( - JSON.stringify( - (await txn.transaction()).instructions.map((ix) => ix.keys), - null, - " " - ) - ); - console.log(JSON.stringify(txn._txFn)); - await txn.rpc(); + let data1 = await program.account.data1.fetchNullable(optional1.publicKey); + let data2 = await program.account.data2.fetchNullable(optional2.publicKey); - let txn2 = await program.methods - .initialize(new anchor.BN(10), optional1.publicKey) - .accounts({ - payer: payer.publicKey, - optional1: optional1.publicKey, - systemProgram: SystemProgram.programId, - // optional2: optional2.publicKey, - }) - .signers([optional1]); - console.log( - JSON.stringify( - (await txn2.transaction()).instructions.map((ix) => ix.keys), - null, - " " - ) - ); + expect(data1).to.equal(null); + expect(data2.optional1.toString()).to.equal(optional2.publicKey.toString()); - // .signers([optional1, optional2]) - // .signers([optional2]) - // let data1 = await program.account.data1.fetch(optional1.publicKey); - // console.log(data1) - let data2 = await program.account.data2.fetch(optional2.publicKey); - console.log(data2); - // - // // expect(data1.data.toNumber()).to.equal(10); - expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); - }); + + optional1 = anchor.web3.Keypair.generate(); + optional2 = anchor.web3.Keypair.generate(); + await program.methods + .initialize(new anchor.BN(10), optional2.publicKey) + .accounts({ + payer: payer.publicKey, + optional1: optional1.publicKey, + systemProgram: SystemProgram.programId, + }) + .signers([optional1]).rpc() + + data1 = await program.account.data1.fetchNullable(optional1.publicKey); + data2 = await program.account.data2.fetchNullable(optional2.publicKey); + + expect(data2).to.equal(null); + expect(data1.data.toNumber()).to.equal(10); + + + optional1 = anchor.web3.Keypair.generate(); + optional2 = anchor.web3.Keypair.generate(); + await program.methods + .initialize(new anchor.BN(10), optional2.publicKey) + .accounts({ + payer: payer.publicKey, + optional1: optional1.publicKey, + optional2: optional2.publicKey, + systemProgram: SystemProgram.programId, + }) + .signers([optional1]).rpc() + + data1 = await program.account.data1.fetchNullable(optional1.publicKey); + data2 = await program.account.data2.fetchNullable(optional2.publicKey); + + expect(data1.data.toNumber()).to.equal(10); + expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); + + }); }); From 4cf4768408ae1a8167ff93f6fd4e4c49d7146c45 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 13:34:29 -0400 Subject: [PATCH 042/109] update optional test --- tests/optional/tests/optional.ts | 51 +++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index 97bce1008d..06af85b0f9 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -1,7 +1,7 @@ import * as anchor from "@project-serum/anchor"; -import {Program} from "@project-serum/anchor"; +import {AnchorError, Program} from "@project-serum/anchor"; import {Optional} from "../target/types/optional"; -import {expect} from "chai"; +import {expect, assert} from "chai"; import {SystemProgram} from "@solana/web3.js"; describe("Optional", () => { @@ -33,7 +33,6 @@ describe("Optional", () => { optional1 = anchor.web3.Keypair.generate(); - optional2 = anchor.web3.Keypair.generate(); await program.methods .initialize(new anchor.BN(10), optional2.publicKey) .accounts({ @@ -44,11 +43,31 @@ describe("Optional", () => { .signers([optional1]).rpc() data1 = await program.account.data1.fetchNullable(optional1.publicKey); - data2 = await program.account.data2.fetchNullable(optional2.publicKey); - expect(data2).to.equal(null); expect(data1.data.toNumber()).to.equal(10); + }); + it("realloc_with_constraints", async () => { + try { + await program.methods + .realloc() + .accounts({ + payer: payer.publicKey, + optional1: optional1.publicKey, + optional2: optional2.publicKey, + systemProgram: SystemProgram.programId + }) + .rpc(); + + assert.ok(false); + } catch (e) { + assert.isTrue(e instanceof AnchorError); + const err: AnchorError = e; + const errMsg = + "A has one constraint was violated"; + assert.strictEqual(err.error.errorMessage, errMsg); + assert.strictEqual(err.error.errorCode.number,2001); + } optional1 = anchor.web3.Keypair.generate(); optional2 = anchor.web3.Keypair.generate(); @@ -60,13 +79,29 @@ describe("Optional", () => { optional2: optional2.publicKey, systemProgram: SystemProgram.programId, }) - .signers([optional1]).rpc() + .signers([optional1, optional2]).rpc() + + let data1 = await program.account.data1.fetchNullable(optional1.publicKey); + let data2 = await program.account.data2.fetchNullable(optional2.publicKey); + let data1_info = await program.account.data1.getAccountInfo(optional1.publicKey); - data1 = await program.account.data1.fetchNullable(optional1.publicKey); - data2 = await program.account.data2.fetchNullable(optional2.publicKey); expect(data1.data.toNumber()).to.equal(10); expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); + expect(data1_info.data.length).to.equal(16); + + + await program.methods + .realloc() + .accounts({ + payer: payer.publicKey, + optional1: optional1.publicKey, + optional2: optional2.publicKey, + systemProgram: SystemProgram.programId + }) + .rpc(); + data1_info = await program.account.data1.getAccountInfo(optional1.publicKey); + expect(data1_info.data.length).to.equal(20); }); }); From 8979511233c422ec099e4ed2f99663574f2b513c Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 13:57:28 -0400 Subject: [PATCH 043/109] update optional rust cli test --- client/example/run-test.sh | 5 ++++- client/example/src/main.rs | 22 +++++++--------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/client/example/run-test.sh b/client/example/run-test.sh index 2835d78d8e..197d6b1a6e 100755 --- a/client/example/run-test.sh +++ b/client/example/run-test.sh @@ -26,6 +26,7 @@ main() { local basic_2_pid="Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" local basic_4_pid="CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr" local events_pid="2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy" + local optional_pid="FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG" # # Bootup validator. @@ -35,13 +36,15 @@ main() { --bpf-program $basic_2_pid ../../examples/tutorial/basic-2/target/deploy/basic_2.so \ --bpf-program $basic_4_pid ../../examples/tutorial/basic-4/target/deploy/basic_4.so \ --bpf-program $events_pid ../../tests/events/target/deploy/events.so \ + --bpf-program $optional_pid ../../tests/optional/target/deploy/optioal.so \ + > test-validator.log & sleep 5 # # Run Test. # - cargo run -- --composite-pid $composite_pid --basic-2-pid $basic_2_pid --basic-4-pid $basic_4_pid --events-pid $events_pid + cargo run -- --composite-pid $composite_pid --basic-2-pid $basic_2_pid --basic-4-pid $basic_4_pid --events-pid $events_pid --optional_pid $optional_pid } cleanup() { diff --git a/client/example/src/main.rs b/client/example/src/main.rs index 26fdbdd781..e34d81872e 100644 --- a/client/example/src/main.rs +++ b/client/example/src/main.rs @@ -62,10 +62,10 @@ fn main() -> Result<()> { let client = Client::new_with_options(url, Rc::new(payer), CommitmentConfig::processed()); // Run tests. - // composite(&client, opts.composite_pid)?; - // basic_2(&client, opts.basic_2_pid)?; - // basic_4(&client, opts.basic_4_pid)?; - // events(&client, opts.events_pid)?; + composite(&client, opts.composite_pid)?; + basic_2(&client, opts.basic_2_pid)?; + basic_4(&client, opts.basic_4_pid)?; + events(&client, opts.events_pid)?; optional(&client, opts.optional_pid, payer_clone)?; // Success. @@ -253,11 +253,9 @@ fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { optional2: None, system_program: Some(system_program::ID), }; - let metas = initialize.to_account_metas(None); - println!("{metas:#?}"); // Build and send a transaction. - let builder = program + program .request() .signer(&optional1) .signer(&signer) @@ -271,17 +269,11 @@ fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { .args(optional_instruction::Initialize { value: 10, key: optional1.pubkey(), - }); - - let txn = builder.transaction()?; - println!("{txn:#?}"); - - let sent = builder.send()?; + }) + .send()?; // Assert the transaction worked. let optional1_account: Data1 = program.account(optional1.pubkey())?; - // let optional2_account: Result = program.account(optional2.pubkey()); - assert_eq!(optional1_account.data, 10); println!("Optional success!"); From fe8ca60b9bea69bcb2383bb013414daeda11cea6 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 5 Aug 2022 15:52:18 -0400 Subject: [PATCH 044/109] fix linting and tests --- client/example/run-test.sh | 2 +- tests/optional/tests/optional.ts | 209 ++++++++++++++++--------------- 2 files changed, 107 insertions(+), 104 deletions(-) diff --git a/client/example/run-test.sh b/client/example/run-test.sh index 197d6b1a6e..c0cde1fb3e 100755 --- a/client/example/run-test.sh +++ b/client/example/run-test.sh @@ -36,7 +36,7 @@ main() { --bpf-program $basic_2_pid ../../examples/tutorial/basic-2/target/deploy/basic_2.so \ --bpf-program $basic_4_pid ../../examples/tutorial/basic-4/target/deploy/basic_4.so \ --bpf-program $events_pid ../../tests/events/target/deploy/events.so \ - --bpf-program $optional_pid ../../tests/optional/target/deploy/optioal.so \ + --bpf-program $optional_pid ../../tests/optional/target/deploy/optional.so \ > test-validator.log & sleep 5 diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index 06af85b0f9..495fa44547 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -1,107 +1,110 @@ import * as anchor from "@project-serum/anchor"; -import {AnchorError, Program} from "@project-serum/anchor"; -import {Optional} from "../target/types/optional"; -import {expect, assert} from "chai"; -import {SystemProgram} from "@solana/web3.js"; +import { AnchorError, Program } from "@project-serum/anchor"; +import { Optional } from "../target/types/optional"; +import { expect, assert } from "chai"; +import { SystemProgram } from "@solana/web3.js"; describe("Optional", () => { - // configure the client to use the local cluster - anchor.setProvider(anchor.AnchorProvider.env()); - let optional1 = anchor.web3.Keypair.generate(); - let optional2 = anchor.web3.Keypair.generate(); - - // optional program - const program = anchor.workspace.Optional as Program; - // payer of the transactions - const payer = (program.provider as anchor.AnchorProvider).wallet; - - it("initialize", async () => { - await program.methods - .initialize(new anchor.BN(10), optional2.publicKey) - .accounts({ - payer: payer.publicKey, - systemProgram: SystemProgram.programId, - optional2: optional2.publicKey, - }) - .signers([optional2]).rpc(); - - let data1 = await program.account.data1.fetchNullable(optional1.publicKey); - let data2 = await program.account.data2.fetchNullable(optional2.publicKey); - - expect(data1).to.equal(null); - expect(data2.optional1.toString()).to.equal(optional2.publicKey.toString()); - - - optional1 = anchor.web3.Keypair.generate(); - await program.methods - .initialize(new anchor.BN(10), optional2.publicKey) - .accounts({ - payer: payer.publicKey, - optional1: optional1.publicKey, - systemProgram: SystemProgram.programId, - }) - .signers([optional1]).rpc() - - data1 = await program.account.data1.fetchNullable(optional1.publicKey); - - expect(data1.data.toNumber()).to.equal(10); - }); - - it("realloc_with_constraints", async () => { - try { - await program.methods - .realloc() - .accounts({ - payer: payer.publicKey, - optional1: optional1.publicKey, - optional2: optional2.publicKey, - systemProgram: SystemProgram.programId - }) - .rpc(); - - assert.ok(false); - } catch (e) { - assert.isTrue(e instanceof AnchorError); - const err: AnchorError = e; - const errMsg = - "A has one constraint was violated"; - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number,2001); - } - - optional1 = anchor.web3.Keypair.generate(); - optional2 = anchor.web3.Keypair.generate(); - await program.methods - .initialize(new anchor.BN(10), optional2.publicKey) - .accounts({ - payer: payer.publicKey, - optional1: optional1.publicKey, - optional2: optional2.publicKey, - systemProgram: SystemProgram.programId, - }) - .signers([optional1, optional2]).rpc() - - let data1 = await program.account.data1.fetchNullable(optional1.publicKey); - let data2 = await program.account.data2.fetchNullable(optional2.publicKey); - let data1_info = await program.account.data1.getAccountInfo(optional1.publicKey); - - - expect(data1.data.toNumber()).to.equal(10); - expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); - expect(data1_info.data.length).to.equal(16); - - - await program.methods - .realloc() - .accounts({ - payer: payer.publicKey, - optional1: optional1.publicKey, - optional2: optional2.publicKey, - systemProgram: SystemProgram.programId - }) - .rpc(); - - data1_info = await program.account.data1.getAccountInfo(optional1.publicKey); - expect(data1_info.data.length).to.equal(20); - }); + // configure the client to use the local cluster + anchor.setProvider(anchor.AnchorProvider.env()); + let optional1 = anchor.web3.Keypair.generate(); + let optional2 = anchor.web3.Keypair.generate(); + + // optional program + const program = anchor.workspace.Optional as Program; + // payer of the transactions + const payer = (program.provider as anchor.AnchorProvider).wallet; + + it("initialize", async () => { + await program.methods + .initialize(new anchor.BN(10), optional2.publicKey) + .accounts({ + payer: payer.publicKey, + systemProgram: SystemProgram.programId, + optional2: optional2.publicKey, + }) + .signers([optional2]) + .rpc(); + + let data1 = await program.account.data1.fetchNullable(optional1.publicKey); + let data2 = await program.account.data2.fetchNullable(optional2.publicKey); + + expect(data1).to.equal(null); + expect(data2.optional1.toString()).to.equal(optional2.publicKey.toString()); + + optional1 = anchor.web3.Keypair.generate(); + await program.methods + .initialize(new anchor.BN(10), optional2.publicKey) + .accounts({ + payer: payer.publicKey, + optional1: optional1.publicKey, + systemProgram: SystemProgram.programId, + }) + .signers([optional1]) + .rpc(); + + data1 = await program.account.data1.fetchNullable(optional1.publicKey); + + expect(data1.data.toNumber()).to.equal(10); + }); + + it("realloc_with_constraints", async () => { + try { + await program.methods + .realloc() + .accounts({ + payer: payer.publicKey, + optional1: optional1.publicKey, + optional2: optional2.publicKey, + systemProgram: SystemProgram.programId, + }) + .rpc(); + + assert.ok(false); + } catch (e) { + assert.isTrue(e instanceof AnchorError); + const err: AnchorError = e; + const errMsg = "A has one constraint was violated"; + assert.strictEqual(err.error.errorMessage, errMsg); + assert.strictEqual(err.error.errorCode.number, 2001); + } + + optional1 = anchor.web3.Keypair.generate(); + optional2 = anchor.web3.Keypair.generate(); + await program.methods + .initialize(new anchor.BN(10), optional2.publicKey) + .accounts({ + payer: payer.publicKey, + optional1: optional1.publicKey, + optional2: optional2.publicKey, + systemProgram: SystemProgram.programId, + }) + .signers([optional1, optional2]) + .rpc(); + + let data1 = await program.account.data1.fetchNullable(optional1.publicKey); + let data2 = await program.account.data2.fetchNullable(optional2.publicKey); + let data1_info = await program.account.data1.getAccountInfo( + optional1.publicKey + ); + + expect(data1.data.toNumber()).to.equal(10); + expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); + expect(data1_info.data.length).to.equal(16); + + await program.methods + .realloc() + .accounts({ + payer: payer.publicKey, + optional1: optional1.publicKey, + optional2: optional2.publicKey, + systemProgram: SystemProgram.programId, + }) + .rpc(); + + data1_info = await program.account.data1.getAccountInfo( + optional1.publicKey + ); + expect(data1_info.data.length).to.equal(20); + }); }); From 668dfc95bd0c1259988102441807ee0b5fa78178 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sat, 6 Aug 2022 20:02:14 -0400 Subject: [PATCH 045/109] fix tests --- .github/workflows/no-cashing-tests.yaml | 6 ++++++ .github/workflows/tests.yaml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/.github/workflows/no-cashing-tests.yaml b/.github/workflows/no-cashing-tests.yaml index 07dad8bccc..78e1b7bdd5 100644 --- a/.github/workflows/no-cashing-tests.yaml +++ b/.github/workflows/no-cashing-tests.yaml @@ -79,6 +79,8 @@ jobs: fail-fast: false matrix: node: + - path: tests/optional/ + name: optional.so - path: tests/events/ name: events.so - path: examples/tutorial/basic-4/ @@ -121,6 +123,10 @@ jobs: path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/anchor + - uses: actions/download-artifact@v2 + with: + name: optional.so + path: tests/optional/target/deploy/ - uses: actions/download-artifact@v2 with: name: events.so diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 842d97be1b..3dce20fd95 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -134,6 +134,8 @@ jobs: fail-fast: false matrix: node: + - path: tests/optional/ + name: optional.so - path: tests/events/ name: events.so - path: examples/tutorial/basic-4/ @@ -176,6 +178,10 @@ jobs: path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/anchor + - uses: actions/download-artifact@v2 + with: + name: optional.so + path: tests/optional/target/deploy/ - uses: actions/download-artifact@v2 with: name: events.so From 624212ec5610978014c54ae5a48e35c6cae76763 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 22 Aug 2022 15:02:38 -0400 Subject: [PATCH 046/109] update try_accounts to pass in accs during constraint gen --- lang/syn/src/codegen/accounts/try_accounts.rs | 5 ++--- tests/cashiers-check/Anchor.toml | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lang/syn/src/codegen/accounts/try_accounts.rs b/lang/syn/src/codegen/accounts/try_accounts.rs index aeb527e954..f90e83bff4 100644 --- a/lang/syn/src/codegen/accounts/try_accounts.rs +++ b/lang/syn/src/codegen/accounts/try_accounts.rs @@ -134,7 +134,6 @@ pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream { // Deserialization for each pda init field. This must be after // the inital extraction from the accounts slice and before access_checks. - let has_optional = accs.has_optional(); let init_fields: Vec = accs .fields .iter() @@ -145,14 +144,14 @@ pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream { true => Some(f), }, }) - .map(|f| constraints::generate(f, has_optional)) + .map(|f| constraints::generate(f, accs)) .collect(); // Constraint checks for each account fields. let access_checks: Vec = non_init_fields .iter() .map(|af: &&AccountField| match af { - AccountField::Field(f) => constraints::generate(f, has_optional), + AccountField::Field(f) => constraints::generate(f, accs), AccountField::CompositeField(s) => constraints::generate_composite(s), }) .collect(); diff --git a/tests/cashiers-check/Anchor.toml b/tests/cashiers-check/Anchor.toml index 5a733e1a35..8a6f8e3c06 100644 --- a/tests/cashiers-check/Anchor.toml +++ b/tests/cashiers-check/Anchor.toml @@ -9,3 +9,6 @@ cashiers_check = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" test = "yarn run mocha -t 1000000 tests/" [features] + +[test.validator] +limit_ledger_size = "100000" \ No newline at end of file From 39db4e999809978afc1993fd7e147a49be486043 Mon Sep 17 00:00:00 2001 From: febo Date: Tue, 23 Aug 2022 00:31:59 +0100 Subject: [PATCH 047/109] Add default impl for TryToAccountInfos --- lang/src/accounts/account.rs | 3 --- lang/src/accounts/account_info.rs | 6 +----- lang/src/accounts/account_loader.rs | 6 +----- lang/src/accounts/boxed.rs | 6 +----- lang/src/accounts/cpi_account.rs | 6 +----- lang/src/accounts/cpi_state.rs | 3 --- lang/src/accounts/loader.rs | 6 +----- lang/src/accounts/program.rs | 6 +----- lang/src/accounts/program_account.rs | 3 --- lang/src/accounts/signer.rs | 6 +----- lang/src/accounts/state.rs | 3 --- lang/src/accounts/system_account.rs | 6 +----- lang/src/accounts/sysvar.rs | 6 +----- lang/src/accounts/unchecked_account.rs | 6 +----- lang/src/lib.rs | 6 ++++-- 15 files changed, 14 insertions(+), 64 deletions(-) diff --git a/lang/src/accounts/account.rs b/lang/src/accounts/account.rs index f5c5681bbc..cdc64daf16 100644 --- a/lang/src/accounts/account.rs +++ b/lang/src/accounts/account.rs @@ -387,9 +387,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountI impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> TryToAccountInfos<'info> for Account<'info, T> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } } impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AsRef> diff --git a/lang/src/accounts/account_info.rs b/lang/src/accounts/account_info.rs index dbb815b902..204dacc3a3 100644 --- a/lang/src/accounts/account_info.rs +++ b/lang/src/accounts/account_info.rs @@ -46,11 +46,7 @@ impl<'info> ToAccountInfos<'info> for AccountInfo<'info> { } } -impl<'info> TryToAccountInfos<'info> for AccountInfo<'info> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } -} +impl<'info> TryToAccountInfos<'info> for AccountInfo<'info> {} impl<'info> TryToAccountInfo<'info> for AccountInfo<'info> { fn try_to_account_info(&self) -> Result> { diff --git a/lang/src/accounts/account_loader.rs b/lang/src/accounts/account_loader.rs index 595b4a0070..9d7be060aa 100644 --- a/lang/src/accounts/account_loader.rs +++ b/lang/src/accounts/account_loader.rs @@ -280,11 +280,7 @@ impl<'info, T: ZeroCopy + Owner> ToAccountInfos<'info> for AccountLoader<'info, } } -impl<'info, T: ZeroCopy + Owner> TryToAccountInfos<'info> for AccountLoader<'info, T> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } -} +impl<'info, T: ZeroCopy + Owner> TryToAccountInfos<'info> for AccountLoader<'info, T> {} impl<'info, T: ZeroCopy + Owner> TryToAccountInfo<'info> for AccountLoader<'info, T> { fn try_to_account_info(&self) -> Result> { diff --git a/lang/src/accounts/boxed.rs b/lang/src/accounts/boxed.rs index 96e8f61acd..741a6f13ed 100644 --- a/lang/src/accounts/boxed.rs +++ b/lang/src/accounts/boxed.rs @@ -47,11 +47,7 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Box { } } -impl<'info, T: ToAccountInfos<'info>> TryToAccountInfos<'info> for Box { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } -} +impl<'info, T: ToAccountInfos<'info>> TryToAccountInfos<'info> for Box {} impl ToAccountMetas for Box { fn to_account_metas(&self, is_signer: Option) -> Vec { diff --git a/lang/src/accounts/cpi_account.rs b/lang/src/accounts/cpi_account.rs index aa1de88975..4a1bf57530 100644 --- a/lang/src/accounts/cpi_account.rs +++ b/lang/src/accounts/cpi_account.rs @@ -86,11 +86,7 @@ impl<'info, T: AccountDeserialize + Clone> ToAccountInfos<'info> for CpiAccount< } #[allow(deprecated)] -impl<'info, T: AccountDeserialize + Clone> TryToAccountInfos<'info> for CpiAccount<'info, T> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } -} +impl<'info, T: AccountDeserialize + Clone> TryToAccountInfos<'info> for CpiAccount<'info, T> {} impl<'info, T: AccountDeserialize + Clone> TryToAccountInfo<'info> for CpiAccount<'info, T> { fn try_to_account_info(&self) -> Result> { diff --git a/lang/src/accounts/cpi_state.rs b/lang/src/accounts/cpi_state.rs index d6c9bbf037..969bf3d7f4 100644 --- a/lang/src/accounts/cpi_state.rs +++ b/lang/src/accounts/cpi_state.rs @@ -114,9 +114,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfos<'info> for CpiState<'info, T> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } } impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfo<'info> diff --git a/lang/src/accounts/loader.rs b/lang/src/accounts/loader.rs index ceb6e80f8a..8d935cae9a 100644 --- a/lang/src/accounts/loader.rs +++ b/lang/src/accounts/loader.rs @@ -228,11 +228,7 @@ impl<'info, T: ZeroCopy> ToAccountInfos<'info> for Loader<'info, T> { } #[allow(deprecated)] -impl<'info, T: ZeroCopy> TryToAccountInfos<'info> for Loader<'info, T> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } -} +impl<'info, T: ZeroCopy> TryToAccountInfos<'info> for Loader<'info, T> {} impl<'info, T: ZeroCopy> TryToAccountInfo<'info> for Loader<'info, T> { fn try_to_account_info(&self) -> Result> { diff --git a/lang/src/accounts/program.rs b/lang/src/accounts/program.rs index b2942488eb..2331c4b690 100644 --- a/lang/src/accounts/program.rs +++ b/lang/src/accounts/program.rs @@ -176,11 +176,7 @@ impl<'info, T: Id + Clone> ToAccountInfos<'info> for Program<'info, T> { } } -impl<'info, T: Id + Clone> TryToAccountInfos<'info> for Program<'info, T> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } -} +impl<'info, T: Id + Clone> TryToAccountInfos<'info> for Program<'info, T> {} impl<'info, T: Id + Clone> TryToAccountInfo<'info> for Program<'info, T> { fn try_to_account_info(&self) -> Result> { diff --git a/lang/src/accounts/program_account.rs b/lang/src/accounts/program_account.rs index 92a4e856c0..08ea8518db 100644 --- a/lang/src/accounts/program_account.rs +++ b/lang/src/accounts/program_account.rs @@ -151,9 +151,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfos<'info> for ProgramAccount<'info, T> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } } #[allow(deprecated)] diff --git a/lang/src/accounts/signer.rs b/lang/src/accounts/signer.rs index 6fba994f80..536e291d11 100644 --- a/lang/src/accounts/signer.rs +++ b/lang/src/accounts/signer.rs @@ -94,11 +94,7 @@ impl<'info> ToAccountInfos<'info> for Signer<'info> { } } -impl<'info> TryToAccountInfos<'info> for Signer<'info> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } -} +impl<'info> TryToAccountInfos<'info> for Signer<'info> {} impl<'info> TryToAccountInfo<'info> for Signer<'info> { fn try_to_account_info(&self) -> Result> { diff --git a/lang/src/accounts/state.rs b/lang/src/accounts/state.rs index fab2a0616e..5608d3ce21 100644 --- a/lang/src/accounts/state.rs +++ b/lang/src/accounts/state.rs @@ -112,9 +112,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfos<'info> for ProgramState<'info, T> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } } impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfo<'info> diff --git a/lang/src/accounts/system_account.rs b/lang/src/accounts/system_account.rs index 3086e6e02b..9d567d9c87 100644 --- a/lang/src/accounts/system_account.rs +++ b/lang/src/accounts/system_account.rs @@ -70,11 +70,7 @@ impl<'info> ToAccountInfos<'info> for SystemAccount<'info> { } } -impl<'info> TryToAccountInfos<'info> for SystemAccount<'info> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } -} +impl<'info> TryToAccountInfos<'info> for SystemAccount<'info> {} impl<'info> TryToAccountInfo<'info> for SystemAccount<'info> { fn try_to_account_info(&self) -> Result> { diff --git a/lang/src/accounts/sysvar.rs b/lang/src/accounts/sysvar.rs index 73a2e368d9..c47e7f6aea 100644 --- a/lang/src/accounts/sysvar.rs +++ b/lang/src/accounts/sysvar.rs @@ -97,11 +97,7 @@ impl<'info, T: solana_program::sysvar::Sysvar> ToAccountInfos<'info> for Sysvar< } } -impl<'info, T: solana_program::sysvar::Sysvar> TryToAccountInfos<'info> for Sysvar<'info, T> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } -} +impl<'info, T: solana_program::sysvar::Sysvar> TryToAccountInfos<'info> for Sysvar<'info, T> {} impl<'info, T: solana_program::sysvar::Sysvar> TryToAccountInfo<'info> for Sysvar<'info, T> { fn try_to_account_info(&self) -> Result> { diff --git a/lang/src/accounts/unchecked_account.rs b/lang/src/accounts/unchecked_account.rs index d488674673..286906e726 100644 --- a/lang/src/accounts/unchecked_account.rs +++ b/lang/src/accounts/unchecked_account.rs @@ -57,11 +57,7 @@ impl<'info> ToAccountInfos<'info> for UncheckedAccount<'info> { } } -impl<'info> TryToAccountInfos<'info> for UncheckedAccount<'info> { - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } -} +impl<'info> TryToAccountInfos<'info> for UncheckedAccount<'info> {} impl<'info> TryToAccountInfo<'info> for UncheckedAccount<'info> { fn try_to_account_info(&self) -> Result> { diff --git a/lang/src/lib.rs b/lang/src/lib.rs index e538fcdb8b..af42db47f7 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -128,8 +128,10 @@ pub trait ToAccountInfos<'info> { /// structs. Intended for `Accounts` structs with optional accounts in order to /// pass in the program `AccountInfo` as a sentinel value. When the account /// is not present, it returns a copy of the program's `AccountInfo` instead. -pub trait TryToAccountInfos<'info> { - fn try_to_account_infos(&self, program: &AccountInfo<'info>) -> Vec>; +pub trait TryToAccountInfos<'info>: ToAccountInfos<'info> { + fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { + self.to_account_infos() + } } /// Transformation to an `AccountInfo` struct. From 39f3e456ee43445ca615794c4ec25062faba5171 Mon Sep 17 00:00:00 2001 From: febo Date: Tue, 23 Aug 2022 00:59:10 +0100 Subject: [PATCH 048/109] Removed TryToAccountInfos trait --- lang/attribute/interface/src/lib.rs | 2 +- lang/src/accounts/account.rs | 7 +--- lang/src/accounts/account_info.rs | 4 +-- lang/src/accounts/account_loader.rs | 4 +-- lang/src/accounts/boxed.rs | 4 +-- lang/src/accounts/cpi_account.rs | 3 -- lang/src/accounts/cpi_state.rs | 8 +---- lang/src/accounts/loader.rs | 5 +-- lang/src/accounts/option.rs | 4 +-- lang/src/accounts/program.rs | 4 +-- lang/src/accounts/program_account.rs | 8 +---- lang/src/accounts/signer.rs | 4 +-- lang/src/accounts/state.rs | 8 +---- lang/src/accounts/system_account.rs | 2 -- lang/src/accounts/sysvar.rs | 4 +-- lang/src/accounts/unchecked_account.rs | 4 +-- lang/src/context.rs | 23 +++--------- lang/src/lib.rs | 14 +++----- lang/src/vec.rs | 4 +-- .../codegen/accounts/__cpi_client_accounts.rs | 5 +-- lang/syn/src/codegen/accounts/mod.rs | 3 -- .../src/codegen/accounts/to_account_infos.rs | 16 +++++++++ .../codegen/accounts/try_to_account_infos.rs | 35 ------------------- 23 files changed, 42 insertions(+), 133 deletions(-) delete mode 100644 lang/syn/src/codegen/accounts/try_to_account_infos.rs diff --git a/lang/attribute/interface/src/lib.rs b/lang/attribute/interface/src/lib.rs index 0fcee2ffda..d87931816d 100644 --- a/lang/attribute/interface/src/lib.rs +++ b/lang/attribute/interface/src/lib.rs @@ -197,7 +197,7 @@ pub fn interface( 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> + anchor_lang::TryToAccountInfos<'info>>( + 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<()> { diff --git a/lang/src/accounts/account.rs b/lang/src/accounts/account.rs index cdc64daf16..7c53276ae7 100644 --- a/lang/src/accounts/account.rs +++ b/lang/src/accounts/account.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Owner, - Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, TryToAccountInfo, TryToAccountInfos, + Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -384,11 +384,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountI } } -impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> TryToAccountInfos<'info> - for Account<'info, T> -{ -} - impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AsRef> for Account<'info, T> { diff --git a/lang/src/accounts/account_info.rs b/lang/src/accounts/account_info.rs index 204dacc3a3..43abf7ba1a 100644 --- a/lang/src/accounts/account_info.rs +++ b/lang/src/accounts/account_info.rs @@ -5,7 +5,7 @@ use crate::error::ErrorCode; use crate::{ Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, - TryToAccountInfo, TryToAccountInfos, + TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -46,8 +46,6 @@ impl<'info> ToAccountInfos<'info> for AccountInfo<'info> { } } -impl<'info> TryToAccountInfos<'info> for AccountInfo<'info> {} - impl<'info> TryToAccountInfo<'info> for AccountInfo<'info> { fn try_to_account_info(&self) -> Result> { Ok(self.to_account_info()) diff --git a/lang/src/accounts/account_loader.rs b/lang/src/accounts/account_loader.rs index 9d7be060aa..5580f58466 100644 --- a/lang/src/accounts/account_loader.rs +++ b/lang/src/accounts/account_loader.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ Accounts, AccountsClose, AccountsExit, Key, Owner, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, TryToAccountInfo, TryToAccountInfos, ZeroCopy, + ToAccountMetas, TryToAccountInfo, ZeroCopy, }; use arrayref::array_ref; use solana_program::account_info::AccountInfo; @@ -280,8 +280,6 @@ impl<'info, T: ZeroCopy + Owner> ToAccountInfos<'info> for AccountLoader<'info, } } -impl<'info, T: ZeroCopy + Owner> TryToAccountInfos<'info> for AccountLoader<'info, T> {} - impl<'info, T: ZeroCopy + Owner> TryToAccountInfo<'info> for AccountLoader<'info, T> { fn try_to_account_info(&self) -> Result> { Ok(self.to_account_info()) diff --git a/lang/src/accounts/boxed.rs b/lang/src/accounts/boxed.rs index 741a6f13ed..4a683757c9 100644 --- a/lang/src/accounts/boxed.rs +++ b/lang/src/accounts/boxed.rs @@ -15,7 +15,7 @@ use crate::{ Accounts, AccountsClose, AccountsExit, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, - TryToAccountInfo, TryToAccountInfos, + TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -47,8 +47,6 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Box { } } -impl<'info, T: ToAccountInfos<'info>> TryToAccountInfos<'info> for Box {} - impl ToAccountMetas for Box { fn to_account_metas(&self, is_signer: Option) -> Vec { T::to_account_metas(self, is_signer) diff --git a/lang/src/accounts/cpi_account.rs b/lang/src/accounts/cpi_account.rs index 4a1bf57530..f041489391 100644 --- a/lang/src/accounts/cpi_account.rs +++ b/lang/src/accounts/cpi_account.rs @@ -85,9 +85,6 @@ impl<'info, T: AccountDeserialize + Clone> ToAccountInfos<'info> for CpiAccount< } } -#[allow(deprecated)] -impl<'info, T: AccountDeserialize + Clone> TryToAccountInfos<'info> for CpiAccount<'info, T> {} - impl<'info, T: AccountDeserialize + Clone> TryToAccountInfo<'info> for CpiAccount<'info, T> { fn try_to_account_info(&self) -> Result> { Ok(self.to_account_info()) diff --git a/lang/src/accounts/cpi_state.rs b/lang/src/accounts/cpi_state.rs index 969bf3d7f4..460cee324d 100644 --- a/lang/src/accounts/cpi_state.rs +++ b/lang/src/accounts/cpi_state.rs @@ -3,7 +3,7 @@ use crate::error::ErrorCode; use crate::{accounts::state::ProgramState, context::CpiStateContext}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfo, - ToAccountInfos, ToAccountMetas, TryToAccountInfo, TryToAccountInfos, + ToAccountInfos, ToAccountMetas, TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -110,12 +110,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfos<'info> - for CpiState<'info, T> -{ -} - impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfo<'info> for CpiState<'info, T> { diff --git a/lang/src/accounts/loader.rs b/lang/src/accounts/loader.rs index 8d935cae9a..ba52b77d79 100644 --- a/lang/src/accounts/loader.rs +++ b/lang/src/accounts/loader.rs @@ -2,7 +2,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ Accounts, AccountsClose, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, TryToAccountInfo, TryToAccountInfos, ZeroCopy, + ToAccountMetas, TryToAccountInfo, ZeroCopy, }; use arrayref::array_ref; use solana_program::account_info::AccountInfo; @@ -227,9 +227,6 @@ impl<'info, T: ZeroCopy> ToAccountInfos<'info> for Loader<'info, T> { } } -#[allow(deprecated)] -impl<'info, T: ZeroCopy> TryToAccountInfos<'info> for Loader<'info, T> {} - impl<'info, T: ZeroCopy> TryToAccountInfo<'info> for Loader<'info, T> { fn try_to_account_info(&self) -> Result> { Ok(self.to_account_info()) diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index 67f17eaa80..1aa7ef45b6 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -16,7 +16,7 @@ use solana_program::pubkey::Pubkey; use crate::{ error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, TryKey, TryToAccountInfo, TryToAccountInfos, + ToAccountMetas, TryKey, TryToAccountInfo, }; impl<'info, T: Accounts<'info>> Accounts<'info> for Option { @@ -47,9 +47,7 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option { None => panic!("Cannot run `to_account_infos` on None"), } } -} -impl<'info, T: ToAccountInfos<'info>> TryToAccountInfos<'info> for Option { fn try_to_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { match self { Some(_) => self.to_account_infos(), diff --git a/lang/src/accounts/program.rs b/lang/src/accounts/program.rs index 2331c4b690..093d73a362 100644 --- a/lang/src/accounts/program.rs +++ b/lang/src/accounts/program.rs @@ -3,7 +3,7 @@ use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, Accounts, AccountsExit, Id, Key, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, TryToAccountInfo, TryToAccountInfos, + ToAccountMetas, TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState}; @@ -176,8 +176,6 @@ impl<'info, T: Id + Clone> ToAccountInfos<'info> for Program<'info, T> { } } -impl<'info, T: Id + Clone> TryToAccountInfos<'info> for Program<'info, T> {} - impl<'info, T: Id + Clone> TryToAccountInfo<'info> for Program<'info, T> { fn try_to_account_info(&self) -> Result> { Ok(self.to_account_info()) diff --git a/lang/src/accounts/program_account.rs b/lang/src/accounts/program_account.rs index 08ea8518db..174d6b0dd0 100644 --- a/lang/src/accounts/program_account.rs +++ b/lang/src/accounts/program_account.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Result, - ToAccountInfo, ToAccountInfos, ToAccountMetas, TryToAccountInfo, TryToAccountInfos, + ToAccountInfo, ToAccountInfos, ToAccountMetas, TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -147,12 +147,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfos<'info> - for ProgramAccount<'info, T> -{ -} - #[allow(deprecated)] impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfo<'info> for ProgramAccount<'info, T> diff --git a/lang/src/accounts/signer.rs b/lang/src/accounts/signer.rs index 536e291d11..62d1d27e7f 100644 --- a/lang/src/accounts/signer.rs +++ b/lang/src/accounts/signer.rs @@ -2,7 +2,7 @@ use crate::error::ErrorCode; use crate::{ Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, - TryToAccountInfo, TryToAccountInfos, + TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -94,8 +94,6 @@ impl<'info> ToAccountInfos<'info> for Signer<'info> { } } -impl<'info> TryToAccountInfos<'info> for Signer<'info> {} - impl<'info> TryToAccountInfo<'info> for Signer<'info> { fn try_to_account_info(&self) -> Result> { Ok(self.to_account_info()) diff --git a/lang/src/accounts/state.rs b/lang/src/accounts/state.rs index 5608d3ce21..ac725fb55c 100644 --- a/lang/src/accounts/state.rs +++ b/lang/src/accounts/state.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfo, - ToAccountInfos, ToAccountMetas, TryToAccountInfo, TryToAccountInfos, + ToAccountInfos, ToAccountMetas, TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -108,12 +108,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfos<'info> - for ProgramState<'info, T> -{ -} - impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfo<'info> for ProgramState<'info, T> { diff --git a/lang/src/accounts/system_account.rs b/lang/src/accounts/system_account.rs index 9d567d9c87..832a0bd101 100644 --- a/lang/src/accounts/system_account.rs +++ b/lang/src/accounts/system_account.rs @@ -70,8 +70,6 @@ impl<'info> ToAccountInfos<'info> for SystemAccount<'info> { } } -impl<'info> TryToAccountInfos<'info> for SystemAccount<'info> {} - impl<'info> TryToAccountInfo<'info> for SystemAccount<'info> { fn try_to_account_info(&self) -> Result> { Ok(self.to_account_info()) diff --git a/lang/src/accounts/sysvar.rs b/lang/src/accounts/sysvar.rs index c47e7f6aea..23ad21aea5 100644 --- a/lang/src/accounts/sysvar.rs +++ b/lang/src/accounts/sysvar.rs @@ -3,7 +3,7 @@ use crate::error::ErrorCode; use crate::{ Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, - TryToAccountInfo, TryToAccountInfos, + TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -97,8 +97,6 @@ impl<'info, T: solana_program::sysvar::Sysvar> ToAccountInfos<'info> for Sysvar< } } -impl<'info, T: solana_program::sysvar::Sysvar> TryToAccountInfos<'info> for Sysvar<'info, T> {} - impl<'info, T: solana_program::sysvar::Sysvar> TryToAccountInfo<'info> for Sysvar<'info, T> { fn try_to_account_info(&self) -> Result> { Ok(self.to_account_info()) diff --git a/lang/src/accounts/unchecked_account.rs b/lang/src/accounts/unchecked_account.rs index 286906e726..a9800ca1c5 100644 --- a/lang/src/accounts/unchecked_account.rs +++ b/lang/src/accounts/unchecked_account.rs @@ -4,7 +4,7 @@ use crate::error::ErrorCode; use crate::{ Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, - TryToAccountInfo, TryToAccountInfos, + TryToAccountInfo, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -57,8 +57,6 @@ impl<'info> ToAccountInfos<'info> for UncheckedAccount<'info> { } } -impl<'info> TryToAccountInfos<'info> for UncheckedAccount<'info> {} - impl<'info> TryToAccountInfo<'info> for UncheckedAccount<'info> { fn try_to_account_info(&self) -> Result> { Ok(self.to_account_info()) diff --git a/lang/src/context.rs b/lang/src/context.rs index 86368a2ef5..f009f1502c 100644 --- a/lang/src/context.rs +++ b/lang/src/context.rs @@ -1,6 +1,6 @@ //! Data structures that are used to provide non-argument inputs to program endpoints -use crate::{Accounts, ToAccountInfos, ToAccountMetas, TryToAccountInfos}; +use crate::{Accounts, ToAccountInfos, ToAccountMetas}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -164,7 +164,7 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> Context<'a, 'b, 'c, 'info, T> { /// ``` pub struct CpiContext<'a, 'b, 'c, 'info, T> where - T: ToAccountMetas + ToAccountInfos<'info> + TryToAccountInfos<'info>, + T: ToAccountMetas + ToAccountInfos<'info>, { pub accounts: T, pub remaining_accounts: Vec>, @@ -174,7 +174,7 @@ where impl<'a, 'b, 'c, 'info, T> CpiContext<'a, 'b, 'c, 'info, T> where - T: ToAccountMetas + ToAccountInfos<'info> + TryToAccountInfos<'info>, + T: ToAccountMetas + ToAccountInfos<'info>, { pub fn new(program: AccountInfo<'info>, accounts: T) -> Self { Self { @@ -212,7 +212,7 @@ where } } -impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryToAccountInfos<'info>> +impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountInfos<'info> for CpiContext<'_, '_, '_, 'info, T> { fn to_account_infos(&self) -> Vec> { @@ -224,15 +224,7 @@ impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryToAccountInfos<'info> } } -impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryToAccountInfos<'info>> - TryToAccountInfos<'info> for CpiContext<'_, '_, '_, 'info, T> -{ - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } -} - -impl<'info, T: ToAccountInfos<'info> + ToAccountMetas + TryToAccountInfos<'info>> ToAccountMetas +impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountMetas for CpiContext<'_, '_, '_, 'info, T> { fn to_account_metas(&self, is_signer: Option) -> Vec { @@ -331,12 +323,7 @@ impl<'a, 'b, 'c, 'info, T: Accounts<'info>> ToAccountInfos<'info> infos.push(self.cpi_ctx.program.clone()); infos } -} -#[allow(deprecated)] -impl<'a, 'b, 'c, 'info, T: Accounts<'info>> TryToAccountInfos<'info> - for CpiStateContext<'a, 'b, 'c, 'info, T> -{ fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { let mut infos = self.cpi_ctx.accounts.try_to_account_infos(self.program()); infos.push(self.state.clone()); diff --git a/lang/src/lib.rs b/lang/src/lib.rs index af42db47f7..8cf119640b 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -66,7 +66,7 @@ pub type Result = std::result::Result; /// cases, it's recommended to use the [`Accounts`](./derive.Accounts.html) /// derive macro to implement this trait. pub trait Accounts<'info>: - ToAccountMetas + ToAccountInfos<'info> + TryToAccountInfos<'info> + Sized + ToAccountMetas + ToAccountInfos<'info> + Sized { /// Returns the validated accounts struct. What constitutes "valid" is /// program dependent. However, users of these types should never have to @@ -121,14 +121,10 @@ pub trait ToAccountMetas { /// structs. pub trait ToAccountInfos<'info> { fn to_account_infos(&self) -> Vec>; -} -/// Transformation to -/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html) -/// structs. Intended for `Accounts` structs with optional accounts in order to -/// pass in the program `AccountInfo` as a sentinel value. When the account -/// is not present, it returns a copy of the program's `AccountInfo` instead. -pub trait TryToAccountInfos<'info>: ToAccountInfos<'info> { + /// Intended for `Accounts` structs with optional accounts in order to + /// pass in the program `AccountInfo` as a sentinel value. When the account + /// is not present, it returns a copy of the program's `AccountInfo` instead. fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { self.to_account_infos() } @@ -273,7 +269,7 @@ pub mod prelude { require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state, system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result, - ToAccountInfo, ToAccountInfos, ToAccountMetas, TryKey, TryToAccountInfo, TryToAccountInfos, + ToAccountInfo, ToAccountInfos, ToAccountMetas, TryKey, TryToAccountInfo, }; pub use anchor_attribute_error::*; pub use borsh; diff --git a/lang/src/vec.rs b/lang/src/vec.rs index 1f657340ca..d934dfdb87 100644 --- a/lang/src/vec.rs +++ b/lang/src/vec.rs @@ -1,4 +1,4 @@ -use crate::{Accounts, Result, ToAccountInfos, ToAccountMetas, TryToAccountInfos}; +use crate::{Accounts, Result, ToAccountInfos, ToAccountMetas}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -10,9 +10,7 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Vec { .flat_map(|item| item.to_account_infos()) .collect() } -} -impl<'info, T: TryToAccountInfos<'info>> TryToAccountInfos<'info> for Vec { fn try_to_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { self.iter() .flat_map(|item| item.try_to_account_infos(program)) diff --git a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs index 7b391912bd..e7195621ae 100644 --- a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs @@ -135,7 +135,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { .map(|f: &AccountField| { let name = &f.ident(); quote! { - try_account_infos.extend(anchor_lang::TryToAccountInfos::try_to_account_infos(&self.#name, program)); + try_account_infos.extend(anchor_lang::ToAccountInfos::try_to_account_infos(&self.#name, program)); } }) .collect(); @@ -213,10 +213,7 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { #(#account_struct_infos)* account_infos } - } - #[automatically_derived] - impl<'info> anchor_lang::TryToAccountInfos<'info> for #name #generics { fn try_to_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { let mut try_account_infos = vec![]; #(#account_struct_try_infos)* diff --git a/lang/syn/src/codegen/accounts/mod.rs b/lang/syn/src/codegen/accounts/mod.rs index fd06a674ec..3a239cbe33 100644 --- a/lang/syn/src/codegen/accounts/mod.rs +++ b/lang/syn/src/codegen/accounts/mod.rs @@ -12,12 +12,10 @@ mod exit; mod to_account_infos; mod to_account_metas; mod try_accounts; -mod try_to_account_infos; pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { let impl_try_accounts = try_accounts::generate(accs); let impl_to_account_infos = to_account_infos::generate(accs); - let impl_try_to_account_infos = try_to_account_infos::generate(accs); let impl_to_account_metas = to_account_metas::generate(accs); let impl_exit = exit::generate(accs); @@ -27,7 +25,6 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { quote! { #impl_try_accounts #impl_to_account_infos - #impl_try_to_account_infos #impl_to_account_metas #impl_exit diff --git a/lang/syn/src/codegen/accounts/to_account_infos.rs b/lang/syn/src/codegen/accounts/to_account_infos.rs index 938b22fc38..c50d76362a 100644 --- a/lang/syn/src/codegen/accounts/to_account_infos.rs +++ b/lang/syn/src/codegen/accounts/to_account_infos.rs @@ -20,6 +20,14 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { quote! { account_infos.extend(self.#name.to_account_infos()); } }) .collect(); + let try_acc_infos: Vec = accs + .fields + .iter() + .map(|f: &AccountField| { + let name = &f.ident(); + quote! { account_infos.extend(self.#name.try_to_account_infos(program)); } + }) + .collect(); quote! { #[automatically_derived] impl<#combined_generics> anchor_lang::ToAccountInfos<#trait_generics> for #name <#struct_generics> #where_clause{ @@ -30,6 +38,14 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { account_infos } + + fn try_to_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { + let mut account_infos = vec![]; + + #(#try_acc_infos)* + + account_infos + } } } } diff --git a/lang/syn/src/codegen/accounts/try_to_account_infos.rs b/lang/syn/src/codegen/accounts/try_to_account_infos.rs deleted file mode 100644 index 4e8be523cc..0000000000 --- a/lang/syn/src/codegen/accounts/try_to_account_infos.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::codegen::accounts::{generics, ParsedGenerics}; -use crate::{AccountField, AccountsStruct}; -use quote::quote; - -// Generates the `TryToAccountInfos` trait implementation. -pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { - let name = &accs.ident; - let ParsedGenerics { - combined_generics, - trait_generics, - struct_generics, - where_clause, - } = generics(accs); - - let try_acc_infos: Vec = accs - .fields - .iter() - .map(|f: &AccountField| { - let name = &f.ident(); - quote! { account_infos.extend(self.#name.try_to_account_infos(program)); } - }) - .collect(); - quote! { - #[automatically_derived] - impl<#combined_generics> anchor_lang::TryToAccountInfos<#trait_generics> for #name <#struct_generics> #where_clause{ - fn try_to_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { - let mut account_infos = vec![]; - - #(#try_acc_infos)* - - account_infos - } - } - } -} From a2b127ec1e8023e876d143398f6c9e0eb93935b9 Mon Sep 17 00:00:00 2001 From: febo Date: Tue, 23 Aug 2022 01:03:34 +0100 Subject: [PATCH 049/109] Formatting --- lang/src/context.rs | 4 ++-- lang/src/lib.rs | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lang/src/context.rs b/lang/src/context.rs index f009f1502c..494d3cf9cb 100644 --- a/lang/src/context.rs +++ b/lang/src/context.rs @@ -212,8 +212,8 @@ where } } -impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> - ToAccountInfos<'info> for CpiContext<'_, '_, '_, 'info, T> +impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountInfos<'info> + for CpiContext<'_, '_, '_, 'info, T> { fn to_account_infos(&self) -> Vec> { // always uses try_account_infos in case there are optional accounts diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 8cf119640b..d7c170de7f 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -65,9 +65,7 @@ pub type Result = std::result::Result; /// maintain any invariants required for the program to run securely. In most /// cases, it's recommended to use the [`Accounts`](./derive.Accounts.html) /// derive macro to implement this trait. -pub trait Accounts<'info>: - ToAccountMetas + ToAccountInfos<'info> + Sized -{ +pub trait Accounts<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized { /// Returns the validated accounts struct. What constitutes "valid" is /// program dependent. However, users of these types should never have to /// worry about account substitution attacks. For example, if a program From 2c01f385d0ac3dda2e04f9457aa10f4d5d51a615 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 22 Aug 2022 21:48:53 -0400 Subject: [PATCH 050/109] remove unneccesary traits and improve constraint gen drastically --- lang/src/accounts/account.rs | 10 +- lang/src/accounts/account_info.rs | 11 +- lang/src/accounts/account_loader.rs | 8 +- lang/src/accounts/boxed.rs | 11 +- lang/src/accounts/cpi_account.rs | 6 - lang/src/accounts/cpi_state.rs | 12 +- lang/src/accounts/loader.rs | 8 +- lang/src/accounts/option.rs | 21 +- lang/src/accounts/program.rs | 9 +- lang/src/accounts/program_account.rs | 11 +- lang/src/accounts/signer.rs | 11 +- lang/src/accounts/state.rs | 10 +- lang/src/accounts/system_account.rs | 6 - lang/src/accounts/sysvar.rs | 11 +- lang/src/accounts/unchecked_account.rs | 11 +- lang/src/ctor.rs | 2 +- lang/src/error.rs | 9 +- lang/src/lib.rs | 19 +- lang/syn/src/codegen/accounts/constraints.rs | 442 +++++++++++++------ lang/syn/src/codegen/accounts/exit.rs | 18 +- lang/syn/src/lib.rs | 23 +- tests/optional/programs/optional/src/lib.rs | 6 +- 22 files changed, 359 insertions(+), 316 deletions(-) diff --git a/lang/src/accounts/account.rs b/lang/src/accounts/account.rs index 7c53276ae7..9e53452cd6 100644 --- a/lang/src/accounts/account.rs +++ b/lang/src/accounts/account.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Owner, - Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, TryToAccountInfo, + Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -392,14 +392,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AsRef TryToAccountInfo<'info> - for Account<'info, T> -{ - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AsRef for Account<'info, T> { diff --git a/lang/src/accounts/account_info.rs b/lang/src/accounts/account_info.rs index 43abf7ba1a..54d6cc80b5 100644 --- a/lang/src/accounts/account_info.rs +++ b/lang/src/accounts/account_info.rs @@ -3,10 +3,7 @@ //! should be used instead. use crate::error::ErrorCode; -use crate::{ - Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, - TryToAccountInfo, -}; +use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -46,12 +43,6 @@ impl<'info> ToAccountInfos<'info> for AccountInfo<'info> { } } -impl<'info> TryToAccountInfo<'info> for AccountInfo<'info> { - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - impl<'info> AccountsExit<'info> for AccountInfo<'info> {} impl<'info> Key for AccountInfo<'info> { diff --git a/lang/src/accounts/account_loader.rs b/lang/src/accounts/account_loader.rs index 5580f58466..73d8c22a03 100644 --- a/lang/src/accounts/account_loader.rs +++ b/lang/src/accounts/account_loader.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ Accounts, AccountsClose, AccountsExit, Key, Owner, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, TryToAccountInfo, ZeroCopy, + ToAccountMetas, ZeroCopy, }; use arrayref::array_ref; use solana_program::account_info::AccountInfo; @@ -280,12 +280,6 @@ impl<'info, T: ZeroCopy + Owner> ToAccountInfos<'info> for AccountLoader<'info, } } -impl<'info, T: ZeroCopy + Owner> TryToAccountInfo<'info> for AccountLoader<'info, T> { - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - impl<'info, T: ZeroCopy + Owner> Key for AccountLoader<'info, T> { fn key(&self) -> Pubkey { *self.acc_info.key diff --git a/lang/src/accounts/boxed.rs b/lang/src/accounts/boxed.rs index 4a683757c9..b143024ead 100644 --- a/lang/src/accounts/boxed.rs +++ b/lang/src/accounts/boxed.rs @@ -13,10 +13,7 @@ //! } //! ``` -use crate::{ - Accounts, AccountsClose, AccountsExit, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, - TryToAccountInfo, -}; +use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -53,12 +50,6 @@ impl ToAccountMetas for Box { } } -impl<'info, T: ToAccountInfo<'info>> TryToAccountInfo<'info> for Box { - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - impl<'info, T: AccountsClose<'info>> AccountsClose<'info> for Box { fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> { T::close(self, sol_destination) diff --git a/lang/src/accounts/cpi_account.rs b/lang/src/accounts/cpi_account.rs index f041489391..ee7c907615 100644 --- a/lang/src/accounts/cpi_account.rs +++ b/lang/src/accounts/cpi_account.rs @@ -85,12 +85,6 @@ impl<'info, T: AccountDeserialize + Clone> ToAccountInfos<'info> for CpiAccount< } } -impl<'info, T: AccountDeserialize + Clone> TryToAccountInfo<'info> for CpiAccount<'info, T> { - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - #[allow(deprecated)] impl<'info, T: AccountDeserialize + Clone> AsRef> for CpiAccount<'info, T> { fn as_ref(&self) -> &AccountInfo<'info> { diff --git a/lang/src/accounts/cpi_state.rs b/lang/src/accounts/cpi_state.rs index 460cee324d..42150d2c89 100644 --- a/lang/src/accounts/cpi_state.rs +++ b/lang/src/accounts/cpi_state.rs @@ -2,8 +2,8 @@ use crate::error::ErrorCode; #[allow(deprecated)] use crate::{accounts::state::ProgramState, context::CpiStateContext}; use crate::{ - AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfo, - ToAccountInfos, ToAccountMetas, TryToAccountInfo, + AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfos, + ToAccountMetas, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -110,14 +110,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfo<'info> - for CpiState<'info, T> -{ - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - #[allow(deprecated)] impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> for CpiState<'info, T> diff --git a/lang/src/accounts/loader.rs b/lang/src/accounts/loader.rs index ba52b77d79..1d52207d58 100644 --- a/lang/src/accounts/loader.rs +++ b/lang/src/accounts/loader.rs @@ -2,7 +2,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ Accounts, AccountsClose, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, TryToAccountInfo, ZeroCopy, + ToAccountMetas, ZeroCopy, }; use arrayref::array_ref; use solana_program::account_info::AccountInfo; @@ -227,12 +227,6 @@ impl<'info, T: ZeroCopy> ToAccountInfos<'info> for Loader<'info, T> { } } -impl<'info, T: ZeroCopy> TryToAccountInfo<'info> for Loader<'info, T> { - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - #[allow(deprecated)] impl<'info, T: ZeroCopy> Key for Loader<'info, T> { fn key(&self) -> Pubkey { diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index 1aa7ef45b6..46e85e3905 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -14,10 +14,7 @@ use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; -use crate::{ - error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, TryKey, TryToAccountInfo, -}; +use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas}; impl<'info, T: Accounts<'info>> Accounts<'info> for Option { fn try_accounts( @@ -76,19 +73,3 @@ impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Option { self.as_ref().map_or(Ok(()), |t| t.exit(program_id)) } } - -impl TryKey for Option { - fn try_key(&self) -> Result { - self.as_ref() - .map_or(Err(ErrorCode::TryKeyOnNone.into()), |t| t.try_key()) - } -} - -impl<'info, T: ToAccountInfo<'info>> TryToAccountInfo<'info> for Option { - fn try_to_account_info(&self) -> Result> { - self.as_ref() - .map_or(Err(ErrorCode::TryToAccountInfoOnNone.into()), |t| { - Ok(t.to_account_info()) - }) - } -} diff --git a/lang/src/accounts/program.rs b/lang/src/accounts/program.rs index 093d73a362..d7e9975f89 100644 --- a/lang/src/accounts/program.rs +++ b/lang/src/accounts/program.rs @@ -2,8 +2,7 @@ use crate::error::{Error, ErrorCode}; use crate::{ - AccountDeserialize, Accounts, AccountsExit, Id, Key, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, TryToAccountInfo, + AccountDeserialize, Accounts, AccountsExit, Id, Key, Result, ToAccountInfos, ToAccountMetas, }; use solana_program::account_info::AccountInfo; use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState}; @@ -176,12 +175,6 @@ impl<'info, T: Id + Clone> ToAccountInfos<'info> for Program<'info, T> { } } -impl<'info, T: Id + Clone> TryToAccountInfo<'info> for Program<'info, T> { - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - impl<'info, T: Id + Clone> AsRef> for Program<'info, T> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/program_account.rs b/lang/src/accounts/program_account.rs index 174d6b0dd0..db6baffc72 100644 --- a/lang/src/accounts/program_account.rs +++ b/lang/src/accounts/program_account.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Result, - ToAccountInfo, ToAccountInfos, ToAccountMetas, TryToAccountInfo, + ToAccountInfo, ToAccountInfos, ToAccountMetas, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -147,15 +147,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfo<'info> - for ProgramAccount<'info, T> -{ - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - #[allow(deprecated)] impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> for ProgramAccount<'info, T> diff --git a/lang/src/accounts/signer.rs b/lang/src/accounts/signer.rs index 62d1d27e7f..7d757024a3 100644 --- a/lang/src/accounts/signer.rs +++ b/lang/src/accounts/signer.rs @@ -1,9 +1,6 @@ //! Type validating that the account signed the transaction use crate::error::ErrorCode; -use crate::{ - Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, - TryToAccountInfo, -}; +use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -94,12 +91,6 @@ impl<'info> ToAccountInfos<'info> for Signer<'info> { } } -impl<'info> TryToAccountInfo<'info> for Signer<'info> { - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - impl<'info> AsRef> for Signer<'info> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/state.rs b/lang/src/accounts/state.rs index ac725fb55c..563409fdb5 100644 --- a/lang/src/accounts/state.rs +++ b/lang/src/accounts/state.rs @@ -4,7 +4,7 @@ use crate::bpf_writer::BpfWriter; use crate::error::{Error, ErrorCode}; use crate::{ AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfo, - ToAccountInfos, ToAccountMetas, TryToAccountInfo, + ToAccountInfos, ToAccountMetas, }; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; @@ -108,14 +108,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'in } } -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> TryToAccountInfo<'info> - for ProgramState<'info, T> -{ - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - #[allow(deprecated)] impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> for ProgramState<'info, T> diff --git a/lang/src/accounts/system_account.rs b/lang/src/accounts/system_account.rs index 832a0bd101..8c90fd537c 100644 --- a/lang/src/accounts/system_account.rs +++ b/lang/src/accounts/system_account.rs @@ -70,12 +70,6 @@ impl<'info> ToAccountInfos<'info> for SystemAccount<'info> { } } -impl<'info> TryToAccountInfo<'info> for SystemAccount<'info> { - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - impl<'info> AsRef> for SystemAccount<'info> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/sysvar.rs b/lang/src/accounts/sysvar.rs index 23ad21aea5..c955ff9c15 100644 --- a/lang/src/accounts/sysvar.rs +++ b/lang/src/accounts/sysvar.rs @@ -1,10 +1,7 @@ //! Type validating that the account is a sysvar and deserializing it use crate::error::ErrorCode; -use crate::{ - Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, - TryToAccountInfo, -}; +use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -97,12 +94,6 @@ impl<'info, T: solana_program::sysvar::Sysvar> ToAccountInfos<'info> for Sysvar< } } -impl<'info, T: solana_program::sysvar::Sysvar> TryToAccountInfo<'info> for Sysvar<'info, T> { - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - impl<'info, T: solana_program::sysvar::Sysvar> AsRef> for Sysvar<'info, T> { fn as_ref(&self) -> &AccountInfo<'info> { &self.info diff --git a/lang/src/accounts/unchecked_account.rs b/lang/src/accounts/unchecked_account.rs index a9800ca1c5..5a00127579 100644 --- a/lang/src/accounts/unchecked_account.rs +++ b/lang/src/accounts/unchecked_account.rs @@ -2,10 +2,7 @@ //! that no checks are performed use crate::error::ErrorCode; -use crate::{ - Accounts, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, - TryToAccountInfo, -}; +use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; @@ -57,12 +54,6 @@ impl<'info> ToAccountInfos<'info> for UncheckedAccount<'info> { } } -impl<'info> TryToAccountInfo<'info> for UncheckedAccount<'info> { - fn try_to_account_info(&self) -> Result> { - Ok(self.to_account_info()) - } -} - impl<'info> AccountsExit<'info> for UncheckedAccount<'info> {} impl<'info> AsRef> for UncheckedAccount<'info> { diff --git a/lang/src/ctor.rs b/lang/src/ctor.rs index 22794b8686..c24d392e9f 100644 --- a/lang/src/ctor.rs +++ b/lang/src/ctor.rs @@ -1,4 +1,4 @@ -use crate::{Accounts, TryToAccountInfo}; +use crate::{Accounts, ToAccountInfo}; use solana_program::account_info::AccountInfo; /// The Ctor accounts that can be used to create any account within the program diff --git a/lang/src/error.rs b/lang/src/error.rs index 2ba342765f..020546b2da 100644 --- a/lang/src/error.rs +++ b/lang/src/error.rs @@ -105,6 +105,9 @@ pub enum ErrorCode { /// 2019 - A space constraint was violated #[msg("A space constraint was violated")] ConstraintSpace, + /// 2020 - A required account for the constraint is None + #[msg("A required account for the constraint is None")] + ConstraintAccountIsNone, // Require /// 2500 - A require expression was violated @@ -184,12 +187,6 @@ pub enum ErrorCode { /// 3017 - The account was duplicated for more than one reallocation #[msg("The account was duplicated for more than one reallocation")] AccountDuplicateReallocs, - /// 3018 - Tried to get the key from None - #[msg("Tried to get the key from None")] - TryKeyOnNone, - /// 3019 - Tried to get `to_account_info` from None - #[msg("Tried to get the account info from None")] - TryToAccountInfoOnNone, // State. /// 4000 - The given state account does not have the correct address diff --git a/lang/src/lib.rs b/lang/src/lib.rs index d7c170de7f..fc458ca830 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -142,12 +142,6 @@ where } } -/// Transformation to an AccountInfo struct. This operation may fail. Intended for use with -/// Optional accounts in order to bubble up Errors more easily -pub trait TryToAccountInfo<'info> { - fn try_to_account_info(&self) -> Result>; -} - /// A data structure that can be serialized and stored into account storage, /// i.e. an /// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html#structfield.data)'s @@ -243,17 +237,6 @@ impl Key for Pubkey { } } -/// Defines the Pubkey of an account in an operation that may fail -pub trait TryKey { - fn try_key(&self) -> Result; -} - -impl TryKey for T { - fn try_key(&self) -> Result { - Ok(self.key()) - } -} - /// The prelude contains all commonly used components of the crate. /// All programs should include it via `anchor_lang::prelude::*;`. pub mod prelude { @@ -267,7 +250,7 @@ pub mod prelude { require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state, system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result, - ToAccountInfo, ToAccountInfos, ToAccountMetas, TryKey, TryToAccountInfo, + ToAccountInfo, ToAccountInfos, ToAccountMetas, }; pub use anchor_attribute_error::*; pub use borsh; diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 5316b6edca..43ead082eb 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -1,9 +1,10 @@ -use crate::*; use proc_macro2_diagnostics::SpanDiagnosticExt; use quote::quote; use syn::Expr; -pub fn generate(f: &Field, has_optional: bool) -> proc_macro2::TokenStream { +use crate::*; + +pub fn generate(f: &Field, accs: &AccountsStruct) -> proc_macro2::TokenStream { let constraints = linearize(&f.constraints); let rent = constraints @@ -14,7 +15,7 @@ pub fn generate(f: &Field, has_optional: bool) -> proc_macro2::TokenStream { let checks: Vec = constraints .iter() - .map(|c| generate_constraint(f, c, has_optional)) + .map(|c| generate_constraint(f, c, accs)) .collect(); quote! { @@ -115,12 +116,16 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec { constraints } -fn generate_constraint(f: &Field, c: &Constraint, has_optional: bool) -> proc_macro2::TokenStream { +fn generate_constraint( + f: &Field, + c: &Constraint, + accs: &AccountsStruct, +) -> proc_macro2::TokenStream { let stream = match c { - Constraint::Init(c) => generate_constraint_init(f, c), + Constraint::Init(c) => generate_constraint_init(f, c, accs), Constraint::Zeroed(c) => generate_constraint_zeroed(f, c), Constraint::Mut(c) => generate_constraint_mut(f, c), - Constraint::HasOne(c) => generate_constraint_has_one(f, c, has_optional), + Constraint::HasOne(c) => generate_constraint_has_one(f, c, accs), Constraint::Signer(c) => generate_constraint_signer(f, c), Constraint::Literal(c) => generate_constraint_literal(&f.ident, c), Constraint::Raw(c) => generate_constraint_raw(&f.ident, c), @@ -128,20 +133,24 @@ fn generate_constraint(f: &Field, c: &Constraint, has_optional: bool) -> proc_ma 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), - Constraint::Close(c) => generate_constraint_close(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), - Constraint::TokenAccount(c) => generate_constraint_token_account(f, c), - Constraint::Mint(c) => generate_constraint_mint(f, c), - Constraint::Realloc(c) => generate_constraint_realloc(f, c), + Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c, accs), + Constraint::TokenAccount(c) => generate_constraint_token_account(f, c, accs), + Constraint::Mint(c) => generate_constraint_mint(f, c, accs), + Constraint::Realloc(c) => generate_constraint_realloc(f, c, accs), }; + // This adds a wrapper on optional accounts that prevents constraints from being run on + // optional accounts that are `None` as well as conveniently unwrapping the option on optional + // accounts that are present for use in constraint gen. if f.is_optional { let ident = &f.ident; + let ty_decl = f.ty_decl(false); match c { Constraint::Init(_) | Constraint::Zeroed(_) => { quote! { - let #ident = if let Some(#ident) = #ident { + let #ident: #ty_decl = if let Some(#ident) = #ident { #stream Some(#ident) } else { @@ -178,7 +187,6 @@ fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2: &c.error, quote! { ConstraintAddress }, &Some(&(quote! { actual }, quote! { expected })), - true, ); quote! { { @@ -191,8 +199,12 @@ fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2: } } -pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream { - generate_constraint_init_group(f, c) +pub fn generate_constraint_init( + f: &Field, + c: &ConstraintInitGroup, + accs: &AccountsStruct, +) -> proc_macro2::TokenStream { + generate_constraint_init_group(f, c, accs) } pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream { @@ -214,22 +226,31 @@ pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macr } } -pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2::TokenStream { +pub fn generate_constraint_close( + f: &Field, + c: &ConstraintClose, + accs: &AccountsStruct, +) -> proc_macro2::TokenStream { let field = &f.ident; let name_str = field.to_string(); let target = &c.sol_dest; + let target_optional_check = + generate_optional_check(accs, quote! {#target}, &target.to_string()); quote! { - if #field.key() == #target.try_key()? { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintClose).with_account_name(#name_str)); + { + #target_optional_check + if #field.key() == #target.key() { + return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintClose).with_account_name(#name_str)); + } } } } pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::TokenStream { let ident = &f.ident; - let error = generate_custom_error(ident, &c.error, quote! { ConstraintMut }, &None, true); + let error = generate_custom_error(ident, &c.error, quote! { ConstraintMut }, &None); quote! { - if !#ident.try_to_account_info()?.is_writable { + if !#ident.to_account_info().is_writable { return #error; } } @@ -238,7 +259,7 @@ pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::Tok pub fn generate_constraint_has_one( f: &Field, c: &ConstraintHasOne, - has_optional: bool, + accs: &AccountsStruct, ) -> proc_macro2::TokenStream { let target = c.join_target.clone(); let ident = &f.ident; @@ -247,45 +268,21 @@ pub fn generate_constraint_has_one( Ty::AccountLoader(_) => quote! {#ident.load()?}, _ => quote! {#ident}, }; - let error = generate_custom_error( ident, &c.error, quote! { ConstraintHasOne }, &Some(&(quote! { my_key }, quote! { target_key })), - true, ); - - let none_error = generate_custom_error( - ident, - &c.error, - quote! { ConstraintHasOne }, - &Some(&(quote! { my_key }, quote! { "None" })), - false, - ); - - if has_optional { - quote! { - { - let my_key = #field.#target; - let target_key = #target.try_key(); - if let Ok(target_key) = target_key { - if my_key != target_key { - return #error; - } - } else { - return #none_error; - } - } - } - } else { - quote! { - { - let my_key = #field.#target; - let target_key = #target.try_key()?; - if my_key != target_key { - return #error; - } + let target_optional_check = + generate_optional_check(accs, quote! {#target}, tts_to_string(&target)); + quote! { + { + #target_optional_check + let my_key = #field.#target; + let target_key = #target.key(); + if my_key != target_key { + return #error; } } } @@ -295,14 +292,14 @@ pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro let ident = &f.ident; let info = match f.ty { Ty::AccountInfo => quote! { #ident }, - Ty::ProgramAccount(_) => quote! { #ident.try_to_account_info()? }, - Ty::Account(_) => quote! { #ident.try_to_account_info()? }, - Ty::Loader(_) => quote! { #ident.try_to_account_info()? }, - Ty::AccountLoader(_) => quote! { #ident.try_to_account_info()? }, - Ty::CpiAccount(_) => quote! { #ident.try_to_account_info()? }, + Ty::ProgramAccount(_) => quote! { #ident.to_account_info() }, + Ty::Account(_) => quote! { #ident.to_account_info() }, + Ty::Loader(_) => quote! { #ident.to_account_info() }, + Ty::AccountLoader(_) => quote! { #ident.to_account_info() }, + Ty::CpiAccount(_) => quote! { #ident.to_account_info() }, _ => panic!("Invalid syntax: signer cannot be specified."), }; - let error = generate_custom_error(ident, &c.error, quote! { ConstraintSigner }, &None, true); + let error = generate_custom_error(ident, &c.error, quote! { ConstraintSigner }, &None); quote! { if !#info.is_signer { return #error; @@ -334,7 +331,7 @@ pub fn generate_constraint_literal( pub fn generate_constraint_raw(ident: &Ident, c: &ConstraintRaw) -> proc_macro2::TokenStream { let raw = &c.raw; - let error = generate_custom_error(ident, &c.error, quote! { ConstraintRaw }, &None, true); + let error = generate_custom_error(ident, &c.error, quote! { ConstraintRaw }, &None); quote! { if !(#raw) { return #error; @@ -350,7 +347,6 @@ pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2: &c.error, quote! { ConstraintOwner }, &Some(&(quote! { *my_owner }, quote! { owner_address })), - true, ); quote! { { @@ -370,7 +366,7 @@ pub fn generate_constraint_rent_exempt( let ident = &f.ident; let name_str = ident.to_string(); let info = quote! { - #ident.try_to_account_info()? + #ident.to_account_info() }; match c { ConstraintRentExempt::Skip => quote! {}, @@ -382,13 +378,22 @@ pub fn generate_constraint_rent_exempt( } } -fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_macro2::TokenStream { +fn generate_constraint_realloc( + f: &Field, + c: &ConstraintReallocGroup, + accs: &AccountsStruct, +) -> proc_macro2::TokenStream { let field = &f.ident; let account_name = field.to_string(); let new_space = &c.space; let payer = &c.payer; let zero = &c.zero; + let payer_optional_check = + generate_optional_check(accs, quote! {#payer}, &tts_to_string(payer)); + let system_program_optional_check = + generate_optional_check(accs, quote! {system_program}, "system_program"); + quote! { // Blocks duplicate account reallocs in a single instruction to prevent accidental account overwrites // and to ensure the calculation of the change in bytes is based on account size at program entry @@ -398,7 +403,7 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma } let __anchor_rent = anchor_lang::prelude::Rent::get()?; - let __field_info = #field.try_to_account_info()?; + let __field_info = #field.to_account_info(); let __new_rent_minimum = __anchor_rent.minimum_balance(#new_space); let __delta_space = (::std::convert::TryInto::::try_into(#new_space).unwrap()) @@ -406,7 +411,9 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma .unwrap(); if __delta_space != 0 { + #payer_optional_check if __delta_space > 0 { + #system_program_optional_check if ::std::convert::TryInto::::try_into(__delta_space).unwrap() > anchor_lang::solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE { return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::AccountReallocExceedsLimit).with_account_name(#account_name)); } @@ -414,9 +421,9 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma if __new_rent_minimum > __field_info.lamports() { anchor_lang::system_program::transfer( anchor_lang::context::CpiContext::new( - system_program.try_to_account_info()?, + system_program.to_account_info(), anchor_lang::system_program::Transfer { - from: #payer.try_to_account_info()?, + from: #payer.to_account_info(), to: __field_info.clone(), }, ), @@ -425,17 +432,21 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma } } else { let __lamport_amt = __field_info.lamports().checked_sub(__new_rent_minimum).unwrap(); - **#payer.try_to_account_info()?.lamports.borrow_mut() = #payer.try_to_account_info()?.lamports().checked_add(__lamport_amt).unwrap(); + **#payer.to_account_info().lamports.borrow_mut() = #payer.to_account_info().lamports().checked_add(__lamport_amt).unwrap(); **__field_info.lamports.borrow_mut() = __field_info.lamports().checked_sub(__lamport_amt).unwrap(); } - #field.try_to_account_info()?.realloc(#new_space, #zero)?; + #field.to_account_info().realloc(#new_space, #zero)?; __reallocs.insert(#field.key()); } } } -fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream { +fn generate_constraint_init_group( + f: &Field, + c: &ConstraintInitGroup, + accs: &AccountsStruct, +) -> proc_macro2::TokenStream { let field = &f.ident; let name_str = f.ident.to_string(); let ty_decl = f.ty_decl(true); @@ -449,8 +460,10 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma // Payer for rent exemption. let payer = { let p = &c.payer; + let payer_optional_check = generate_optional_check(accs, quote! {#p}, tts_to_string(p)); quote! { - let payer = #p.try_to_account_info()?; + #payer_optional_check + let payer = #p.to_account_info(); } }; @@ -492,19 +505,46 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma } }; + let system_program_optional_check = + generate_optional_check(accs, quote! {system_program}, "system_program"); + let token_program_optional_check = + generate_optional_check(accs, quote! {token_program}, "token_program"); + let associated_token_program_optional_check = generate_optional_check( + accs, + quote! {associated_token_program}, + "associated_token_program", + ); + let rent_optional_check = generate_optional_check(accs, quote! {rent}, "rent"); + match &c.kind { InitKind::Token { owner, mint } => { + let owner_optional_check = + generate_optional_check(accs, quote! {#owner}, &tts_to_string(owner)); + let mint_optional_check = + generate_optional_check(accs, quote! {#mint}, &tts_to_string(mint)); + let optional_checks = quote! { + #system_program_optional_check + #token_program_optional_check + #rent_optional_check + #owner_optional_check + #mint_optional_check + }; + let create_account = generate_create_account( field, quote! {anchor_spl::token::TokenAccount::LEN}, quote! {&token_program.key()}, seeds_with_bump, ); + quote! { // Define the bump and pda variable. #find_pda let #field: #ty_decl = { + // Checks that all the required accounts for this operation are present. + #optional_checks + if !#if_needed || AsRef::::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID { // Define payer variable. #payer @@ -513,12 +553,12 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma #create_account // Initialize the token account. - let cpi_program = token_program.try_to_account_info()?; + let cpi_program = token_program.to_account_info(); let accounts = anchor_spl::token::InitializeAccount { - account: #field.try_to_account_info()?, - mint: #mint.try_to_account_info()?, - authority: #owner.try_to_account_info()?, - rent: rent.try_to_account_info()?, + account: #field.to_account_info(), + mint: #mint.to_account_info(), + authority: #owner.to_account_info(), + rent: rent.to_account_info(), }; let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts); anchor_spl::token::initialize_account(cpi_ctx)?; @@ -538,23 +578,39 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma } } InitKind::AssociatedToken { owner, mint } => { + let owner_optional_check = + generate_optional_check(accs, quote! {#owner}, &tts_to_string(owner)); + let mint_optional_check = + generate_optional_check(accs, quote! {#mint}, &tts_to_string(mint)); + let optional_checks = quote! { + #system_program_optional_check + #associated_token_program_optional_check + #token_program_optional_check + #rent_optional_check + #owner_optional_check + #mint_optional_check + }; + quote! { // Define the bump and pda variable. #find_pda let #field: #ty_decl = { + // Checks that all the required accounts for this operation are present. + #optional_checks + if !#if_needed || AsRef::::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID { #payer - let cpi_program = associated_token_program.try_to_account_info()?; + let cpi_program = associated_token_program.to_account_info(); let cpi_accounts = anchor_spl::associated_token::Create { - payer: payer.try_to_account_info()?, - associated_token: #field.try_to_account_info()?, - authority: #owner.try_to_account_info()?, - mint: #mint.try_to_account_info()?, - system_program: system_program.try_to_account_info()?, - token_program: token_program.try_to_account_info()?, - rent: rent.try_to_account_info()?, + payer: payer.to_account_info(), + associated_token: #field.to_account_info(), + authority: #owner.to_account_info(), + mint: #mint.to_account_info(), + system_program: system_program.to_account_info(), + token_program: token_program.to_account_info(), + rent: rent.to_account_info(), }; let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, cpi_accounts); anchor_spl::associated_token::create(cpi_ctx)?; @@ -581,6 +637,15 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma decimals, freeze_authority, } => { + let owner_optional_check = + generate_optional_check(accs, quote! {#owner}, &tts_to_string(owner)); + let optional_checks = quote! { + #system_program_optional_check + #token_program_optional_check + #rent_optional_check + #owner_optional_check + }; + let create_account = generate_create_account( field, quote! {anchor_spl::token::Mint::LEN}, @@ -588,7 +653,14 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma seeds_with_bump, ); let freeze_authority = match freeze_authority { - Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, + Some(fa) => { + let freeze_authority_optional_check = + generate_optional_check(accs, quote! {#fa}, &tts_to_string(fa)); + quote! { + #freeze_authority_optional_check + Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) + } + } None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, }; quote! { @@ -596,6 +668,9 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma #find_pda let #field: #ty_decl = { + // Checks that all the required accounts for this operation are present. + #optional_checks + if !#if_needed || AsRef::::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID { // Define payer variable. #payer @@ -604,10 +679,10 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma #create_account // Initialize the mint account. - let cpi_program = token_program.try_to_account_info()?; + let cpi_program = token_program.to_account_info(); let accounts = anchor_spl::token::InitializeMint { - mint: #field.try_to_account_info()?, - rent: rent.try_to_account_info()?, + mint: #field.to_account_info(), + rent: rent.to_account_info(), }; let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts); anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.key(), #freeze_authority)?; @@ -641,9 +716,18 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma None => quote! { program_id }, - Some(o) => quote! { - &#o - }, + Some(o) => { + let owner_optional_check = + generate_optional_check(accs, quote! {#o}, &tts_to_string(o)); + quote! { + #owner_optional_check + &#o + } + } + }; + + let optional_checks = quote! { + #system_program_optional_check }; // CPI to the system program to create the account. @@ -656,7 +740,10 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma #find_pda let #field = { - let actual_field = #field.try_to_account_info()?; + // Checks that all the required accounts for this operation are present. + #optional_checks + + let actual_field = #field.to_account_info(); let actual_owner = actual_field.owner; // Define the account space variable. @@ -690,7 +777,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma { let required_lamports = __anchor_rent.minimum_balance(space); - if pa.try_to_account_info()?.lamports() < required_lamports { + if pa.to_account_info().lamports() < required_lamports { return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintRentExempt).with_account_name(#name_str)); } } @@ -790,13 +877,27 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2 fn generate_constraint_associated_token( f: &Field, c: &ConstraintAssociatedToken, + accs: &AccountsStruct, ) -> proc_macro2::TokenStream { let name = &f.ident; let name_str = name.to_string(); let wallet_address = &c.wallet; let spl_token_mint_address = &c.mint; + let wallet_address_optional_check = generate_optional_check( + accs, + quote! {#wallet_address}, + tts_to_string(wallet_address), + ); + let spl_token_mint_address_optional_check = generate_optional_check( + accs, + quote! {#spl_token_mint_address}, + tts_to_string(spl_token_mint_address), + ); quote! { { + #wallet_address_optional_check + #spl_token_mint_address_optional_check + let my_owner = #name.owner; let wallet_address = #wallet_address.key(); if my_owner != wallet_address { @@ -814,27 +915,44 @@ fn generate_constraint_associated_token( fn generate_constraint_token_account( f: &Field, c: &ConstraintTokenAccountGroup, + accs: &AccountsStruct, ) -> proc_macro2::TokenStream { let name = &f.ident; let authority_check = match &c.authority { Some(authority) => { - quote! { if #name.owner != #authority.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); } } + let authority_optional_check = + generate_optional_check(accs, quote! {#authority}, tts_to_string(authority)); + quote! { + #authority_optional_check + if #name.owner != #authority.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); } + } } None => quote! {}, }; let mint_check = match &c.mint { Some(mint) => { - quote! { if #name.mint != #mint.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); } } + let mint_optional_check = + generate_optional_check(accs, quote! {#mint}, tts_to_string(mint)); + quote! { + #mint_optional_check + if #name.mint != #mint.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); } + } } None => quote! {}, }; quote! { - #authority_check - #mint_check + { + #authority_check + #mint_check + } } } -fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_macro2::TokenStream { +fn generate_constraint_mint( + f: &Field, + c: &ConstraintTokenMintGroup, + accs: &AccountsStruct, +) -> proc_macro2::TokenStream { let name = &f.ident; let decimal_check = match &c.decimals { @@ -846,25 +964,62 @@ fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_mac None => quote! {}, }; let mint_authority_check = match &c.mint_authority { - Some(mint_authority) => quote! { - if #name.mint_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#mint_authority)) { - return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into()); + Some(mint_authority) => { + let mint_authority_optional_check = generate_optional_check( + accs, + quote! {#mint_authority}, + tts_to_string(mint_authority), + ); + quote! { + #mint_authority_optional_check + if #name.mint_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#mint_authority)) { + return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into()); + } } - }, + } None => quote! {}, }; let freeze_authority_check = match &c.freeze_authority { - Some(freeze_authority) => quote! { - if #name.freeze_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#freeze_authority)) { - return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into()); + Some(freeze_authority) => { + let freeze_authority_optional_check = generate_optional_check( + accs, + quote! {#freeze_authority}, + tts_to_string(freeze_authority), + ); + quote! { + #freeze_authority_optional_check + if #name.freeze_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#freeze_authority)) { + return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into()); + } } - }, + } None => quote! {}, }; quote! { - #decimal_check - #mint_authority_check - #freeze_authority_check + { + #decimal_check + #mint_authority_check + #freeze_authority_check + } + } +} + +pub fn generate_optional_check( + accs: &AccountsStruct, + field: TokenStream, + field_name: impl ToString, +) -> TokenStream { + let field_name = field_name.to_string(); + if accs.is_field_optional(&field) { + quote! { + let #field = if let Some(#field) = &#field { + #field + } else { + return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAccountIsNone).with_account_name(#field_name)); + }; + } + } else { + quote! {} } } @@ -873,12 +1028,15 @@ fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_mac // // `seeds_with_nonce` should be given for creating PDAs. Otherwise it's an // empty stream. -pub fn generate_create_account( +// +// This should only be run within scopes where `system_program` is not Optional +fn generate_create_account( field: &Ident, space: proc_macro2::TokenStream, owner: proc_macro2::TokenStream, seeds_with_nonce: proc_macro2::TokenStream, ) -> proc_macro2::TokenStream { + // Field, payer, and system program are already validated to not be an Option at this point quote! { // If the account being initialized already has lamports, then // return them all back to the payer so that the account has @@ -889,10 +1047,10 @@ pub fn generate_create_account( // Create the token account with right amount of lamports and space, and the correct owner. let lamports = __anchor_rent.minimum_balance(#space); let cpi_accounts = anchor_lang::system_program::CreateAccount { - from: payer.try_to_account_info()?, - to: #field.try_to_account_info()? + from: payer.to_account_info(), + to: #field.to_account_info() }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.try_to_account_info()?, cpi_accounts); + let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); anchor_lang::system_program::create_account(cpi_context.with_signer(&[#seeds_with_nonce]), lamports, #space as u64, #owner)?; } else { // Fund the account for rent exemption. @@ -902,23 +1060,23 @@ pub fn generate_create_account( .saturating_sub(__current_lamports); if required_lamports > 0 { let cpi_accounts = anchor_lang::system_program::Transfer { - from: payer.try_to_account_info()?, - to: #field.try_to_account_info()?, + from: payer.to_account_info(), + to: #field.to_account_info(), }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.try_to_account_info()?, cpi_accounts); + let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); anchor_lang::system_program::transfer(cpi_context, required_lamports)?; } // Allocate space. let cpi_accounts = anchor_lang::system_program::Allocate { - account_to_allocate: #field.try_to_account_info()? + account_to_allocate: #field.to_account_info() }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.try_to_account_info()?, cpi_accounts); + let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); anchor_lang::system_program::allocate(cpi_context.with_signer(&[#seeds_with_nonce]), #space as u64)?; // Assign to the spl token program. let cpi_accounts = anchor_lang::system_program::Assign { - account_to_assign: #field.try_to_account_info()? + account_to_assign: #field.to_account_info() }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.try_to_account_info()?, cpi_accounts); + let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); anchor_lang::system_program::assign(cpi_context.with_signer(&[#seeds_with_nonce]), #owner)?; } } @@ -930,14 +1088,21 @@ pub fn generate_constraint_executable( ) -> proc_macro2::TokenStream { let name = &f.ident; let name_str = name.to_string(); + + // because we are only acting on the field, we know it isnt optional at this point + // as it was unwrapped in `generate_constraint` quote! { - if !#name.try_to_account_info()?.executable { + if !#name.to_account_info().executable { return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintExecutable).with_account_name(#name_str)); } } } -pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2::TokenStream { +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(); @@ -945,14 +1110,22 @@ pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2: Ty::CpiState(ty) => &ty.account_type_path, _ => panic!("Invalid state constraint"), }; + let program_target_optional_check = generate_optional_check( + accs, + quote! {#program_target}, + tts_to_string(&program_target), + ); quote! { - // 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)); + { + #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)); + } } } } @@ -962,7 +1135,6 @@ fn generate_custom_error( custom_error: &Option, error: proc_macro2::TokenStream, compared_values: &Option<&(proc_macro2::TokenStream, proc_macro2::TokenStream)>, - with_pubkeys: bool, ) -> proc_macro2::TokenStream { let account_name = account_name.to_string(); let mut error = match custom_error { @@ -975,13 +1147,7 @@ fn generate_custom_error( }; let compared_values = match compared_values { - Some((left, right)) => { - if with_pubkeys { - quote! { .with_pubkeys((#left, #right)) } - } else { - quote! { .with_values((#left, #right)) } - } - } + Some((left, right)) => quote! { .with_pubkeys((#left, #right)) }, None => quote! {}, }; diff --git a/lang/syn/src/codegen/accounts/exit.rs b/lang/syn/src/codegen/accounts/exit.rs index e59da85ef5..30bca77c71 100644 --- a/lang/syn/src/codegen/accounts/exit.rs +++ b/lang/syn/src/codegen/accounts/exit.rs @@ -1,3 +1,4 @@ +use crate::accounts_codegen::constraints::generate_optional_check; use crate::codegen::accounts::{generics, ParsedGenerics}; use crate::{AccountField, AccountsStruct}; use quote::quote; @@ -29,11 +30,20 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { let name_str = ident.to_string(); if f.constraints.is_close() { let close_target = &f.constraints.close.as_ref().unwrap().sol_dest; + let close_target_optional_check = generate_optional_check( + accs, + quote! {#close_target}, + close_target.to_string(), + ); + quote! { - anchor_lang::AccountsClose::close( - &self.#ident, - self.#close_target.try_to_account_info()?, - ).map_err(|e| e.with_account_name(#name_str))?; + { + #close_target_optional_check + anchor_lang::AccountsClose::close( + &self.#ident, + self.#close_target.to_account_info(), + ).map_err(|e| e.with_account_name(#name_str))?; + } } } else { match f.constraints.is_mutable() { diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs index 437f2af17e..7ba658c040 100644 --- a/lang/syn/src/lib.rs +++ b/lang/syn/src/lib.rs @@ -1,3 +1,4 @@ +use crate::parser::tts_to_string; use codegen::accounts as accounts_codegen; use codegen::program as program_codegen; use parser::accounts as accounts_parser; @@ -190,6 +191,18 @@ impl AccountsStruct { } false } + + pub fn is_field_optional(&self, field: &T) -> bool { + let matching_field = self + .fields + .iter() + .find(|f| *f.ident() == tts_to_string(field)); + if let Some(matching_field) = matching_field { + matching_field.is_optional() + } else { + false + } + } } #[allow(clippy::large_enum_variant)] @@ -247,7 +260,7 @@ impl Field { } } - pub fn ty_decl(&self, option_inner_ty: bool) -> proc_macro2::TokenStream { + pub fn ty_decl(&self, ignore_option: bool) -> proc_macro2::TokenStream { let account_ty = self.account_ty(); let container_ty = self.container_ty(); let inner_ty = match &self.ty { @@ -298,7 +311,7 @@ impl Field { #container_ty<#account_ty> }, }; - if self.is_optional && !option_inner_ty { + if self.is_optional && !ignore_option { quote! { Option<#inner_ty> } @@ -311,6 +324,8 @@ impl Field { // TODO: remove the option once `CpiAccount` is completely removed (not // just deprecated). + // Ignores optional accounts. Optional account checks and handing should be done prior to this + // function being called. pub fn from_account_info( &self, kind: Option<&InitKind>, @@ -329,9 +344,9 @@ impl Field { }, }; match &self.ty { - Ty::AccountInfo => quote! { #field.try_to_account_info()? }, + Ty::AccountInfo => quote! { #field.to_account_info() }, Ty::UncheckedAccount => { - quote! { UncheckedAccount::try_from(#field.try_to_account_info()?) } + quote! { UncheckedAccount::try_from(#field.to_account_info()) } } Ty::Account(AccountTy { boxed, .. }) => { let stream = if checked { diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index 6666e3100f..1dad6d7c42 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -17,8 +17,8 @@ mod optional { data.data = value; } if let Some(data2) = optional2 { - if let Ok(optional_key) = optional.try_key() { - data2.optional1 = optional_key; + if let Some(optional) = optional { + data2.optional1 = optional.key(); } else { data2.optional1 = key; } @@ -79,7 +79,7 @@ pub struct Realloc<'info> { pub optional1: Option>, #[account(has_one = optional1)] pub optional2: Option>, - pub system_program: Program<'info, System>, + pub system_program: Option>, } #[derive(Accounts)] From 66d6b4c52878b9775567044c2cc4eb29d75b48a1 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 22 Aug 2022 22:20:43 -0400 Subject: [PATCH 051/109] fix exit generation --- lang/syn/src/codegen/accounts/exit.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lang/syn/src/codegen/accounts/exit.rs b/lang/syn/src/codegen/accounts/exit.rs index 30bca77c71..ed23cfbdc2 100644 --- a/lang/syn/src/codegen/accounts/exit.rs +++ b/lang/syn/src/codegen/accounts/exit.rs @@ -30,18 +30,27 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { let name_str = ident.to_string(); if f.constraints.is_close() { let close_target = &f.constraints.close.as_ref().unwrap().sol_dest; - let close_target_optional_check = generate_optional_check( - accs, - quote! {#close_target}, - close_target.to_string(), - ); + let close_target_optional_info = if accs.is_field_optional(close_target) { + let close_target_name = close_target.to_string(); + quote! { + let close_target_info = if let Some(close_target) = &self.#close_target { + close_target.to_account_info() + } else { + return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAccountIsNone).with_account_name(#close_target_name)); + }; + } + } else { + quote! { + let close_target_info = self.#close_target.to_account_info(); + } + }; quote! { { - #close_target_optional_check + #close_target_optional_info anchor_lang::AccountsClose::close( &self.#ident, - self.#close_target.to_account_info(), + close_target_info, ).map_err(|e| e.with_account_name(#name_str))?; } } From 49810492c650a3275ac7e22d6deec451dff40399 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 22 Aug 2022 22:37:45 -0400 Subject: [PATCH 052/109] clippy --- lang/syn/src/codegen/accounts/exit.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lang/syn/src/codegen/accounts/exit.rs b/lang/syn/src/codegen/accounts/exit.rs index ed23cfbdc2..6938ba7281 100644 --- a/lang/syn/src/codegen/accounts/exit.rs +++ b/lang/syn/src/codegen/accounts/exit.rs @@ -1,4 +1,3 @@ -use crate::accounts_codegen::constraints::generate_optional_check; use crate::codegen::accounts::{generics, ParsedGenerics}; use crate::{AccountField, AccountsStruct}; use quote::quote; From a4b365e6eb8f427be48245f0465154174206d173 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 23 Aug 2022 13:41:59 -0400 Subject: [PATCH 053/109] improve cross check error message --- lang/syn/src/parser/accounts/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/syn/src/parser/accounts/mod.rs b/lang/syn/src/parser/accounts/mod.rs index 201d195de4..bc72d4da30 100644 --- a/lang/syn/src/parser/accounts/mod.rs +++ b/lang/syn/src/parser/accounts/mod.rs @@ -44,8 +44,8 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { let message = |constraint: &str, field: &str, required: bool| { if required { format! { - "the {} constraint requires \ - the {} field to exist in the account \ + "a non-optional {} constraint requires \ + a non-optional {} field to exist in the account \ validation struct. Use the Program type to add \ the {} field to your validation struct.", constraint, field, field } From e025f093962f9827ea40fe7ff23a1b6aa5b403d7 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sun, 28 Aug 2022 23:06:19 -0400 Subject: [PATCH 054/109] improve comments --- lang/src/accounts/option.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index 46e85e3905..04e7e48998 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -24,14 +24,25 @@ impl<'info, T: Accounts<'info>> Accounts<'info> for Option { bumps: &mut BTreeMap, reallocs: &mut BTreeSet, ) -> Result { + // There are three cases we have to handle. We don't care if + // accounts is empty, so if that's the case we return None. This + // allows adding optional accounts at the end of the Accounts + // struct without causing a breaking change. This is safe and will error + // out if a required account is then added after the optional account and + // the accounts aren't passed in. if accounts.is_empty() { return Ok(None); } - let account = &accounts[0]; - if account.key == program_id { + + // If there are enough accounts, it will check the program_id and return + // None if it matches, popping the first account off the accounts vec. + if accounts[0].key == program_id { *accounts = &accounts[1..]; Ok(None) } else { + // If the program_id doesn't equal the account key, we default to + // the try_accounts implementation for the inner type and then wrap that with + // Some. This should handle all possible valid cases. T::try_accounts(program_id, accounts, ix_data, bumps, reallocs).map(Some) } } From fe083c562ae7007f3b1de53b064180e15bf7922e Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sun, 28 Aug 2022 23:06:37 -0400 Subject: [PATCH 055/109] more comments --- lang/syn/src/codegen/accounts/try_accounts.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lang/syn/src/codegen/accounts/try_accounts.rs b/lang/syn/src/codegen/accounts/try_accounts.rs index f90e83bff4..dbedec97ca 100644 --- a/lang/syn/src/codegen/accounts/try_accounts.rs +++ b/lang/syn/src/codegen/accounts/try_accounts.rs @@ -34,7 +34,11 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { // AccountInfo for later use at constraint validation time. if is_init(af) || f.constraints.zeroed.is_some() { let name = &f.ident; + // Optional accounts have slightly different behavior here and + // we can't leverage the try_accounts implementation for zero and init. if f.is_optional { + // Thus, this block essentially reimplements the try_accounts + // behavior with optional accounts minus the deserialziation. quote! { let #name = if accounts.is_empty() { None From 38521456bf951b90c4e3abd3778275103842ec9e Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 29 Aug 2022 01:15:34 -0400 Subject: [PATCH 056/109] update constraints hopefully good now? --- lang/syn/src/codegen/accounts/constraints.rs | 165 ++++++++-------- tests/optional/programs/optional/src/lib.rs | 98 +--------- .../programs/optional/src/processor/close.rs | 17 ++ .../programs/optional/src/processor/errors.rs | 7 + .../optional/src/processor/initialize.rs | 36 ++++ .../programs/optional/src/processor/mod.rs | 11 ++ .../optional/src/processor/realloc.rs | 24 +++ .../programs/optional/src/processor/update.rs | 25 +++ tests/optional/programs/optional/src/state.rs | 20 ++ tests/optional/tests/optional.ts | 179 ++++++++++++------ 10 files changed, 351 insertions(+), 231 deletions(-) create mode 100644 tests/optional/programs/optional/src/processor/close.rs create mode 100644 tests/optional/programs/optional/src/processor/errors.rs create mode 100644 tests/optional/programs/optional/src/processor/initialize.rs create mode 100644 tests/optional/programs/optional/src/processor/mod.rs create mode 100644 tests/optional/programs/optional/src/processor/realloc.rs create mode 100644 tests/optional/programs/optional/src/processor/update.rs create mode 100644 tests/optional/programs/optional/src/state.rs diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 43ead082eb..a2cf623b83 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -18,9 +18,38 @@ pub fn generate(f: &Field, accs: &AccountsStruct) -> proc_macro2::TokenStream { .map(|c| generate_constraint(f, c, accs)) .collect(); + let mut all_checks = quote! {#(#checks)*}; + + // If the field is optional we do all the inner checks as if the account + // wasn't optional. If the account is init we also need to return an Option + // by wrapping the resulting value with Some or returning None if it doesn't exist. + if f.is_optional && !constraints.is_empty() { + let ident = &f.ident; + let ty_decl = f.ty_decl(false); + all_checks = match &constraints[0] { + Constraint::Init(_) | Constraint::Zeroed(_) => { + quote! { + let #ident: #ty_decl = if let Some(#ident) = #ident { + #all_checks + Some(#ident) + } else { + None + }; + } + } + _ => { + quote! { + if let Some(#ident) = &#ident { + #all_checks + } + } + } + }; + } + quote! { #rent - #(#checks)* + #all_checks } } @@ -121,7 +150,7 @@ fn generate_constraint( c: &Constraint, accs: &AccountsStruct, ) -> proc_macro2::TokenStream { - let stream = match c { + match c { Constraint::Init(c) => generate_constraint_init(f, c, accs), Constraint::Zeroed(c) => generate_constraint_zeroed(f, c), Constraint::Mut(c) => generate_constraint_mut(f, c), @@ -140,34 +169,6 @@ fn generate_constraint( Constraint::TokenAccount(c) => generate_constraint_token_account(f, c, accs), Constraint::Mint(c) => generate_constraint_mint(f, c, accs), Constraint::Realloc(c) => generate_constraint_realloc(f, c, accs), - }; - // This adds a wrapper on optional accounts that prevents constraints from being run on - // optional accounts that are `None` as well as conveniently unwrapping the option on optional - // accounts that are present for use in constraint gen. - if f.is_optional { - let ident = &f.ident; - let ty_decl = f.ty_decl(false); - match c { - Constraint::Init(_) | Constraint::Zeroed(_) => { - quote! { - let #ident: #ty_decl = if let Some(#ident) = #ident { - #stream - Some(#ident) - } else { - None - }; - } - } - _ => { - quote! { - if let Some(#ident) = &#ident { - #stream - } - } - } - } - } else { - stream } } @@ -487,6 +488,36 @@ fn generate_constraint_init_group( quote! { #seeds, } }); + let validate_pda = { + // If the bump is provided with init *and target*, then force it to be the + // canonical bump. + // + // Note that for `#[account(init, seeds)]`, find_program_address has already + // been run in the init constraint find_pda variable. + if c.bump.is_some() { + let b = c.bump.as_ref().unwrap(); + quote! { + if #field.key() != __pda_address { + return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#field.key(), __pda_address))); + } + if __bump != #b { + return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_values((__bump, #b))); + } + } + } else { + // Init seeds but no bump. We already used the canonical to create bump so + // just check the address. + // + // Note that for `#[account(init, seeds)]`, find_program_address has already + // been run in the init constraint find_pda variable. + quote! { + if #field.key() != __pda_address { + return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#field.key(), __pda_address))); + } + } + } + }; + ( quote! { let (__pda_address, __bump) = Pubkey::find_program_address( @@ -494,6 +525,7 @@ fn generate_constraint_init_group( program_id, ); __bumps.insert(#name_str.to_string(), __bump); + #validate_pda }, quote! { &[ @@ -792,59 +824,36 @@ fn generate_constraint_init_group( } fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream { - let name = &f.ident; - let name_str = name.to_string(); - - let s = &mut c.seeds.clone(); - - let deriving_program_id = c - .program_seed - .clone() - // If they specified a seeds::program to use when deriving the PDA, use it. - .map(|program_id| quote! { #program_id.key() }) - // Otherwise fall back to the current program's program_id. - .unwrap_or(quote! { program_id }); - - // If the seeds came with a trailing comma, we need to chop it off - // before we interpolate them below. - if let Some(pair) = s.pop() { - s.push_value(pair.into_value()); - } - - // If the bump is provided with init *and target*, then force it to be the - // canonical bump. - // - // Note that for `#[account(init, seeds)]`, find_program_address has already - // been run in the init constraint. - if c.is_init && c.bump.is_some() { - let b = c.bump.as_ref().unwrap(); - quote! { - if #name.key() != __pda_address { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#name.key(), __pda_address))); - } - if __bump != #b { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_values((__bump, #b))); - } - } - } - // Init seeds but no bump. We already used the canonical to create bump so - // just check the address. - // - // Note that for `#[account(init, seeds)]`, find_program_address has already - // been run in the init constraint. - else if c.is_init { - quote! { - if #name.key() != __pda_address { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#name.key(), __pda_address))); - } + if c.is_init { + // Note that for `#[account(init, seeds)]`, the seed generation and checks is checked in + // the init constraint find_pda/validate_pda block, so we don't do anything here and + // return nothing! + quote! {} + } else { + let name = &f.ident; + let name_str = name.to_string(); + + let s = &mut c.seeds.clone(); + + let deriving_program_id = c + .program_seed + .clone() + // If they specified a seeds::program to use when deriving the PDA, use it. + .map(|program_id| quote! { #program_id.key() }) + // Otherwise fall back to the current program's program_id. + .unwrap_or(quote! { program_id }); + + // If the seeds came with a trailing comma, we need to chop it off + // before we interpolate them below. + if let Some(pair) = s.pop() { + s.push_value(pair.into_value()); } - } - // No init. So we just check the address. - else { + let maybe_seeds_plus_comma = (!s.is_empty()).then(|| { quote! { #s, } }); + // Not init here, so do all the checks. let define_pda = match c.bump.as_ref() { // Bump target not given. Find it. None => quote! { diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index 1dad6d7c42..f745986d6b 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -2,7 +2,10 @@ //! structs deriving `Accounts`. use anchor_lang::prelude::*; +use processor::*; +pub mod processor; +pub mod state; declare_id!("FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG"); #[program] @@ -10,103 +13,18 @@ mod optional { use super::*; pub fn initialize(ctx: Context, value: u64, key: Pubkey) -> Result<()> { - let optional = &mut ctx.accounts.optional1; - let optional2 = &mut ctx.accounts.optional2; - - if let Some(data) = optional { - data.data = value; - } - if let Some(data2) = optional2 { - if let Some(optional) = optional { - data2.optional1 = optional.key(); - } else { - data2.optional1 = key; - } - } - Ok(()) + handle_initialize(ctx, value, key) } pub fn update(ctx: Context, value: u64, key: Pubkey) -> Result<()> { - if let Some(data) = &mut ctx.accounts.optional1 { - data.data = value; - }; - if let Some(data2) = &mut ctx.accounts.optional2 { - data2.optional1 = key; - }; - Ok(()) + handle_update(ctx, value, key) } pub fn realloc(ctx: Context) -> Result<()> { - let optional = &ctx.accounts.optional1; - if let Some(acc) = optional { - let len = acc.to_account_info().data_len(); - msg!("Len: {}", len); - } - Ok(()) + handle_realloc(ctx) } - pub fn close(_ctx: Context) -> Result<()> { - Ok(()) + pub fn close(ctx: Context) -> Result<()> { + handle_close(ctx) } } - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(mut)] - pub payer: Option>, - #[account(init, payer=payer, space=Data1::LEN, constraint = payer.is_some() && system_program.is_some())] - pub optional1: Option>, - #[account(init, payer=payer, space=Data2::LEN, constraint = payer.is_some() && system_program.is_some())] - pub optional2: Option>, - pub system_program: Option>, -} - -#[derive(Accounts)] -pub struct Update<'info> { - #[account(mut)] - pub payer: Option>, - #[account(mut, constraint = payer.is_some())] - pub optional1: Option>, - #[account(mut, constraint = payer.is_some())] - pub optional2: Option>, -} - -#[derive(Accounts)] -pub struct Realloc<'info> { - #[account(mut)] - pub payer: Signer<'info>, - #[account(mut, realloc = 20, realloc::payer = payer, realloc::zero = false)] - pub optional1: Option>, - #[account(has_one = optional1)] - pub optional2: Option>, - pub system_program: Option>, -} - -#[derive(Accounts)] -pub struct Close<'info> { - #[account(mut)] - pub payer: Option>, - #[account(mut, close = payer, constraint = payer.is_some() && system_program.is_some())] - pub optional1: Option>, - #[account(mut, close = payer, has_one = optional1, constraint = payer.is_some() && system_program.is_some())] - pub optional2: Option>, - pub system_program: Option>, -} - -#[account] -pub struct Data1 { - pub data: u64, -} - -impl Data1 { - const LEN: usize = 8 + 8; -} - -#[account] -pub struct Data2 { - pub optional1: Pubkey, -} - -impl Data2 { - const LEN: usize = 8 + 32; -} diff --git a/tests/optional/programs/optional/src/processor/close.rs b/tests/optional/programs/optional/src/processor/close.rs new file mode 100644 index 0000000000..3adbccfc39 --- /dev/null +++ b/tests/optional/programs/optional/src/processor/close.rs @@ -0,0 +1,17 @@ +use crate::state::*; +use anchor_lang::prelude::*; + +#[derive(Accounts)] +pub struct Close<'info> { + #[account(mut)] + pub payer: Option>, + #[account(mut, close = payer, constraint = system_program.is_some())] + pub optional_1: Option>, + #[account(mut, close = payer, has_one = optional_1, constraint = payer.is_some())] + pub optional_2: Option>, + pub system_program: Option>, +} + +pub fn handle_close(_ctx: Context) -> Result<()> { + Ok(()) +} diff --git a/tests/optional/programs/optional/src/processor/errors.rs b/tests/optional/programs/optional/src/processor/errors.rs new file mode 100644 index 0000000000..39f993e298 --- /dev/null +++ b/tests/optional/programs/optional/src/processor/errors.rs @@ -0,0 +1,7 @@ +use anchor_lang::prelude::*; + +#[error_code] +pub enum OptionalErrors { + #[msg("Failed realloc")] + ReallocFailed, +} diff --git a/tests/optional/programs/optional/src/processor/initialize.rs b/tests/optional/programs/optional/src/processor/initialize.rs new file mode 100644 index 0000000000..c36018793d --- /dev/null +++ b/tests/optional/programs/optional/src/processor/initialize.rs @@ -0,0 +1,36 @@ +use crate::state::*; +use anchor_lang::prelude::*; + +#[derive(Accounts)] +pub struct Initialize<'info> { + #[account(mut)] + pub payer: Option>, + #[account(zero)] + pub optional_2: Option>, + #[account(zero)] + pub required: Account<'info, Data2>, + #[account(init, seeds=[Data1::PREFIX.as_ref(), optional_2.unwrap().key().as_ref()], bump, payer=payer, space=Data1::LEN, constraint = payer.is_some())] + pub optional_1: Option>, + pub system_program: Option>, +} + +pub fn handle_initialize(ctx: Context, value: u64, key: Pubkey) -> Result<()> { + let optional_1 = &mut ctx.accounts.optional_1; + let optional_2 = &mut ctx.accounts.optional_2; + let required = &mut ctx.accounts.required; + + required.optional_1 = Pubkey::default(); + + if let Some(data) = optional_1 { + data.data = value; + } + if let Some(data2) = optional_2 { + if let Some(optional) = optional_1 { + data2.optional_1 = optional.key(); + } else { + data2.optional_1 = key; + } + } + + Ok(()) +} diff --git a/tests/optional/programs/optional/src/processor/mod.rs b/tests/optional/programs/optional/src/processor/mod.rs new file mode 100644 index 0000000000..37e0e374f7 --- /dev/null +++ b/tests/optional/programs/optional/src/processor/mod.rs @@ -0,0 +1,11 @@ +mod close; +mod errors; +mod initialize; +mod realloc; +mod update; + +pub use close::*; +pub use errors::*; +pub use initialize::*; +pub use realloc::*; +pub use update::*; diff --git a/tests/optional/programs/optional/src/processor/realloc.rs b/tests/optional/programs/optional/src/processor/realloc.rs new file mode 100644 index 0000000000..869a31c025 --- /dev/null +++ b/tests/optional/programs/optional/src/processor/realloc.rs @@ -0,0 +1,24 @@ +use crate::state::*; +use crate::OptionalErrors; +use anchor_lang::prelude::*; + +#[derive(Accounts)] +pub struct Realloc<'info> { + #[account(mut)] + pub payer: Signer<'info>, + #[account(mut, realloc = 20, realloc::payer = payer, realloc::zero = false)] + pub optional_1: Option>, + pub required: Account<'info, Data2>, + pub system_program: Option>, +} + +pub fn handle_realloc(ctx: Context) -> Result<()> { + let optional = &ctx.accounts.optional_1; + if let Some(acc) = optional { + let len = acc.to_account_info().data_len(); + if len != 20 { + return err!(OptionalErrors::ReallocFailed); + } + } + Ok(()) +} diff --git a/tests/optional/programs/optional/src/processor/update.rs b/tests/optional/programs/optional/src/processor/update.rs new file mode 100644 index 0000000000..878a2f13f8 --- /dev/null +++ b/tests/optional/programs/optional/src/processor/update.rs @@ -0,0 +1,25 @@ +use anchor_lang::prelude::*; + +use crate::state::*; + +#[derive(Accounts)] +pub struct Update<'info> { + #[account(mut)] + pub payer: Option>, + #[account(mut, seeds=[Data1::PREFIX.as_ref(), optional_2.as_ref().unwrap().key().as_ref()], bump)] + pub optional_1: Option>, + #[account(mut, constraint = payer.is_some())] + pub optional_2: Option>, + #[account(constraint = if optional_2.is_none() {optional_1.is_none()} else {true})] + pub required: Program<'info, System>, +} + +pub fn handle_update(ctx: Context, value: u64, key: Pubkey) -> Result<()> { + if let Some(data) = &mut ctx.accounts.optional_1 { + data.data = value; + }; + if let Some(data2) = &mut ctx.accounts.optional_2 { + data2.optional_1 = key; + }; + Ok(()) +} diff --git a/tests/optional/programs/optional/src/state.rs b/tests/optional/programs/optional/src/state.rs new file mode 100644 index 0000000000..7a45ed0f39 --- /dev/null +++ b/tests/optional/programs/optional/src/state.rs @@ -0,0 +1,20 @@ +use anchor_lang::prelude::*; + +#[account] +pub struct Data1 { + pub data: u64, +} + +impl Data1 { + pub const LEN: usize = 8 + 8; + pub const PREFIX: &'static str = "data1"; +} + +#[account] +pub struct Data2 { + pub optional_1: Pubkey, +} + +impl Data2 { + pub const LEN: usize = 8 + 32; +} diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index 495fa44547..2a457f6f35 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -1,66 +1,69 @@ import * as anchor from "@project-serum/anchor"; import { AnchorError, Program } from "@project-serum/anchor"; import { Optional } from "../target/types/optional"; -import { expect, assert } from "chai"; -import { SystemProgram } from "@solana/web3.js"; +import { assert, expect } from "chai"; +import { PublicKey, SystemProgram } from "@project-serum/anchor"; describe("Optional", () => { // configure the client to use the local cluster anchor.setProvider(anchor.AnchorProvider.env()); - let optional1 = anchor.web3.Keypair.generate(); - let optional2 = anchor.web3.Keypair.generate(); - - // optional program + let optional2Keypair = anchor.web3.Keypair.generate(); + let requiredKeypair1 = anchor.web3.Keypair.generate(); + let requiredKeypair2 = anchor.web3.Keypair.generate(); const program = anchor.workspace.Optional as Program; + // payer of the transactions const payer = (program.provider as anchor.AnchorProvider).wallet; it("initialize", async () => { + let createRequired = await program.account.data2.createInstruction( + requiredKeypair1 + ); await program.methods - .initialize(new anchor.BN(10), optional2.publicKey) - .accounts({ - payer: payer.publicKey, - systemProgram: SystemProgram.programId, - optional2: optional2.publicKey, - }) - .signers([optional2]) - .rpc(); - - let data1 = await program.account.data1.fetchNullable(optional1.publicKey); - let data2 = await program.account.data2.fetchNullable(optional2.publicKey); - - expect(data1).to.equal(null); - expect(data2.optional1.toString()).to.equal(optional2.publicKey.toString()); - - optional1 = anchor.web3.Keypair.generate(); - await program.methods - .initialize(new anchor.BN(10), optional2.publicKey) + .initialize(new anchor.BN(10), optional2Keypair.publicKey) + .preInstructions([createRequired]) .accounts({ payer: payer.publicKey, - optional1: optional1.publicKey, systemProgram: SystemProgram.programId, + required: requiredKeypair1.publicKey, + optional1: null, + optional2: null, }) - .signers([optional1]) + .signers([requiredKeypair1]) .rpc(); - data1 = await program.account.data1.fetchNullable(optional1.publicKey); - - expect(data1.data.toNumber()).to.equal(10); - }); + let required1 = await program.account.data2.fetchNullable( + requiredKeypair1.publicKey + ); + expect(required1.optional1.toString()).to.equal( + PublicKey.default.toString() + ); - it("realloc_with_constraints", async () => { + // optional pda + let seeds = [Buffer.from("data1"), optional2Keypair.publicKey.toBuffer()]; + let optional1Pubkey = PublicKey.findProgramAddressSync( + seeds, + program.programId + )[0]; + let createOptional2 = await program.account.data2.createInstruction( + optional2Keypair + ); + let createRequired2 = await program.account.data2.createInstruction( + requiredKeypair2 + ); try { await program.methods - .realloc() + .initialize(new anchor.BN(10), optional2Keypair.publicKey) + .preInstructions([createOptional2, createRequired2]) .accounts({ payer: payer.publicKey, - optional1: optional1.publicKey, - optional2: optional2.publicKey, systemProgram: SystemProgram.programId, + required: requiredKeypair2.publicKey, + optional1: optional1Pubkey, + optional2: null, }) + .signers([requiredKeypair2, optional2Keypair]) .rpc(); - - assert.ok(false); } catch (e) { assert.isTrue(e instanceof AnchorError); const err: AnchorError = e; @@ -69,42 +72,92 @@ describe("Optional", () => { assert.strictEqual(err.error.errorCode.number, 2001); } - optional1 = anchor.web3.Keypair.generate(); - optional2 = anchor.web3.Keypair.generate(); + let initValue = new anchor.BN(10); await program.methods - .initialize(new anchor.BN(10), optional2.publicKey) + .initialize(initValue, optional2Keypair.publicKey) + .preInstructions([createOptional2, createRequired2]) .accounts({ payer: payer.publicKey, - optional1: optional1.publicKey, - optional2: optional2.publicKey, systemProgram: SystemProgram.programId, + required: requiredKeypair2.publicKey, + optional1: optional1Pubkey, + optional2: optional2Keypair.publicKey, }) - .signers([optional1, optional2]) + .signers([requiredKeypair2, optional2Keypair]) .rpc(); - let data1 = await program.account.data1.fetchNullable(optional1.publicKey); - let data2 = await program.account.data2.fetchNullable(optional2.publicKey); - let data1_info = await program.account.data1.getAccountInfo( - optional1.publicKey + let required2 = await program.account.data2.fetchNullable( + requiredKeypair2.publicKey + ); + let optional1 = await program.account.data2.fetchNullable(optional1Pubkey); + let optional2 = await program.account.data2.fetchNullable( + optional2Keypair.publicKey ); - expect(data1.data.toNumber()).to.equal(10); - expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); - expect(data1_info.data.length).to.equal(16); - - await program.methods - .realloc() - .accounts({ - payer: payer.publicKey, - optional1: optional1.publicKey, - optional2: optional2.publicKey, - systemProgram: SystemProgram.programId, - }) - .rpc(); - - data1_info = await program.account.data1.getAccountInfo( - optional1.publicKey + expect(optional1.data).to.equal(initValue); + expect(optional2.optional1.toString()).to.equal(optional1Pubkey.toString()); + expect(required2.optional1.toString()).to.equal( + PublicKey.default.toString() ); - expect(data1_info.data.length).to.equal(20); }); + + // it("realloc_with_constraints", async () => { + // try { + // await program.methods + // .realloc() + // .accounts({ + // payer: payer.publicKey, + // optional1: optional1Pubkey, + // required: optional2Keypair.publicKey, + // systemProgram: null, + // }) + // .rpc(); + // + // assert.ok(false); + // } catch (e) { + // assert.isTrue(e instanceof AnchorError); + // const err: AnchorError = e; + // const errMsg = "A has one constraint was violated"; + // assert.strictEqual(err.error.errorMessage, errMsg); + // assert.strictEqual(err.error.errorCode.number, 2001); + // } + // + // optional1 = anchor.web3.Keypair.generate(); + // optional2 = anchor.web3.Keypair.generate(); + // await program.methods + // .initialize(new anchor.BN(10), optional2Keypair.publicKey) + // .accounts({ + // payer: payer.publicKey, + // optional1: optional1.publicKey, + // optional2: optional2Keypair.publicKey, + // systemProgram: SystemProgram.programId, + // }) + // .signers([optional1, optional2]) + // .rpc(); + // + // let data1 = await program.account.data1.fetchNullable(optional1.publicKey); + // let data2 = await program.account.data2.fetchNullable(optional2Keypair.publicKey); + // let data1_info = await program.account.data1.getAccountInfo( + // optional1.publicKey + // ); + // + // expect(data1.data.toNumber()).to.equal(10); + // expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); + // expect(data1_info.data.length).to.equal(16); + // + // await program.methods + // .realloc() + // .accounts({ + // payer: payer.publicKey, + // optional1: optional1.publicKey, + // required: optional2Keypair.publicKey, + // systemProgram: SystemProgram.programId, + // }) + // .rpc(); + // + // data1_info = await program.account.data1.getAccountInfo( + // optional1.publicKey + // ); + // expect(data1_info.data.length).to.equal(20); + // }); }); From 58b1af24d26bcd4d136ff2fef1540aa69f0aef08 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 29 Aug 2022 04:40:28 -0400 Subject: [PATCH 057/109] add new errors to ts client --- ts/packages/anchor/src/error.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ts/packages/anchor/src/error.ts b/ts/packages/anchor/src/error.ts index bb7fffa1fb..5cb671c51b 100644 --- a/ts/packages/anchor/src/error.ts +++ b/ts/packages/anchor/src/error.ts @@ -337,6 +337,7 @@ export const LangErrorCode = { ConstraintMintFreezeAuthority: 2017, ConstraintMintDecimals: 2018, ConstraintSpace: 2019, + ConstraintAccountIsNone: 2020, // Require. RequireViolated: 2500, @@ -442,6 +443,7 @@ export const LangErrorMessage = new Map([ "A mint decimals constraint was violated", ], [LangErrorCode.ConstraintSpace, "A space constraint was violated"], + [LangErrorCode.ConstraintAccountIsNone, "A required account for the constraint is None"], // Require. [LangErrorCode.RequireViolated, "A require expression was violated"], From a419b67208e6834a476bb276aa1e0c30dc536060 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 29 Aug 2022 04:46:44 -0400 Subject: [PATCH 058/109] add new errors to ts client --- tests/.ammanrc.js | 11 + tests/misc/programs/misc-optional/Cargo.toml | 22 + tests/misc/programs/misc-optional/Xargo.toml | 2 + .../programs/misc-optional/src/account.rs | 75 +++ .../programs/misc-optional/src/context.rs | 577 ++++++++++++++++++ .../misc/programs/misc-optional/src/event.rs | 34 ++ tests/misc/programs/misc-optional/src/lib.rs | 356 +++++++++++ ts/packages/anchor/src/error.ts | 5 +- 8 files changed, 1081 insertions(+), 1 deletion(-) create mode 100644 tests/.ammanrc.js create mode 100644 tests/misc/programs/misc-optional/Cargo.toml create mode 100644 tests/misc/programs/misc-optional/Xargo.toml create mode 100644 tests/misc/programs/misc-optional/src/account.rs create mode 100644 tests/misc/programs/misc-optional/src/context.rs create mode 100644 tests/misc/programs/misc-optional/src/event.rs create mode 100644 tests/misc/programs/misc-optional/src/lib.rs diff --git a/tests/.ammanrc.js b/tests/.ammanrc.js new file mode 100644 index 0000000000..4c33e7aca2 --- /dev/null +++ b/tests/.ammanrc.js @@ -0,0 +1,11 @@ +module.exports = { + validator: { + programs: [ + { + label: 'Optional Test Program', + programId: "FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG", + deployPath: "./optional/target/deploy/optional.so" + }, + ], + } +} \ No newline at end of file diff --git a/tests/misc/programs/misc-optional/Cargo.toml b/tests/misc/programs/misc-optional/Cargo.toml new file mode 100644 index 0000000000..a18e4c1c85 --- /dev/null +++ b/tests/misc/programs/misc-optional/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "misc" +version = "0.1.0" +description = "Created with Anchor" +rust-version = "1.56" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "misc" + +[features] +no-entrypoint = [] +no-idl = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } +anchor-spl = { path = "../../../../spl" } +misc2 = { path = "../misc2", features = ["cpi"] } +spl-associated-token-account = "~1.0.3" diff --git a/tests/misc/programs/misc-optional/Xargo.toml b/tests/misc/programs/misc-optional/Xargo.toml new file mode 100644 index 0000000000..1744f098ae --- /dev/null +++ b/tests/misc/programs/misc-optional/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] \ No newline at end of file diff --git a/tests/misc/programs/misc-optional/src/account.rs b/tests/misc/programs/misc-optional/src/account.rs new file mode 100644 index 0000000000..893dac256b --- /dev/null +++ b/tests/misc/programs/misc-optional/src/account.rs @@ -0,0 +1,75 @@ +use anchor_lang::prelude::*; + +macro_rules! size { + ($name: ident, $size:expr) => { + impl $name { + pub const LEN: usize = $size; + } + }; +} + +pub const MAX_SIZE: usize = 10; +pub const MAX_SIZE_U8: u8 = 11; + +#[account] +pub struct Data { + pub udata: u128, // 16 + pub idata: i128, // 16 +} +size!(Data, 32); + +#[account] +pub struct DataU16 { + pub data: u16, // 2 +} +size!(DataU16, 32); + +#[account] +pub struct DataI8 { + pub data: i8, // 1 +} +size!(DataI8, 1); + +#[account] +pub struct DataI16 { + pub data: i16, // 2 +} +size!(DataI16, 2); + +#[account(zero_copy)] +pub struct DataZeroCopy { + pub data: u16, // 2 + pub _padding: u8, // 1 + pub bump: u8, // 1 +} +size!(DataZeroCopy, 4); + +#[account] +pub struct DataWithFilter { + pub authority: Pubkey, // 32 + pub filterable: Pubkey, // 32 +} +size!(DataWithFilter, 64); + +#[account] +pub struct DataMultidimensionalArray { + pub data: [[u8; 10]; 10], // 100 +} +size!(DataMultidimensionalArray, 100); + +#[account] +pub struct DataConstArraySize { + pub data: [u8; MAX_SIZE], // 10 +} +size!(DataConstArraySize, MAX_SIZE); + +#[account] +pub struct DataConstCastArraySize { + pub data_one: [u8; MAX_SIZE as usize], + pub data_two: [u8; MAX_SIZE_U8 as usize], +} + +#[account] +pub struct DataMultidimensionalArrayConstSizes { + pub data: [[u8; MAX_SIZE_U8 as usize]; MAX_SIZE], +} diff --git a/tests/misc/programs/misc-optional/src/context.rs b/tests/misc/programs/misc-optional/src/context.rs new file mode 100644 index 0000000000..977ce68a8a --- /dev/null +++ b/tests/misc/programs/misc-optional/src/context.rs @@ -0,0 +1,577 @@ +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> { + #[account( + init, + seeds = [b"my-mint-seed".as_ref()], + bump, + payer = authority, + mint::decimals = 6, + mint::authority = authority, + )] + pub mint: Account<'info, Mint>, + #[account( + init, + seeds = [b"my-token-seed".as_ref()], + bump, + payer = authority, + token::mint = mint, + token::authority = authority, + )] + pub my_pda: Account<'info, TokenAccount>, + #[account(mut)] + /// CHECK: + pub authority: AccountInfo<'info>, + pub system_program: Program<'info, System>, + pub rent: Sysvar<'info, Rent>, + pub token_program: Program<'info, Token>, +} + +#[derive(Accounts)] +pub struct TestInitAssociatedToken<'info> { + #[account( + init, + associated_token::mint = mint, + payer = payer, + associated_token::authority = payer, + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, + pub associated_token_program: Program<'info, AssociatedToken>, +} + +#[derive(Accounts)] +pub struct TestValidateAssociatedToken<'info> { + #[account( + associated_token::mint = mint, + associated_token::authority = wallet, + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + /// CHECK: + pub wallet: AccountInfo<'info>, +} + +#[derive(Accounts)] +#[instruction(nonce: u8)] +pub struct TestInstructionConstraint<'info> { + #[account( + seeds = [b"my-seed", my_account.key.as_ref()], + bump = nonce, + )] + /// CHECK: + pub my_pda: AccountInfo<'info>, + /// CHECK: + pub my_account: AccountInfo<'info>, +} + +#[derive(Accounts)] +#[instruction(domain: String, seed: Vec, bump: u8)] +pub struct TestPdaInit<'info> { + #[account( + init, + seeds = [b"my-seed", domain.as_bytes(), foo.key.as_ref(), &seed], + bump, + payer = my_payer, + space = DataU16::LEN + 8 + )] + pub my_pda: Account<'info, DataU16>, + #[account(mut)] + pub my_payer: Signer<'info>, + /// CHECK: + pub foo: AccountInfo<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestPdaInitZeroCopy<'info> { + #[account( + init, + seeds = [b"my-seed".as_ref()], + bump, + payer = my_payer, + space = DataZeroCopy::LEN + 8 + )] + pub my_pda: AccountLoader<'info, DataZeroCopy>, + #[account(mut)] + pub my_payer: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestPdaMutZeroCopy<'info> { + #[account( + mut, + seeds = [b"my-seed".as_ref()], + bump = my_pda.load()?.bump, + )] + pub my_pda: AccountLoader<'info, DataZeroCopy>, + /// CHECK: + pub my_payer: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct Ctor {} + +#[derive(Accounts)] +pub struct RemainingAccounts {} + +#[derive(Accounts)] +pub struct Initialize<'info> { + #[account(zero)] + pub data: Account<'info, Data>, +} + +#[derive(Accounts)] +pub struct InitializeSkipRentExempt<'info> { + #[account(zero, rent_exempt = skip)] + pub data: Account<'info, Data>, +} + +#[derive(Accounts)] +pub struct InitializeNoRentExempt<'info> { + /// CHECK: + pub data: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestOwner<'info> { + #[account(owner = *misc.key)] + /// CHECK: + pub data: AccountInfo<'info>, + /// CHECK: + pub misc: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestExecutable<'info> { + #[account(executable)] + /// CHECK: + 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)] + pub data: Account<'info, Data>, + /// CHECK: + sol_dest: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestU16<'info> { + #[account(zero)] + pub my_account: Account<'info, DataU16>, +} + +#[derive(Accounts)] +pub struct TestI16<'info> { + #[account(zero)] + pub data: Account<'info, DataI16>, +} + +#[derive(Accounts)] +pub struct TestSimulate {} + +#[derive(Accounts)] +pub struct TestI8<'info> { + #[account(zero)] + pub data: Account<'info, DataI8>, +} + +#[derive(Accounts)] +pub struct TestInit<'info> { + #[account(init, payer = payer, space = DataI8::LEN + 8)] + pub data: Account<'info, DataI8>, + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestInitZeroCopy<'info> { + #[account(init, payer = payer, space = DataZeroCopy::LEN + 8)] + pub data: Loader<'info, DataZeroCopy>, + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestInitMint<'info> { + #[account(init, mint::decimals = 6, mint::authority = payer, mint::freeze_authority = payer, payer = payer, )] + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, +} + +#[derive(Accounts)] +pub struct TestInitToken<'info> { + #[account(init, token::mint = mint, token::authority = payer, payer = payer, )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, +} + +#[derive(Accounts)] +pub struct TestCompositePayer<'info> { + pub composite: TestInit<'info>, + #[account(init, payer = composite.payer, space = Data::LEN + 8)] + pub data: Account<'info, Data>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestFetchAll<'info> { + #[account(init, payer = authority, space = DataWithFilter::LEN + 8)] + pub data: Account<'info, DataWithFilter>, + #[account(mut)] + pub authority: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestInitWithEmptySeeds<'info> { + #[account(init, seeds = [], bump, payer = authority, space = Data::LEN + 8)] + pub pda: Account<'info, Data>, + #[account(mut)] + pub authority: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestEmptySeedsConstraint<'info> { + #[account(seeds = [], bump)] + /// CHECK: + pub pda: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct InitWithSpace<'info> { + #[account(init, payer = payer, space = DataU16::LEN + 8)] + pub data: Account<'info, DataU16>, + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestInitIfNeeded<'info> { + // intentionally using more space (+500) to check whether space is checked when using init_if_needed + #[account(init_if_needed, payer = payer, space = DataU16::LEN + 8 + 500)] + pub data: Account<'info, DataU16>, + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestInitIfNeededChecksOwner<'info> { + #[account(init_if_needed, payer = payer, space = 100, owner = *owner.key, seeds = [b"hello"], bump)] + /// CHECK: + pub data: UncheckedAccount<'info>, + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Program<'info, System>, + /// CHECK: + pub owner: AccountInfo<'info>, +} + +#[derive(Accounts)] +#[instruction(seed_data: String)] +pub struct TestInitIfNeededChecksSeeds<'info> { + #[account(init_if_needed, payer = payer, space = 100, seeds = [seed_data.as_bytes()], bump)] + /// CHECK: + pub data: UncheckedAccount<'info>, + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +#[instruction(decimals: u8)] +pub struct TestInitMintIfNeeded<'info> { + #[account(init_if_needed, mint::decimals = decimals, mint::authority = mint_authority, mint::freeze_authority = freeze_authority, payer = payer)] + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, + /// CHECK: + pub mint_authority: AccountInfo<'info>, + /// CHECK: + pub freeze_authority: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestInitTokenIfNeeded<'info> { + #[account(init_if_needed, token::mint = mint, token::authority = authority, payer = payer, )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, + /// CHECK: + pub authority: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestInitAssociatedTokenIfNeeded<'info> { + #[account( + init_if_needed, + payer = payer, + associated_token::mint = mint, + associated_token::authority = authority + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + #[account(mut)] + pub payer: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: Program<'info, System>, + pub token_program: Program<'info, Token>, + pub associated_token_program: Program<'info, AssociatedToken>, + /// CHECK: + pub authority: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestMultidimensionalArray<'info> { + #[account(zero)] + pub data: Account<'info, DataMultidimensionalArray>, +} + +#[derive(Accounts)] +pub struct TestConstArraySize<'info> { + #[account(zero)] + pub data: Account<'info, DataConstArraySize>, +} + +#[derive(Accounts)] +pub struct TestConstIxDataSize<'info> { + #[account(zero)] + pub data: Account<'info, DataConstArraySize>, +} + +#[derive(Accounts)] +pub struct TestMultidimensionalArrayConstSizes<'info> { + #[account(zero)] + pub data: Account<'info, DataMultidimensionalArrayConstSizes>, +} + +#[derive(Accounts)] +pub struct NoRentExempt<'info> { + /// CHECK: + pub data: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct EnforceRentExempt<'info> { + #[account(rent_exempt = enforce)] + /// CHECK: + pub data: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct InitDecreaseLamports<'info> { + #[account(init, payer = user, space = 1000)] + /// CHECK: + pub data: AccountInfo<'info>, + #[account(mut)] + pub user: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct InitIfNeededChecksRentExemption<'info> { + #[account(init_if_needed, payer = user, space = 1000)] + /// CHECK: + pub data: AccountInfo<'info>, + #[account(mut)] + pub user: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +#[instruction(bump: u8, second_bump: u8)] +pub struct TestProgramIdConstraint<'info> { + // not a real associated token account + // just deriving like this for testing purposes + #[account(seeds = [b"seed"], bump = bump, seeds::program = anchor_spl::associated_token::ID)] + /// CHECK: + first: AccountInfo<'info>, + + #[account(seeds = [b"seed"], bump = second_bump, seeds::program = crate::ID)] + /// CHECK: + second: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestProgramIdConstraintUsingFindPda<'info> { + // not a real associated token account + // just deriving like this for testing purposes + #[account(seeds = [b"seed"], bump, seeds::program = anchor_spl::associated_token::ID)] + /// CHECK: + first: AccountInfo<'info>, + + #[account(seeds = [b"seed"], bump, seeds::program = crate::ID)] + /// CHECK: + second: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestUnsafeFieldSafetyErrors<'info> { + #[doc = "test"] + /// CHECK: + pub data: UncheckedAccount<'info>, + #[account(mut)] + /// CHECK: + pub data_two: UncheckedAccount<'info>, + #[account( + seeds = [b"my-seed", signer.key.as_ref()], + bump + )] + /// CHECK: + pub data_three: UncheckedAccount<'info>, + /// CHECK: + pub data_four: UncheckedAccount<'info>, + pub signer: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct TestConstraintToken<'info> { + #[account( + token::mint = mint, + token::authority = payer + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + pub payer: Signer<'info>, +} + +#[derive(Accounts)] +pub struct TestAuthorityConstraint<'info> { + #[account( + token::mint = mint, + token::authority = fake_authority + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + pub fake_authority: AccountInfo<'info>, +} +#[derive(Accounts)] +pub struct TestOnlyAuthorityConstraint<'info> { + #[account( + token::authority = payer + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + pub payer: Signer<'info>, +} +#[derive(Accounts)] +pub struct TestOnlyMintConstraint<'info> { + #[account( + token::mint = mint, + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, +} + +#[derive(Accounts)] +#[instruction(decimals: u8)] +pub struct TestMintConstraint<'info> { + #[account( + mint::decimals = decimals, + mint::authority = mint_authority, + mint::freeze_authority = freeze_authority + )] + pub mint: Account<'info, Mint>, + pub mint_authority: AccountInfo<'info>, + pub freeze_authority: AccountInfo<'info>, +} + +#[derive(Accounts)] +#[instruction(decimals: u8)] +pub struct TestMintOnlyDecimalsConstraint<'info> { + #[account( + mint::decimals = decimals, + )] + pub mint: Account<'info, Mint>, +} + +#[derive(Accounts)] +pub struct TestMintAuthorityConstraint<'info> { + #[account( + mint::authority = mint_authority, + mint::freeze_authority = freeze_authority + )] + pub mint: Account<'info, Mint>, + pub mint_authority: AccountInfo<'info>, + pub freeze_authority: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestMintOneAuthorityConstraint<'info> { + #[account( + mint::authority = mint_authority, + )] + pub mint: Account<'info, Mint>, + pub mint_authority: AccountInfo<'info>, +} + +#[derive(Accounts)] +#[instruction(decimals: u8)] +pub struct TestMintMissMintAuthConstraint<'info> { + #[account( + mint::decimals = decimals, + mint::freeze_authority = freeze_authority, + )] + pub mint: Account<'info, Mint>, + pub freeze_authority: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestAssociatedToken<'info> { + #[account( + associated_token::mint = mint, + associated_token::authority = authority, + )] + pub token: Account<'info, TokenAccount>, + pub mint: Account<'info, Mint>, + pub authority: AccountInfo<'info>, +} diff --git a/tests/misc/programs/misc-optional/src/event.rs b/tests/misc/programs/misc-optional/src/event.rs new file mode 100644 index 0000000000..80b7dd8f88 --- /dev/null +++ b/tests/misc/programs/misc-optional/src/event.rs @@ -0,0 +1,34 @@ +use anchor_lang::prelude::*; + +pub const MAX_EVENT_SIZE: usize = 10; +pub const MAX_EVENT_SIZE_U8: u8 = 11; + +#[event] +pub struct E1 { + pub data: u32, +} + +#[event] +pub struct E2 { + pub data: u32, +} + +#[event] +pub struct E3 { + pub data: u32, +} + +#[event] +pub struct E4 { + pub data: Pubkey, +} + +#[event] +pub struct E5 { + pub data: [u8; MAX_EVENT_SIZE], +} + +#[event] +pub struct E6 { + pub data: [u8; MAX_EVENT_SIZE_U8 as usize], +} diff --git a/tests/misc/programs/misc-optional/src/lib.rs b/tests/misc/programs/misc-optional/src/lib.rs new file mode 100644 index 0000000000..44893563a1 --- /dev/null +++ b/tests/misc/programs/misc-optional/src/lib.rs @@ -0,0 +1,356 @@ +//! Misc example is a catchall program for testing unrelated features. +//! It's not too instructive/coherent by itself, so please see other examples. + +use account::MAX_SIZE; +use anchor_lang::prelude::*; +use context::*; +use event::*; +use misc2::Auth; + +mod account; +mod context; +mod event; + +declare_id!("3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh"); + +#[constant] +pub const BASE: u128 = 1_000_000; +#[constant] +pub const DECIMALS: u8 = 6; +pub const NO_IDL: u16 = 55; + +#[program] +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; + Ok(()) + } + + pub fn initialize_no_rent_exempt(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn initialize_skip_rent_exempt(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_owner(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_executable(_ctx: Context) -> Result<()> { + 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(()) + } + + pub fn test_simulate(_ctx: Context, data: u32) -> Result<()> { + emit!(E1 { data }); + emit!(E2 { data: 1234 }); + emit!(E3 { data: 9 }); + emit!(E5 { + data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + }); + emit!(E6 { + data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + }); + Ok(()) + } + + pub fn test_i8(ctx: Context, data: i8) -> Result<()> { + ctx.accounts.data.data = data; + Ok(()) + } + + pub fn test_i16(ctx: Context, data: i16) -> Result<()> { + ctx.accounts.data.data = data; + Ok(()) + } + + pub fn test_const_array_size(ctx: Context, data: u8) -> Result<()> { + ctx.accounts.data.data[0] = data; + Ok(()) + } + + pub fn test_const_ix_data_size( + ctx: Context, + data: [u8; MAX_SIZE], + ) -> Result<()> { + ctx.accounts.data.data = data; + Ok(()) + } + + pub fn test_close(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_instruction_constraint( + _ctx: Context, + _nonce: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_pda_init( + ctx: Context, + _domain: String, + _seed: Vec, + _bump: u8, + ) -> Result<()> { + ctx.accounts.my_pda.data = 6; + Ok(()) + } + + pub fn test_pda_init_zero_copy(ctx: Context) -> Result<()> { + let mut acc = ctx.accounts.my_pda.load_init()?; + acc.data = 9; + acc.bump = *ctx.bumps.get("my_pda").unwrap(); + Ok(()) + } + + pub fn test_pda_mut_zero_copy(ctx: Context) -> Result<()> { + let mut acc = ctx.accounts.my_pda.load_mut()?; + acc.data = 1234; + Ok(()) + } + + pub fn test_token_seeds_init(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn default<'info>( + _program_id: &Pubkey, + _accounts: &[AccountInfo<'info>], + _data: &[u8], + ) -> Result<()> { + Err(ProgramError::Custom(1234).into()) + } + + pub fn test_init(ctx: Context) -> Result<()> { + ctx.accounts.data.data = 3; + Ok(()) + } + + pub fn test_init_zero_copy(ctx: Context) -> Result<()> { + let mut data = ctx.accounts.data.load_init()?; + data.data = 10; + data.bump = 2; + Ok(()) + } + + pub fn test_init_mint(ctx: Context) -> Result<()> { + assert!(ctx.accounts.mint.decimals == 6); + Ok(()) + } + + pub fn test_init_token(ctx: Context) -> Result<()> { + assert!(ctx.accounts.token.mint == ctx.accounts.mint.key()); + Ok(()) + } + + pub fn test_composite_payer(ctx: Context) -> Result<()> { + ctx.accounts.composite.data.data = 1; + ctx.accounts.data.udata = 2; + ctx.accounts.data.idata = 3; + Ok(()) + } + + pub fn test_init_associated_token(ctx: Context) -> Result<()> { + assert!(ctx.accounts.token.mint == ctx.accounts.mint.key()); + Ok(()) + } + + pub fn test_validate_associated_token( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_fetch_all(ctx: Context, filterable: Pubkey) -> Result<()> { + ctx.accounts.data.authority = ctx.accounts.authority.key(); + ctx.accounts.data.filterable = filterable; + Ok(()) + } + + pub fn test_init_with_empty_seeds(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_empty_seeds_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_init_if_needed(ctx: Context, data: u16) -> Result<()> { + ctx.accounts.data.data = data; + Ok(()) + } + + pub fn test_init_if_needed_checks_owner( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_init_if_needed_checks_seeds( + _ctx: Context, + _seed_data: String, + ) -> Result<()> { + Ok(()) + } + + pub fn test_init_mint_if_needed( + _ctx: Context, + _decimals: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_init_token_if_needed(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_init_associated_token_if_needed( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn init_with_space(_ctx: Context, data: u16) -> Result<()> { + Ok(()) + } + + pub fn test_multidimensional_array( + ctx: Context, + data: [[u8; 10]; 10], + ) -> Result<()> { + ctx.accounts.data.data = data; + Ok(()) + } + + pub fn test_multidimensional_array_const_sizes( + ctx: Context, + data: [[u8; 11]; 10], + ) -> Result<()> { + ctx.accounts.data.data = data; + Ok(()) + } + + pub fn test_no_rent_exempt(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_enforce_rent_exempt(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn init_decrease_lamports(ctx: Context) -> Result<()> { + **ctx.accounts.data.try_borrow_mut_lamports()? -= 1; + **ctx.accounts.user.try_borrow_mut_lamports()? += 1; + Ok(()) + } + + pub fn init_if_needed_checks_rent_exemption( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_program_id_constraint( + _ctx: Context, + _bump: u8, + _second_bump: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_program_id_constraint_find_pda( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_token_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_token_auth_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_only_auth_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_only_mint_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_mint_constraint(_ctx: Context, _decimals: u8) -> Result<()> { + Ok(()) + } + + pub fn test_mint_only_decimals_constraint( + _ctx: Context, + _decimals: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_mint_only_auth_constraint( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_mint_only_one_auth_constraint( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_mint_miss_mint_auth_constraint( + _ctx: Context, + _decimals: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_associated_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } +} diff --git a/ts/packages/anchor/src/error.ts b/ts/packages/anchor/src/error.ts index 5cb671c51b..ca24f3146d 100644 --- a/ts/packages/anchor/src/error.ts +++ b/ts/packages/anchor/src/error.ts @@ -443,7 +443,10 @@ export const LangErrorMessage = new Map([ "A mint decimals constraint was violated", ], [LangErrorCode.ConstraintSpace, "A space constraint was violated"], - [LangErrorCode.ConstraintAccountIsNone, "A required account for the constraint is None"], + [ + LangErrorCode.ConstraintAccountIsNone, + "A required account for the constraint is None", + ], // Require. [LangErrorCode.RequireViolated, "A require expression was violated"], From b2aecdb2d73401832600a44a3ffaf6cc06f5d19b Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 29 Aug 2022 04:49:02 -0400 Subject: [PATCH 059/109] update optional test --- .../optional/src/{processor => }/errors.rs | 0 tests/optional/programs/optional/src/lib.rs | 2 + .../optional/src/processor/initialize.rs | 1 + .../programs/optional/src/processor/mod.rs | 2 - .../optional/src/processor/realloc.rs | 3 +- tests/optional/tests/optional.ts | 324 ++++++++++-------- tests/yarn.lock | 125 ++++++- 7 files changed, 284 insertions(+), 173 deletions(-) rename tests/optional/programs/optional/src/{processor => }/errors.rs (100%) diff --git a/tests/optional/programs/optional/src/processor/errors.rs b/tests/optional/programs/optional/src/errors.rs similarity index 100% rename from tests/optional/programs/optional/src/processor/errors.rs rename to tests/optional/programs/optional/src/errors.rs diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index f745986d6b..1f91688779 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -4,6 +4,8 @@ use anchor_lang::prelude::*; use processor::*; +pub mod errors; +pub use errors::OptionalErrors; pub mod processor; pub mod state; declare_id!("FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG"); diff --git a/tests/optional/programs/optional/src/processor/initialize.rs b/tests/optional/programs/optional/src/processor/initialize.rs index c36018793d..470ca32333 100644 --- a/tests/optional/programs/optional/src/processor/initialize.rs +++ b/tests/optional/programs/optional/src/processor/initialize.rs @@ -1,4 +1,5 @@ use crate::state::*; +use crate::OptionalErrors; use anchor_lang::prelude::*; #[derive(Accounts)] diff --git a/tests/optional/programs/optional/src/processor/mod.rs b/tests/optional/programs/optional/src/processor/mod.rs index 37e0e374f7..31c2c401e6 100644 --- a/tests/optional/programs/optional/src/processor/mod.rs +++ b/tests/optional/programs/optional/src/processor/mod.rs @@ -1,11 +1,9 @@ mod close; -mod errors; mod initialize; mod realloc; mod update; pub use close::*; -pub use errors::*; pub use initialize::*; pub use realloc::*; pub use update::*; diff --git a/tests/optional/programs/optional/src/processor/realloc.rs b/tests/optional/programs/optional/src/processor/realloc.rs index 869a31c025..3e7da9df3d 100644 --- a/tests/optional/programs/optional/src/processor/realloc.rs +++ b/tests/optional/programs/optional/src/processor/realloc.rs @@ -1,6 +1,7 @@ +use anchor_lang::prelude::*; + use crate::state::*; use crate::OptionalErrors; -use anchor_lang::prelude::*; #[derive(Accounts)] pub struct Realloc<'info> { diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index 2a457f6f35..af9bb6b6de 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -1,163 +1,185 @@ import * as anchor from "@project-serum/anchor"; -import { AnchorError, Program } from "@project-serum/anchor"; -import { Optional } from "../target/types/optional"; -import { assert, expect } from "chai"; -import { PublicKey, SystemProgram } from "@project-serum/anchor"; +import {AnchorError, LangErrorMessage, Program, ProgramError, web3} from "@project-serum/anchor"; +import {Optional} from "../target/types/optional"; +import {assert, expect} from "chai"; describe("Optional", () => { - // configure the client to use the local cluster - anchor.setProvider(anchor.AnchorProvider.env()); - let optional2Keypair = anchor.web3.Keypair.generate(); - let requiredKeypair1 = anchor.web3.Keypair.generate(); - let requiredKeypair2 = anchor.web3.Keypair.generate(); - const program = anchor.workspace.Optional as Program; + // configure the client to use the local cluster + anchor.setProvider(anchor.AnchorProvider.env()); + let optional2Keypair = web3.Keypair.generate(); + let requiredKeypair1 = web3.Keypair.generate(); + let requiredKeypair2 = web3.Keypair.generate(); + const program = anchor.workspace.Optional as Program; - // payer of the transactions - const payer = (program.provider as anchor.AnchorProvider).wallet; + // payer of the transactions + const payer = (program.provider as anchor.AnchorProvider).wallet; - it("initialize", async () => { - let createRequired = await program.account.data2.createInstruction( - requiredKeypair1 - ); - await program.methods - .initialize(new anchor.BN(10), optional2Keypair.publicKey) - .preInstructions([createRequired]) - .accounts({ - payer: payer.publicKey, - systemProgram: SystemProgram.programId, - required: requiredKeypair1.publicKey, - optional1: null, - optional2: null, - }) - .signers([requiredKeypair1]) - .rpc(); + it("initialize", async () => { + let createRequired = await program.account.data2.createInstruction( + requiredKeypair1 + ); + // await program.methods + // .initialize(new anchor.BN(10), optional2Keypair.publicKey) + // .preInstructions([createRequired]) + // .accounts({ + // payer: payer.publicKey, + // systemProgram: web3.SystemProgram.programId, + // required: requiredKeypair1.publicKey, + // optional1: null, + // optional2: null, + // }) + // .signers([requiredKeypair1]) + // .rpc({skipPreflight: true}); - let required1 = await program.account.data2.fetchNullable( - requiredKeypair1.publicKey - ); - expect(required1.optional1.toString()).to.equal( - PublicKey.default.toString() - ); + // let required1 = await program.account.data2.fetchNullable( + // requiredKeypair1.publicKey + // ); + // expect(required1.optional1.toString()).to.equal( + // web3.PublicKey.default.toString() + // ); - // optional pda - let seeds = [Buffer.from("data1"), optional2Keypair.publicKey.toBuffer()]; - let optional1Pubkey = PublicKey.findProgramAddressSync( - seeds, - program.programId - )[0]; - let createOptional2 = await program.account.data2.createInstruction( - optional2Keypair - ); - let createRequired2 = await program.account.data2.createInstruction( - requiredKeypair2 - ); - try { - await program.methods - .initialize(new anchor.BN(10), optional2Keypair.publicKey) - .preInstructions([createOptional2, createRequired2]) - .accounts({ - payer: payer.publicKey, - systemProgram: SystemProgram.programId, - required: requiredKeypair2.publicKey, - optional1: optional1Pubkey, - optional2: null, - }) - .signers([requiredKeypair2, optional2Keypair]) - .rpc(); - } catch (e) { - assert.isTrue(e instanceof AnchorError); - const err: AnchorError = e; - const errMsg = "A has one constraint was violated"; - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number, 2001); - } + // optional pda + let seeds = [Buffer.from("data1"), optional2Keypair.publicKey.toBuffer()]; + let optional1Pubkey = web3.PublicKey.findProgramAddressSync( + seeds, + program.programId + )[0]; + let createOptional2 = await program.account.data2.createInstruction( + optional2Keypair + ); + let createRequired2 = await program.account.data2.createInstruction( + requiredKeypair2 + ); + try { + await program.methods + .initialize(new anchor.BN(10), optional2Keypair.publicKey) + .preInstructions([createOptional2, createRequired2]) + .accountsStrict({ + payer: payer.publicKey, + systemProgram: web3.SystemProgram.programId, + required: requiredKeypair2.publicKey, + optional1: optional1Pubkey, + optional2: null, + }) + .signers([requiredKeypair2, optional2Keypair]) + .rpc(); + expect(false).to.be.true; + } catch (e) { + const errMsg1 = "ProgramFailedToComplete"; + const errMsg2 = "Program failed to complete"; + let error: string = e.toString(); + assert(error.includes(errMsg1) || error.includes(errMsg2), "Program didn't fail to complete!!"); + } - let initValue = new anchor.BN(10); - await program.methods - .initialize(initValue, optional2Keypair.publicKey) - .preInstructions([createOptional2, createRequired2]) - .accounts({ - payer: payer.publicKey, - systemProgram: SystemProgram.programId, - required: requiredKeypair2.publicKey, - optional1: optional1Pubkey, - optional2: optional2Keypair.publicKey, - }) - .signers([requiredKeypair2, optional2Keypair]) - .rpc(); + try { + await program.methods + .initialize(new anchor.BN(10), optional2Keypair.publicKey) + .preInstructions([createOptional2, createRequired2]) + .accounts({ + payer: null, + systemProgram: web3.SystemProgram.programId, + required: requiredKeypair2.publicKey, + optional1: optional1Pubkey, + optional2: optional2Keypair.publicKey, + }) + .signers([requiredKeypair2, optional2Keypair]) + .rpc({skipPreflight: true}); + expect(false).to.be.true; + } catch (e) { + console.log(e); + assert.isTrue(e instanceof ProgramError); + const err: ProgramError = e; + assert.strictEqual(err.msg, LangErrorMessage.get(2020)); + assert.strictEqual(err.code, 2020); + } - let required2 = await program.account.data2.fetchNullable( - requiredKeypair2.publicKey - ); - let optional1 = await program.account.data2.fetchNullable(optional1Pubkey); - let optional2 = await program.account.data2.fetchNullable( - optional2Keypair.publicKey - ); + let initValue = new anchor.BN(10); + await program.methods + .initialize(initValue, optional2Keypair.publicKey) + .preInstructions([createOptional2, createRequired2]) + .accounts({ + payer: payer.publicKey, + systemProgram: web3.SystemProgram.programId, + required: requiredKeypair2.publicKey, + optional1: optional1Pubkey, + optional2: optional2Keypair.publicKey, + }) + .signers([requiredKeypair2, optional2Keypair]) + .rpc({skipPreflight: true}); + console.log("here") - expect(optional1.data).to.equal(initValue); - expect(optional2.optional1.toString()).to.equal(optional1Pubkey.toString()); - expect(required2.optional1.toString()).to.equal( - PublicKey.default.toString() - ); - }); + let required2 = await program.account.data2.fetchNullable( + requiredKeypair2.publicKey + ); + let optional1 = await program.account.data1.fetchNullable(optional1Pubkey); + let optional2 = await program.account.data2.fetchNullable( + optional2Keypair.publicKey + ); - // it("realloc_with_constraints", async () => { - // try { - // await program.methods - // .realloc() - // .accounts({ - // payer: payer.publicKey, - // optional1: optional1Pubkey, - // required: optional2Keypair.publicKey, - // systemProgram: null, - // }) - // .rpc(); - // - // assert.ok(false); - // } catch (e) { - // assert.isTrue(e instanceof AnchorError); - // const err: AnchorError = e; - // const errMsg = "A has one constraint was violated"; - // assert.strictEqual(err.error.errorMessage, errMsg); - // assert.strictEqual(err.error.errorCode.number, 2001); - // } - // - // optional1 = anchor.web3.Keypair.generate(); - // optional2 = anchor.web3.Keypair.generate(); - // await program.methods - // .initialize(new anchor.BN(10), optional2Keypair.publicKey) - // .accounts({ - // payer: payer.publicKey, - // optional1: optional1.publicKey, - // optional2: optional2Keypair.publicKey, - // systemProgram: SystemProgram.programId, - // }) - // .signers([optional1, optional2]) - // .rpc(); - // - // let data1 = await program.account.data1.fetchNullable(optional1.publicKey); - // let data2 = await program.account.data2.fetchNullable(optional2Keypair.publicKey); - // let data1_info = await program.account.data1.getAccountInfo( - // optional1.publicKey - // ); - // - // expect(data1.data.toNumber()).to.equal(10); - // expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); - // expect(data1_info.data.length).to.equal(16); - // - // await program.methods - // .realloc() - // .accounts({ - // payer: payer.publicKey, - // optional1: optional1.publicKey, - // required: optional2Keypair.publicKey, - // systemProgram: SystemProgram.programId, - // }) - // .rpc(); - // - // data1_info = await program.account.data1.getAccountInfo( - // optional1.publicKey - // ); - // expect(data1_info.data.length).to.equal(20); - // }); + expect(optional1.data.toNumber()).to.equal(initValue.toNumber()); + expect(optional2.optional1.toString()).to.equal(optional1Pubkey.toString()); + expect(required2.optional1.toString()).to.equal( + web3.PublicKey.default.toString() + ); + }); + + // it("realloc_with_constraints", async () => { + // try { + // await program.methods + // .realloc() + // .accounts({ + // payer: payer.publicKey, + // optional1: optional1Pubkey, + // required: optional2Keypair.publicKey, + // systemProgram: null, + // }) + // .rpc({skipPreflight: true}); + // + // assert.ok(false); + // } catch (e) { + // assert.isTrue(e instanceof AnchorError); + // const err: AnchorError = e; + // const errMsg = "A has one constraint was violated"; + // assert.strictEqual(err.error.errorMessage, errMsg); + // assert.strictEqual(err.error.errorCode.number, 2001); + // } + // + // optional1 = web3.Keypair.generate(); + // optional2 = web3.Keypair.generate(); + // await program.methods + // .initialize(new anchor.BN(10), optional2Keypair.publicKey) + // .accounts({ + // payer: payer.publicKey, + // optional1: optional1.publicKey, + // optional2: optional2Keypair.publicKey, + // systemProgram: SystemProgram.programId, + // }) + // .signers([optional1, optional2]) + // .rpc({skipPreflight: true}); + // + // let data1 = await program.account.data1.fetchNullable(optional1.publicKey); + // let data2 = await program.account.data2.fetchNullable(optional2Keypair.publicKey); + // let data1_info = await program.account.data1.getAccountInfo( + // optional1.publicKey + // ); + // + // expect(data1.data.toNumber()).to.equal(10); + // expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); + // expect(data1_info.data.length).to.equal(16); + // + // await program.methods + // .realloc() + // .accounts({ + // payer: payer.publicKey, + // optional1: optional1.publicKey, + // required: optional2Keypair.publicKey, + // systemProgram: SystemProgram.programId, + // }) + // .rpc({skipPreflight: true}); + // + // data1_info = await program.account.data1.getAccountInfo( + // optional1.publicKey + // ); + // expect(data1_info.data.length).to.equal(20); + // }); }); diff --git a/tests/yarn.lock b/tests/yarn.lock index 0cbe7b634d..add4d922d1 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -9,6 +9,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.17.2": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" + integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== + dependencies: + regenerator-runtime "^0.13.4" + "@ethersproject/bytes@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" @@ -30,6 +37,21 @@ "@ethersproject/logger" "^5.5.0" hash.js "1.1.7" +"@noble/ed25519@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.0.tgz#583ac38340a479314b9e348d4572101ed9492f9d" + integrity sha512-LeAxFK0+181zQOhOUuKE8Jnd3duzYhDNd3iCLxpmzA5K+e4I1FdbrK3Ot0ZHBwZMeRD/6EojyUfTbpHZ+hkQHg== + +"@noble/hashes@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" + integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== + +"@noble/secp256k1@^1.6.3": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94" + integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== + "@project-serum/anchor@^0.11.1": version "0.11.1" resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.11.1.tgz#155bff2c70652eafdcfd5559c81a83bb19cec9ff" @@ -54,7 +76,7 @@ version "0.25.0" dependencies: "@project-serum/borsh" "^0.2.5" - "@solana/web3.js" "^1.36.0" + "@solana/web3.js" "^1.54.1" base64-js "^1.5.1" bn.js "^5.1.2" bs58 "^4.0.1" @@ -112,6 +134,13 @@ dependencies: buffer "~6.0.3" +"@solana/buffer-layout@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.0.tgz#75b1b11adc487234821c81dfae3119b73a5fd734" + integrity sha512-lR0EMP2HC3+Mxwd4YcnZb0smnaDw7Bl2IQWZiTevRH5ZZBZn6VRWn3/92E3qdU4SSImJkA6IDHawOHAnx/qUvQ== + dependencies: + buffer "~6.0.3" + "@solana/spl-token@^0.1.6", "@solana/spl-token@^0.1.8": version "0.1.8" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" @@ -144,25 +173,27 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solana/web3.js@^1.36.0": - version "1.36.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.36.0.tgz#79d7d5217b49b80139f4de68953adc5b9a9a264f" - integrity sha512-RNT1451iRR7TyW7EJKMCrH/0OXawIe4zVm0DWQASwXlR/u1jmW6FrmH0lujIh7cGTlfOVbH+2ZU9AVUPLBFzwA== +"@solana/web3.js@^1.54.1": + version "1.54.1" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.54.1.tgz#c39ffa598beaa6d761ab55c78263d874f4441e14" + integrity sha512-/PViPDGxF6oZZidcILndlm0MdbuzBouiQcqxrAfiBZ4lHMntLE4U75KhC+205EkVnkgCC4/prkjKVeSnbkfzrw== dependencies: "@babel/runtime" "^7.12.5" - "@ethersproject/sha2" "^5.5.0" - "@solana/buffer-layout" "^3.0.0" + "@noble/ed25519" "^1.7.0" + "@noble/hashes" "^1.1.2" + "@noble/secp256k1" "^1.6.3" + "@solana/buffer-layout" "^4.0.0" + bigint-buffer "^1.1.5" bn.js "^5.0.0" - borsh "^0.4.0" + borsh "^0.7.0" bs58 "^4.0.1" buffer "6.0.1" - cross-fetch "^3.1.4" + fast-stable-stringify "^1.0.0" jayson "^3.4.4" js-sha3 "^0.8.0" - rpc-websockets "^7.4.2" - secp256k1 "^4.0.2" + node-fetch "2" + rpc-websockets "^7.5.0" superstruct "^0.14.2" - tweetnacl "^1.0.0" "@types/bn.js@^4.11.5": version "4.11.6" @@ -309,11 +340,25 @@ base64-js@^1.3.1, base64-js@^1.5.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bigint-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" + integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA== + dependencies: + bindings "^1.3.0" + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bindings@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" @@ -324,6 +369,11 @@ bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== +bn.js@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + borsh@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.4.0.tgz#9dd6defe741627f1315eac2a73df61421f6ddb9f" @@ -334,6 +384,15 @@ borsh@^0.4.0: bs58 "^4.0.0" text-encoding-utf-8 "^1.0.2" +borsh@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" + integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA== + dependencies: + bn.js "^5.2.0" + bs58 "^4.0.0" + text-encoding-utf-8 "^1.0.2" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -630,6 +689,16 @@ eyes@^0.1.8: resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= +fast-stable-stringify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" + integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag== + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -1026,18 +1095,18 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-fetch@2.6.7: +node-fetch@2, node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" @@ -1137,6 +1206,19 @@ rpc-websockets@^7.4.2: bufferutil "^4.0.1" utf-8-validate "^5.0.2" +rpc-websockets@^7.5.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.0.tgz#bbeb87572e66703ff151e50af1658f98098e2748" + integrity sha512-9tIRi1uZGy7YmDjErf1Ax3wtqdSSLIlnmL5OtOzgd5eqPKbsPpwDP5whUDO2LQay3Xp0CcHlcNSGzacNRluBaQ== + dependencies: + "@babel/runtime" "^7.17.2" + eventemitter3 "^4.0.7" + uuid "^8.3.2" + ws "^8.5.0" + optionalDependencies: + bufferutil "^4.0.1" + utf-8-validate "^5.0.2" + safe-buffer@^5.0.1, safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -1344,7 +1426,7 @@ uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.0: +uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -1398,6 +1480,11 @@ ws@^7.4.5: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== +ws@^8.5.0: + version "8.8.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" + integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" From 90f23228643ffbbd43e1aec2438194d960b0d405 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 29 Aug 2022 04:49:17 -0400 Subject: [PATCH 060/109] update anchor ts client --- .../anchor/src/program/accounts-resolver.ts | 16 +++++++- ts/packages/anchor/src/program/context.ts | 4 +- .../src/program/namespace/instruction.ts | 32 ++++++++------- .../anchor/src/program/namespace/methods.ts | 26 +++++++++++-- ts/yarn.lock | 39 ++++++++++++++++++- 5 files changed, 96 insertions(+), 21 deletions(-) diff --git a/ts/packages/anchor/src/program/accounts-resolver.ts b/ts/packages/anchor/src/program/accounts-resolver.ts index e538538fe7..339ead2461 100644 --- a/ts/packages/anchor/src/program/accounts-resolver.ts +++ b/ts/packages/anchor/src/program/accounts-resolver.ts @@ -21,7 +21,7 @@ export class AccountsResolver> { constructor( private _args: Array, - private _accounts: { [name: string]: PublicKey }, + private _accounts: { [name: string]: PublicKey | null }, private _provider: Provider, private _programId: PublicKey, private _idlIx: AllInstructions, @@ -46,7 +46,7 @@ export class AccountsResolver> { const accountDesc = this._idlIx.accounts[k] as IdlAccount; const accountDescName = camelCase(accountDesc.name); - if (accountDesc.isOptional && !this._accounts[accountDescName]) { + if (accountDesc.isOptional && this._accounts[accountDescName] === null) { this._accounts[accountDescName] = this._programId; continue; } @@ -91,6 +91,15 @@ export class AccountsResolver> { } } + public isOptional(name: string): boolean { + const accountDesc = this._idlIx.accounts.find((acc) => acc.name === name); + if (accountDesc !== undefined && "isOptional" in accountDesc) { + return accountDesc.isOptional ?? false; + } else { + return false; + } + } + private async autoPopulatePda(accountDesc: IdlAccount) { if (!accountDesc.pda || !accountDesc.pda.seeds) throw new Error("Must have seeds"); @@ -170,6 +179,9 @@ export class AccountsResolver> { const fieldName = pathComponents[0]; const fieldPubkey = this._accounts[camelCase(fieldName)]; + if (fieldPubkey === null) { + throw new Error(`fieldPubkey is null`); + } // The seed is a pubkey of the account. if (pathComponents.length === 1) { diff --git a/ts/packages/anchor/src/program/context.ts b/ts/packages/anchor/src/program/context.ts index ca7b3406a7..d3d230dca2 100644 --- a/ts/packages/anchor/src/program/context.ts +++ b/ts/packages/anchor/src/program/context.ts @@ -1,7 +1,7 @@ import { AccountMeta, - Signer, ConfirmOptions, + Signer, TransactionInstruction, } from "@solana/web3.js"; import { Address } from "./common.js"; @@ -67,6 +67,8 @@ export type Accounts = { type Account = A extends IdlAccounts ? Accounts + : A extends { isOptional: true } + ? Address | null : Address; export function splitArgsAndCtx( diff --git a/ts/packages/anchor/src/program/namespace/instruction.ts b/ts/packages/anchor/src/program/namespace/instruction.ts index d65dcf535b..285b1eda2f 100644 --- a/ts/packages/anchor/src/program/namespace/instruction.ts +++ b/ts/packages/anchor/src/program/namespace/instruction.ts @@ -100,20 +100,26 @@ export default class InstructionNamespaceFactory { } else { const account: IdlAccount = acc as IdlAccount; let pubkey; - try { - pubkey = translateAddress(ctx[acc.name] as Address); - } catch (err) { - throw new Error( - `Wrong input type for account "${ - acc.name - }" in the instruction accounts object${ - ixName !== undefined ? ' for instruction "' + ixName + '"' : "" - }. Expected PublicKey or string.` - ); + if (ctx[acc.name] === null && account.isOptional) { + pubkey = programId; + } else { + try { + pubkey = translateAddress(ctx[acc.name] as Address); + } catch (err) { + throw new Error( + `Wrong input type for account "${ + acc.name + }" in the instruction accounts object${ + ixName !== undefined + ? ' for instruction "' + ixName + '"' + : "" + }. Expected PublicKey or string.` + ); + } } - const optionalFalse = account.isOptional && pubkey === programId; - const isWritable = account.isMut && !optionalFalse; - const isSigner = account.isSigner && !optionalFalse; + const optional = account.isOptional && pubkey === programId; + const isWritable = account.isMut && !optional; + const isSigner = account.isSigner && !optional; return { pubkey, isWritable, diff --git a/ts/packages/anchor/src/program/namespace/methods.ts b/ts/packages/anchor/src/program/namespace/methods.ts index 4cf88260b6..bd59762735 100644 --- a/ts/packages/anchor/src/program/namespace/methods.ts +++ b/ts/packages/anchor/src/program/namespace/methods.ts @@ -9,7 +9,7 @@ import { } from "@solana/web3.js"; import { SimulateResponse } from "./simulate.js"; import { TransactionFn } from "./transaction.js"; -import { Idl } from "../../idl.js"; +import { Idl, IdlAccount, idlAddress } from "../../idl.js"; import { AllInstructions, MethodsFn, @@ -59,7 +59,7 @@ export class MethodsBuilderFactory { } export class MethodsBuilder> { - private readonly _accounts: { [name: string]: PublicKey } = {}; + private readonly _accounts: { [name: string]: PublicKey | null } = {}; private _remainingAccounts: Array = []; private _signers: Array = []; private _preInstructions: Array = []; @@ -102,7 +102,16 @@ export class MethodsBuilder> { accounts: Partial> ): MethodsBuilder { this._autoResolveAccounts = true; - Object.assign(this._accounts, accounts); + for (const accountName in accounts) { + if (accounts[accountName]) { + this._accounts[accountName] = accounts[accountName]; + } else if ( + accounts[accountName] === null && + this._accountsResolver.isOptional(accountName) + ) { + this._accounts[accountName] = null; + } + } return this; } @@ -110,7 +119,16 @@ export class MethodsBuilder> { accounts: Accounts ): MethodsBuilder { this._autoResolveAccounts = false; - Object.assign(this._accounts, accounts); + for (const accountName in accounts) { + if (accounts[accountName]) { + this._accounts[accountName] = accounts[accountName]; + } else if ( + accounts[accountName] === null && + this._accountsResolver.isOptional(accountName) + ) { + this._accounts[accountName] = null; + } + } return this; } diff --git a/ts/yarn.lock b/ts/yarn.lock index e24e81d6a3..12d801c097 100644 --- a/ts/yarn.lock +++ b/ts/yarn.lock @@ -746,6 +746,21 @@ "@solana/buffer-layout" "=4.0.0" "@solana/buffer-layout-utils" "=0.2.0" +"@noble/ed25519@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.0.tgz#583ac38340a479314b9e348d4572101ed9492f9d" + integrity sha512-LeAxFK0+181zQOhOUuKE8Jnd3duzYhDNd3iCLxpmzA5K+e4I1FdbrK3Ot0ZHBwZMeRD/6EojyUfTbpHZ+hkQHg== + +"@noble/hashes@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" + integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== + +"@noble/secp256k1@^1.6.3": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94" + integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -889,7 +904,7 @@ dependencies: buffer "~6.0.3" -"@solana/web3.js@*", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0": +"@solana/web3.js@*", "@solana/web3.js@^1.32.0": version "1.53.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.53.0.tgz#24a6341e4026fc2b993656141361c54bec917c07" integrity sha512-QyQDA9U5b+AiTo1ANsj9WihWWECeLv6VRpiTE7xPe5hLYANXZYecnlLglNiEzVgRg/jLvR5DrCISXhHx/mAEJw== @@ -912,6 +927,28 @@ superstruct "^0.14.2" tweetnacl "^1.0.3" +"@solana/web3.js@^1.36.0": + version "1.54.1" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.54.1.tgz#c39ffa598beaa6d761ab55c78263d874f4441e14" + integrity sha512-/PViPDGxF6oZZidcILndlm0MdbuzBouiQcqxrAfiBZ4lHMntLE4U75KhC+205EkVnkgCC4/prkjKVeSnbkfzrw== + dependencies: + "@babel/runtime" "^7.12.5" + "@noble/ed25519" "^1.7.0" + "@noble/hashes" "^1.1.2" + "@noble/secp256k1" "^1.6.3" + "@solana/buffer-layout" "^4.0.0" + bigint-buffer "^1.1.5" + bn.js "^5.0.0" + borsh "^0.7.0" + bs58 "^4.0.1" + buffer "6.0.1" + fast-stable-stringify "^1.0.0" + jayson "^3.4.4" + js-sha3 "^0.8.0" + node-fetch "2" + rpc-websockets "^7.5.0" + superstruct "^0.14.2" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" From f32955dac1c8145153faa8f7ffa66b6494eb437c Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 29 Aug 2022 05:39:30 -0400 Subject: [PATCH 061/109] update misc crate --- tests/misc/programs/misc-optional/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/misc/programs/misc-optional/Cargo.toml b/tests/misc/programs/misc-optional/Cargo.toml index a18e4c1c85..722320ebd4 100644 --- a/tests/misc/programs/misc-optional/Cargo.toml +++ b/tests/misc/programs/misc-optional/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "misc" +name = "misc-optional" version = "0.1.0" description = "Created with Anchor" rust-version = "1.56" @@ -7,7 +7,7 @@ edition = "2021" [lib] crate-type = ["cdylib", "lib"] -name = "misc" +name = "misc-optional" [features] no-entrypoint = [] From de8d7c19573678dc4365a4d0ade628dfb5c83fe6 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 29 Aug 2022 05:44:25 -0400 Subject: [PATCH 062/109] linting --- tests/optional/tests/optional.ts | 370 ++++++++++++++++++------------- 1 file changed, 216 insertions(+), 154 deletions(-) diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index af9bb6b6de..76afa5296f 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -1,129 +1,209 @@ import * as anchor from "@project-serum/anchor"; -import {AnchorError, LangErrorMessage, Program, ProgramError, web3} from "@project-serum/anchor"; -import {Optional} from "../target/types/optional"; -import {assert, expect} from "chai"; +import { + AnchorError, + LangErrorCode, + LangErrorMessage, + Program, + ProgramError, + web3, +} from "@project-serum/anchor"; +import { Optional } from "../target/types/optional"; +import { assert, expect } from "chai"; describe("Optional", () => { - // configure the client to use the local cluster - anchor.setProvider(anchor.AnchorProvider.env()); - let optional2Keypair = web3.Keypair.generate(); - let requiredKeypair1 = web3.Keypair.generate(); - let requiredKeypair2 = web3.Keypair.generate(); - const program = anchor.workspace.Optional as Program; + // configure the client to use the local cluster + anchor.setProvider(anchor.AnchorProvider.env()); + let optional2Keypair = web3.Keypair.generate(); + let requiredKeypair1 = web3.Keypair.generate(); + let requiredKeypair2 = web3.Keypair.generate(); + const program = anchor.workspace.Optional as Program; - // payer of the transactions - const payer = (program.provider as anchor.AnchorProvider).wallet; + // payer of the transactions + const payer = (program.provider as anchor.AnchorProvider).wallet; + let initValue = new anchor.BN(10); - it("initialize", async () => { - let createRequired = await program.account.data2.createInstruction( - requiredKeypair1 - ); - // await program.methods - // .initialize(new anchor.BN(10), optional2Keypair.publicKey) - // .preInstructions([createRequired]) - // .accounts({ - // payer: payer.publicKey, - // systemProgram: web3.SystemProgram.programId, - // required: requiredKeypair1.publicKey, - // optional1: null, - // optional2: null, - // }) - // .signers([requiredKeypair1]) - // .rpc({skipPreflight: true}); + // optional pda + let seeds = [Buffer.from("data1"), optional2Keypair.publicKey.toBuffer()]; + let optional1Pubkey = web3.PublicKey.findProgramAddressSync( + seeds, + program.programId + )[0]; + let createOptional2; + let createRequired2; - // let required1 = await program.account.data2.fetchNullable( - // requiredKeypair1.publicKey - // ); - // expect(required1.optional1.toString()).to.equal( - // web3.PublicKey.default.toString() - // ); + it("Initialize with optionals null works", async () => { + let createRequired = await program.account.data2.createInstruction( + requiredKeypair1 + ); + await program.methods + .initialize(new anchor.BN(10), optional2Keypair.publicKey) + .preInstructions([createRequired]) + .accounts({ + payer: payer.publicKey, + systemProgram: web3.SystemProgram.programId, + required: requiredKeypair1.publicKey, + optional1: null, + optional2: null, + }) + .signers([requiredKeypair1]) + .rpc({ skipPreflight: true }); - // optional pda - let seeds = [Buffer.from("data1"), optional2Keypair.publicKey.toBuffer()]; - let optional1Pubkey = web3.PublicKey.findProgramAddressSync( - seeds, - program.programId - )[0]; - let createOptional2 = await program.account.data2.createInstruction( - optional2Keypair - ); - let createRequired2 = await program.account.data2.createInstruction( - requiredKeypair2 - ); - try { - await program.methods - .initialize(new anchor.BN(10), optional2Keypair.publicKey) - .preInstructions([createOptional2, createRequired2]) - .accountsStrict({ - payer: payer.publicKey, - systemProgram: web3.SystemProgram.programId, - required: requiredKeypair2.publicKey, - optional1: optional1Pubkey, - optional2: null, - }) - .signers([requiredKeypair2, optional2Keypair]) - .rpc(); - expect(false).to.be.true; - } catch (e) { - const errMsg1 = "ProgramFailedToComplete"; - const errMsg2 = "Program failed to complete"; - let error: string = e.toString(); - assert(error.includes(errMsg1) || error.includes(errMsg2), "Program didn't fail to complete!!"); - } + let required1 = await program.account.data2.fetchNullable( + requiredKeypair1.publicKey + ); + expect(required1.optional1.toString()).to.equal( + web3.PublicKey.default.toString() + ); + }); - try { - await program.methods - .initialize(new anchor.BN(10), optional2Keypair.publicKey) - .preInstructions([createOptional2, createRequired2]) - .accounts({ - payer: null, - systemProgram: web3.SystemProgram.programId, - required: requiredKeypair2.publicKey, - optional1: optional1Pubkey, - optional2: optional2Keypair.publicKey, - }) - .signers([requiredKeypair2, optional2Keypair]) - .rpc({skipPreflight: true}); - expect(false).to.be.true; - } catch (e) { - console.log(e); - assert.isTrue(e instanceof ProgramError); - const err: ProgramError = e; - assert.strictEqual(err.msg, LangErrorMessage.get(2020)); - assert.strictEqual(err.code, 2020); - } + it("Initialize missing optional2 fails", async () => { + try { + let x = await program.methods + .initialize(initValue, optional2Keypair.publicKey) + .preInstructions([await createRequired2]) + .accounts({ + payer: payer.publicKey, + systemProgram: web3.SystemProgram.programId, + required: requiredKeypair2.publicKey, + optional1: optional1Pubkey, + optional2: null, + }) + .signers([requiredKeypair2]) + .transaction(); + console.log("hi"); + assert.ok(false); + } catch (e) { + const errMsg1 = "ProgramFailedToComplete"; + const errMsg2 = "Program failed to complete"; + let error: string = e.toString(); + console.log("Error:", error); + assert( + error.includes(errMsg1) || error.includes(errMsg2), + "Program didn't fail to complete!!" + ); + } + }); - let initValue = new anchor.BN(10); - await program.methods - .initialize(initValue, optional2Keypair.publicKey) - .preInstructions([createOptional2, createRequired2]) - .accounts({ - payer: payer.publicKey, - systemProgram: web3.SystemProgram.programId, - required: requiredKeypair2.publicKey, - optional1: optional1Pubkey, - optional2: optional2Keypair.publicKey, - }) - .signers([requiredKeypair2, optional2Keypair]) - .rpc({skipPreflight: true}); - console.log("here") + it("Initialize missing payer fails", async () => { + createOptional2 = await program.account.data2.createInstruction( + optional2Keypair + ); + createRequired2 = await program.account.data2.createInstruction( + requiredKeypair2 + ); - let required2 = await program.account.data2.fetchNullable( - requiredKeypair2.publicKey - ); - let optional1 = await program.account.data1.fetchNullable(optional1Pubkey); - let optional2 = await program.account.data2.fetchNullable( - optional2Keypair.publicKey - ); + try { + await program.methods + .initialize(initValue, optional2Keypair.publicKey) + .preInstructions([createOptional2, createRequired2]) + .accounts({ + payer: null, + systemProgram: web3.SystemProgram.programId, + required: requiredKeypair2.publicKey, + optional1: optional1Pubkey, + optional2: optional2Keypair.publicKey, + }) + .signers([requiredKeypair2, optional2Keypair]) + .rpc({ skipPreflight: true }); + assert.ok(false); + } catch (e) { + assert.isTrue(e instanceof ProgramError); + const err: ProgramError = e; + assert.strictEqual( + err.msg, + LangErrorMessage.get(LangErrorCode.ConstraintAccountIsNone) + ); + assert.strictEqual(err.code, LangErrorCode.ConstraintAccountIsNone); + } + }); - expect(optional1.data.toNumber()).to.equal(initValue.toNumber()); - expect(optional2.optional1.toString()).to.equal(optional1Pubkey.toString()); - expect(required2.optional1.toString()).to.equal( - web3.PublicKey.default.toString() - ); - }); + it("Initialize with bad PDA fails", async () => { + try { + // bad optional pda + let seeds = [ + Buffer.from("fakedata1"), + optional2Keypair.publicKey.toBuffer(), + ]; + let badOptional1Pubkey = web3.PublicKey.findProgramAddressSync( + seeds, + program.programId + )[0]; + await program.methods + .initialize(initValue, optional2Keypair.publicKey) + .preInstructions([createOptional2, createRequired2]) + .accounts({ + systemProgram: web3.SystemProgram.programId, + required: requiredKeypair2.publicKey, + optional1: badOptional1Pubkey, + optional2: optional2Keypair.publicKey, + }) + .signers([requiredKeypair2, optional2Keypair]) + .rpc({ skipPreflight: true }); + assert.ok(false); + } catch (e) { + assert.isTrue(e instanceof ProgramError); + const err: ProgramError = e; + assert.strictEqual( + err.msg, + LangErrorMessage.get(LangErrorCode.ConstraintSeeds) + ); + assert.strictEqual(err.code, LangErrorCode.ConstraintSeeds); + } + }); - // it("realloc_with_constraints", async () => { + it("Initialize with all valid accounts works", async () => { + await program.methods + .initialize(initValue, optional2Keypair.publicKey) + .preInstructions([createOptional2, createRequired2]) + .accounts({ + payer: payer.publicKey, + systemProgram: web3.SystemProgram.programId, + required: requiredKeypair2.publicKey, + optional1: optional1Pubkey, + optional2: optional2Keypair.publicKey, + }) + .signers([requiredKeypair2, optional2Keypair]) + .rpc({ skipPreflight: true }); + + let required2 = await program.account.data2.fetchNullable( + requiredKeypair2.publicKey + ); + let optional1 = await program.account.data1.fetchNullable(optional1Pubkey); + let optional2 = await program.account.data2.fetchNullable( + optional2Keypair.publicKey + ); + + expect(optional1.data.toNumber()).to.equal(initValue.toNumber()); + expect(optional2.optional1.toString()).to.equal(optional1Pubkey.toString()); + expect(required2.optional1.toString()).to.equal( + web3.PublicKey.default.toString() + ); + }); + + it("realloc_with_constraints", async () => { + try { + await program.methods + .realloc() + .accounts({ + payer: payer.publicKey, + optional1: optional1Pubkey, + required: optional2Keypair.publicKey, + systemProgram: null, + }) + .rpc({ skipPreflight: true }); + + assert.ok(false); + } catch (e) { + assert.isTrue(e instanceof AnchorError); + const err: AnchorError = e; + const errorCode = LangErrorCode.ConstraintHasOne; + assert.strictEqual( + err.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(err.error.errorCode.number, errorCode); + } + // // try { // await program.methods // .realloc() @@ -139,47 +219,29 @@ describe("Optional", () => { // } catch (e) { // assert.isTrue(e instanceof AnchorError); // const err: AnchorError = e; - // const errMsg = "A has one constraint was violated"; - // assert.strictEqual(err.error.errorMessage, errMsg); - // assert.strictEqual(err.error.errorCode.number, 2001); + // const errorCode = LangErrorCode.ConstraintHasOne; + // assert.strictEqual(err.error.errorMessage, LangErrorMessage.get(errorCode)); + // assert.strictEqual(err.error.errorCode.number, errorCode); // } // - // optional1 = web3.Keypair.generate(); - // optional2 = web3.Keypair.generate(); - // await program.methods - // .initialize(new anchor.BN(10), optional2Keypair.publicKey) - // .accounts({ - // payer: payer.publicKey, - // optional1: optional1.publicKey, - // optional2: optional2Keypair.publicKey, - // systemProgram: SystemProgram.programId, - // }) - // .signers([optional1, optional2]) - // .rpc({skipPreflight: true}); - // - // let data1 = await program.account.data1.fetchNullable(optional1.publicKey); - // let data2 = await program.account.data2.fetchNullable(optional2Keypair.publicKey); - // let data1_info = await program.account.data1.getAccountInfo( - // optional1.publicKey - // ); - // - // expect(data1.data.toNumber()).to.equal(10); - // expect(data2.optional1.toString()).to.equal(optional1.publicKey.toString()); - // expect(data1_info.data.length).to.equal(16); - // - // await program.methods - // .realloc() - // .accounts({ - // payer: payer.publicKey, - // optional1: optional1.publicKey, - // required: optional2Keypair.publicKey, - // systemProgram: SystemProgram.programId, - // }) - // .rpc({skipPreflight: true}); + // try { + // await program.methods + // .realloc() + // .accounts({ + // payer: payer.publicKey, + // optional1: optional1Pubkey, + // required: optional2Keypair.publicKey, + // systemProgram: null, + // }) + // .rpc({skipPreflight: true}); // - // data1_info = await program.account.data1.getAccountInfo( - // optional1.publicKey - // ); - // expect(data1_info.data.length).to.equal(20); - // }); + // assert.ok(false); + // } catch (e) { + // assert.isTrue(e instanceof AnchorError); + // const err: AnchorError = e; + // const errorCode = LangErrorCode.ConstraintHasOne; + // assert.strictEqual(err.error.errorMessage, LangErrorMessage.get(errorCode)); + // assert.strictEqual(err.error.errorCode.number, errorCode); + // } + }); }); From 5183b5215f65257d6a2a7e3f200891a754eff47a Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 29 Aug 2022 05:48:02 -0400 Subject: [PATCH 063/109] temporarily comment out optional rs tests --- client/example/src/main.rs | 77 ++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/client/example/src/main.rs b/client/example/src/main.rs index e34d81872e..c4e4e0f2cf 100644 --- a/client/example/src/main.rs +++ b/client/example/src/main.rs @@ -240,43 +240,46 @@ pub fn basic_4(client: &Client, pid: Pubkey) -> Result<()> { // // Make sure to run a localnet with the program deploy to run this example. fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { - // Program client. - let program = client.program(pid); - - // `Initialize` parameters. - let optional1 = Keypair::generate(&mut OsRng); - let optional2 = Keypair::generate(&mut OsRng); - - let initialize = OptionalInitialize { - payer: Some(program.payer()), - optional1: Some(optional1.pubkey()), - optional2: None, - system_program: Some(system_program::ID), - }; - - // Build and send a transaction. - program - .request() - .signer(&optional1) - .signer(&signer) - // .signer(&optional2) - .accounts(OptionalInitialize { - payer: Some(program.payer()), - optional1: Some(optional1.pubkey()), - optional2: None, - system_program: Some(system_program::ID), - }) - .args(optional_instruction::Initialize { - value: 10, - key: optional1.pubkey(), - }) - .send()?; - - // Assert the transaction worked. - let optional1_account: Data1 = program.account(optional1.pubkey())?; - assert_eq!(optional1_account.data, 10); - - println!("Optional success!"); + // // Program client. + // let program = client.program(pid); + // + // // `Initialize` parameters. + // let optional1 = Keypair::generate(&mut OsRng); + // let optional2 = Keypair::generate(&mut OsRng); + // + // let initialize = OptionalInitialize { + // payer: Some(program.payer()), + // optional_2: None, + // required: Default::default(), + // system_program: Some(system_program::ID), + // optional_1: None + // }; + // + // // Build and send a transaction. + // program + // .request() + // .instruction() + // .signer(&optional1) + // .signer(&signer) + // // .signer(&optional2) + // .accounts(OptionalInitialize { + // payer: Some(program.payer()), + // optional_2: None, + // required: Default::default(), + // optional_1: None, + // system_program: Some(system_program::ID), + // }) + // .args(optional_instruction::Initialize { + // value: 10, + // key: optional1.pubkey(), + // }) + // .send()?; + // + // // Assert the transaction worked. + // let optional1_account: Data1 = program.account(optional1.pubkey())?; + // assert_eq!(optional1_account.data, 10); + // + // println!("Optional success!"); Ok(()) } From 4e4debd48a09ba84dbbafef4d6f51f51c5168ab3 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sat, 24 Sep 2022 15:54:31 -0400 Subject: [PATCH 064/109] update ts --- .../anchor/src/program/accounts-resolver.ts | 26 +- .../anchor/src/program/namespace/methods.ts | 454 +++++++++--------- 2 files changed, 256 insertions(+), 224 deletions(-) diff --git a/ts/packages/anchor/src/program/accounts-resolver.ts b/ts/packages/anchor/src/program/accounts-resolver.ts index 006e39db9d..64ad86e2cb 100644 --- a/ts/packages/anchor/src/program/accounts-resolver.ts +++ b/ts/packages/anchor/src/program/accounts-resolver.ts @@ -24,7 +24,7 @@ import { AccountNamespace } from "./namespace/account.js"; import { coder } from "../spl/token"; import { BorshAccountsCoder } from "src/coder/index.js"; -type Accounts = { [name: string]: PublicKey | Accounts }; +type Accounts = { [name: string]: PublicKey | null | Accounts }; export type CustomAccountResolver = (params: { args: Array; @@ -95,6 +95,15 @@ export class AccountsResolver> { } } + public isOptional(name: string): boolean { + const accountDesc = this._idlIx.accounts.find((acc) => acc.name === name); + if (accountDesc !== undefined && "isOptional" in accountDesc) { + return accountDesc.isOptional ?? false; + } else { + return false; + } + } + private get(path: string[]): PublicKey | undefined { // Only return if pubkey const ret = path.reduce( @@ -137,6 +146,11 @@ export class AccountsResolver> { const accountDesc = accountDescOrAccounts as IdlAccount; const accountDescName = camelCase(accountDescOrAccounts.name); + if (accountDesc.isOptional && this._accounts[accountDescName] === null) { + this._accounts[accountDescName] = this._programId; + continue; + } + // Signers default to the provider. if (accountDesc.isSigner && !this.get([...path, accountDescName])) { // @ts-expect-error @@ -179,6 +193,7 @@ export class AccountsResolver> { const accountDescCasted: IdlAccount = accountDesc as IdlAccount; const accountDescName = camelCase(accountDesc.name); + // PDA derived from IDL seeds. if ( accountDescCasted.pda && @@ -239,6 +254,11 @@ export class AccountsResolver> { if (!accountDesc.pda || !accountDesc.pda.seeds) throw new Error("Must have seeds"); + if (accountDesc.isOptional && this._accounts[accountDesc.name] === null) { + this._accounts[accountDesc.name] = this._programId; + return; + } + const seeds: (Buffer | undefined)[] = await Promise.all( accountDesc.pda.seeds.map((seedDesc: IdlSeed) => this.toBuffer(seedDesc)) ); @@ -360,6 +380,10 @@ export class AccountsResolver> { const fieldName = pathComponents[0]; const fieldPubkey = this._accounts[camelCase(fieldName)]; + if (fieldPubkey === null) { + throw new Error(`fieldPubkey is null`); + } + // The seed is a pubkey of the account. if (pathComponents.length === 1) { return fieldPubkey; diff --git a/ts/packages/anchor/src/program/namespace/methods.ts b/ts/packages/anchor/src/program/namespace/methods.ts index 8a24708e71..e972a5f7d1 100644 --- a/ts/packages/anchor/src/program/namespace/methods.ts +++ b/ts/packages/anchor/src/program/namespace/methods.ts @@ -1,259 +1,267 @@ import { - AccountMeta, - ConfirmOptions, - PublicKey, - Signer, - Transaction, - TransactionInstruction, - TransactionSignature, + AccountMeta, + ConfirmOptions, + PublicKey, + Signer, + Transaction, + TransactionInstruction, + TransactionSignature, } from "@solana/web3.js"; -import { Idl, IdlTypeDef } from "../../idl.js"; +import {Idl, IdlTypeDef} from "../../idl.js"; import Provider from "../../provider.js"; -import { - AccountsResolver, - CustomAccountResolver, -} from "../accounts-resolver.js"; -import { Accounts } from "../context.js"; -import { AccountNamespace } from "./account.js"; -import { InstructionFn } from "./instruction.js"; -import { RpcFn } from "./rpc.js"; -import { SimulateFn, SimulateResponse } from "./simulate.js"; -import { TransactionFn } from "./transaction.js"; -import { - AllInstructions, - InstructionAccountAddresses, - MakeMethodsNamespace, - MethodsFn, -} from "./types.js"; -import { ViewFn } from "./views.js"; +import {AccountsResolver, CustomAccountResolver,} from "../accounts-resolver.js"; +import {Accounts} from "../context.js"; +import {AccountNamespace} from "./account.js"; +import {InstructionFn} from "./instruction.js"; +import {RpcFn} from "./rpc.js"; +import {SimulateFn, SimulateResponse} from "./simulate.js"; +import {TransactionFn} from "./transaction.js"; +import {AllInstructions, InstructionAccountAddresses, MakeMethodsNamespace, MethodsFn,} from "./types.js"; +import {ViewFn} from "./views.js"; -export type MethodsNamespace< - IDL extends Idl = Idl, - I extends AllInstructions = AllInstructions -> = MakeMethodsNamespace; +export type MethodsNamespace = AllInstructions> = MakeMethodsNamespace; export class MethodsBuilderFactory { - public static build>( - provider: Provider, - programId: PublicKey, - idlIx: AllInstructions, - ixFn: InstructionFn, - txFn: TransactionFn, - rpcFn: RpcFn, - simulateFn: SimulateFn, - viewFn: ViewFn | undefined, - accountNamespace: AccountNamespace, - idlTypes: IdlTypeDef[], - customResolver?: CustomAccountResolver - ): MethodsFn> { - return (...args) => - new MethodsBuilder( - args, - ixFn, - txFn, - rpcFn, - simulateFn, - viewFn, - provider, - programId, - idlIx, - accountNamespace, - idlTypes, - customResolver - ); - } + public static build>( + provider: Provider, + programId: PublicKey, + idlIx: AllInstructions, + ixFn: InstructionFn, + txFn: TransactionFn, + rpcFn: RpcFn, + simulateFn: SimulateFn, + viewFn: ViewFn | undefined, + accountNamespace: AccountNamespace, + idlTypes: IdlTypeDef[], + customResolver?: CustomAccountResolver + ): MethodsFn> { + return (...args) => + new MethodsBuilder( + args, + ixFn, + txFn, + rpcFn, + simulateFn, + viewFn, + provider, + programId, + idlIx, + accountNamespace, + idlTypes, + customResolver + ); + } } export class MethodsBuilder> { - private readonly _accounts: { [name: string]: PublicKey } = {}; - private _remainingAccounts: Array = []; - private _signers: Array = []; - private _preInstructions: Array = []; - private _postInstructions: Array = []; - private _accountsResolver: AccountsResolver; - private _autoResolveAccounts: boolean = true; - private _args: Array; - - constructor( - _args: Array, - private _ixFn: InstructionFn, - private _txFn: TransactionFn, - private _rpcFn: RpcFn, - private _simulateFn: SimulateFn, - private _viewFn: ViewFn | undefined, - _provider: Provider, - _programId: PublicKey, - _idlIx: AllInstructions, - _accountNamespace: AccountNamespace, - _idlTypes: IdlTypeDef[], - _customResolver?: CustomAccountResolver - ) { - this._args = _args; - this._accountsResolver = new AccountsResolver( - _args, - this._accounts, - _provider, - _programId, - _idlIx, - _accountNamespace, - _idlTypes, - _customResolver - ); - } + private readonly _accounts: { [name: string]: PublicKey | null } = {}; + private _remainingAccounts: Array = []; + private _signers: Array = []; + private _preInstructions: Array = []; + private _postInstructions: Array = []; + private _accountsResolver: AccountsResolver; + private _autoResolveAccounts: boolean = true; + private _args: Array; - public args(_args: Array): void { - this._args = _args; - this._accountsResolver.args(_args); - } + constructor( + _args: Array, + private _ixFn: InstructionFn, + private _txFn: TransactionFn, + private _rpcFn: RpcFn, + private _simulateFn: SimulateFn, + private _viewFn: ViewFn | undefined, + _provider: Provider, + _programId: PublicKey, + _idlIx: AllInstructions, + _accountNamespace: AccountNamespace, + _idlTypes: IdlTypeDef[], + _customResolver?: CustomAccountResolver + ) { + this._args = _args; + this._accountsResolver = new AccountsResolver( + _args, + this._accounts, + _provider, + _programId, + _idlIx, + _accountNamespace, + _idlTypes, + _customResolver + ); + } - public async pubkeys(): Promise< - Partial> - > { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); + public args(_args: Array): void { + this._args = _args; + this._accountsResolver.args(_args); } - return this._accounts as Partial>; - } - public accounts( - accounts: Partial> - ): MethodsBuilder { - this._autoResolveAccounts = true; - Object.assign(this._accounts, accounts); - return this; - } + public async pubkeys(): Promise>> { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); + } + return this._accounts as Partial>; + } - public accountsStrict( - accounts: Accounts - ): MethodsBuilder { - this._autoResolveAccounts = false; - Object.assign(this._accounts, accounts); - return this; - } + public accounts( + accounts: Partial> + ): MethodsBuilder { + this._autoResolveAccounts = true; + // Object.assign(this._accounts, accounts); + for (const accountName in accounts) { + if (accounts[accountName]) { + this._accounts[accountName] = accounts[accountName]; + } else if ( + accounts[accountName] === null && + this._accountsResolver.isOptional(accountName) + ) { + this._accounts[accountName] = null; + } + } + return this; + } - public signers(signers: Array): MethodsBuilder { - this._signers = this._signers.concat(signers); - return this; - } + public accountsStrict( + accounts: Accounts + ): MethodsBuilder { + this._autoResolveAccounts = false; + // Object.assign(this._accounts, accounts); + for (const accountName in accounts) { + if (accounts[accountName]) { + this._accounts[accountName] = accounts[accountName]; + } else if ( + accounts[accountName] === null && + this._accountsResolver.isOptional(accountName) + ) { + this._accounts[accountName] = null; + } + } + return this; + } - public remainingAccounts( - accounts: Array - ): MethodsBuilder { - this._remainingAccounts = this._remainingAccounts.concat(accounts); - return this; - } + public signers(signers: Array): MethodsBuilder { + this._signers = this._signers.concat(signers); + return this; + } - public preInstructions( - ixs: Array - ): MethodsBuilder { - this._preInstructions = this._preInstructions.concat(ixs); - return this; - } + public remainingAccounts( + accounts: Array + ): MethodsBuilder { + this._remainingAccounts = this._remainingAccounts.concat(accounts); + return this; + } - public postInstructions( - ixs: Array - ): MethodsBuilder { - this._postInstructions = this._postInstructions.concat(ixs); - return this; - } + public preInstructions( + ixs: Array + ): MethodsBuilder { + this._preInstructions = this._preInstructions.concat(ixs); + return this; + } - public async rpc(options?: ConfirmOptions): Promise { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); + public postInstructions( + ixs: Array + ): MethodsBuilder { + this._postInstructions = this._postInstructions.concat(ixs); + return this; } - // @ts-ignore - return this._rpcFn(...this._args, { - accounts: this._accounts, - signers: this._signers, - remainingAccounts: this._remainingAccounts, - preInstructions: this._preInstructions, - postInstructions: this._postInstructions, - options: options, - }); - } + public async rpc(options?: ConfirmOptions): Promise { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); + } - public async view(options?: ConfirmOptions): Promise { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); + // @ts-ignore + return this._rpcFn(...this._args, { + accounts: this._accounts, + signers: this._signers, + remainingAccounts: this._remainingAccounts, + preInstructions: this._preInstructions, + postInstructions: this._postInstructions, + options: options, + }); } - if (!this._viewFn) { - throw new Error("Method does not support views"); - } + public async view(options?: ConfirmOptions): Promise { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); + } - // @ts-ignore - return this._viewFn(...this._args, { - accounts: this._accounts, - signers: this._signers, - remainingAccounts: this._remainingAccounts, - preInstructions: this._preInstructions, - postInstructions: this._postInstructions, - options: options, - }); - } + if (!this._viewFn) { + throw new Error("Method does not support views"); + } - public async simulate( - options?: ConfirmOptions - ): Promise> { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); + // @ts-ignore + return this._viewFn(...this._args, { + accounts: this._accounts, + signers: this._signers, + remainingAccounts: this._remainingAccounts, + preInstructions: this._preInstructions, + postInstructions: this._postInstructions, + options: options, + }); } - // @ts-ignore - return this._simulateFn(...this._args, { - accounts: this._accounts, - signers: this._signers, - remainingAccounts: this._remainingAccounts, - preInstructions: this._preInstructions, - postInstructions: this._postInstructions, - options: options, - }); - } + public async simulate( + options?: ConfirmOptions + ): Promise> { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); + } - public async instruction(): Promise { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); + // @ts-ignore + return this._simulateFn(...this._args, { + accounts: this._accounts, + signers: this._signers, + remainingAccounts: this._remainingAccounts, + preInstructions: this._preInstructions, + postInstructions: this._postInstructions, + options: options, + }); } - // @ts-ignore - return this._ixFn(...this._args, { - accounts: this._accounts, - signers: this._signers, - remainingAccounts: this._remainingAccounts, - preInstructions: this._preInstructions, - postInstructions: this._postInstructions, - }); - } + public async instruction(): Promise { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); + } - /** - * Convenient shortcut to get instructions and pubkeys via - * const { pubkeys, instructions } = await prepare(); - */ - public async prepare(): Promise<{ - pubkeys: Partial>; - instruction: TransactionInstruction; - signers: Signer[]; - }> { - return { - instruction: await this.instruction(), - pubkeys: await this.pubkeys(), - signers: await this._signers, - }; - } + // @ts-ignore + return this._ixFn(...this._args, { + accounts: this._accounts, + signers: this._signers, + remainingAccounts: this._remainingAccounts, + preInstructions: this._preInstructions, + postInstructions: this._postInstructions, + }); + } - public async transaction(): Promise { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); + /** + * Convenient shortcut to get instructions and pubkeys via + * const { pubkeys, instructions } = await prepare(); + */ + public async prepare(): Promise<{ + pubkeys: Partial>; + instruction: TransactionInstruction; + signers: Signer[]; + }> { + return { + instruction: await this.instruction(), + pubkeys: await this.pubkeys(), + signers: await this._signers, + }; } - // @ts-ignore - return this._txFn(...this._args, { - accounts: this._accounts, - signers: this._signers, - remainingAccounts: this._remainingAccounts, - preInstructions: this._preInstructions, - postInstructions: this._postInstructions, - }); - } + public async transaction(): Promise { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); + } + + // @ts-ignore + return this._txFn(...this._args, { + accounts: this._accounts, + signers: this._signers, + remainingAccounts: this._remainingAccounts, + preInstructions: this._preInstructions, + postInstructions: this._postInstructions, + }); + } } From d7a127a13c8c95ec4a9cca89160c0bd34106efa6 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sat, 24 Sep 2022 15:55:45 -0400 Subject: [PATCH 065/109] remove local test files --- tests/.ammanrc.js | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 tests/.ammanrc.js diff --git a/tests/.ammanrc.js b/tests/.ammanrc.js deleted file mode 100644 index 4c33e7aca2..0000000000 --- a/tests/.ammanrc.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - validator: { - programs: [ - { - label: 'Optional Test Program', - programId: "FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG", - deployPath: "./optional/target/deploy/optional.so" - }, - ], - } -} \ No newline at end of file From 6a6822cdfc1ec42b431dacb52878d2c0df1b385c Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sat, 24 Sep 2022 16:05:48 -0400 Subject: [PATCH 066/109] linting --- .../anchor/src/program/namespace/methods.ts | 470 +++++++++--------- 1 file changed, 241 insertions(+), 229 deletions(-) diff --git a/ts/packages/anchor/src/program/namespace/methods.ts b/ts/packages/anchor/src/program/namespace/methods.ts index e972a5f7d1..d4194b05f8 100644 --- a/ts/packages/anchor/src/program/namespace/methods.ts +++ b/ts/packages/anchor/src/program/namespace/methods.ts @@ -1,267 +1,279 @@ import { - AccountMeta, - ConfirmOptions, - PublicKey, - Signer, - Transaction, - TransactionInstruction, - TransactionSignature, + AccountMeta, + ConfirmOptions, + PublicKey, + Signer, + Transaction, + TransactionInstruction, + TransactionSignature, } from "@solana/web3.js"; -import {Idl, IdlTypeDef} from "../../idl.js"; +import { Idl, IdlTypeDef } from "../../idl.js"; import Provider from "../../provider.js"; -import {AccountsResolver, CustomAccountResolver,} from "../accounts-resolver.js"; -import {Accounts} from "../context.js"; -import {AccountNamespace} from "./account.js"; -import {InstructionFn} from "./instruction.js"; -import {RpcFn} from "./rpc.js"; -import {SimulateFn, SimulateResponse} from "./simulate.js"; -import {TransactionFn} from "./transaction.js"; -import {AllInstructions, InstructionAccountAddresses, MakeMethodsNamespace, MethodsFn,} from "./types.js"; -import {ViewFn} from "./views.js"; +import { + AccountsResolver, + CustomAccountResolver, +} from "../accounts-resolver.js"; +import { Accounts } from "../context.js"; +import { AccountNamespace } from "./account.js"; +import { InstructionFn } from "./instruction.js"; +import { RpcFn } from "./rpc.js"; +import { SimulateFn, SimulateResponse } from "./simulate.js"; +import { TransactionFn } from "./transaction.js"; +import { + AllInstructions, + InstructionAccountAddresses, + MakeMethodsNamespace, + MethodsFn, +} from "./types.js"; +import { ViewFn } from "./views.js"; -export type MethodsNamespace = AllInstructions> = MakeMethodsNamespace; +export type MethodsNamespace< + IDL extends Idl = Idl, + I extends AllInstructions = AllInstructions +> = MakeMethodsNamespace; export class MethodsBuilderFactory { - public static build>( - provider: Provider, - programId: PublicKey, - idlIx: AllInstructions, - ixFn: InstructionFn, - txFn: TransactionFn, - rpcFn: RpcFn, - simulateFn: SimulateFn, - viewFn: ViewFn | undefined, - accountNamespace: AccountNamespace, - idlTypes: IdlTypeDef[], - customResolver?: CustomAccountResolver - ): MethodsFn> { - return (...args) => - new MethodsBuilder( - args, - ixFn, - txFn, - rpcFn, - simulateFn, - viewFn, - provider, - programId, - idlIx, - accountNamespace, - idlTypes, - customResolver - ); - } + public static build>( + provider: Provider, + programId: PublicKey, + idlIx: AllInstructions, + ixFn: InstructionFn, + txFn: TransactionFn, + rpcFn: RpcFn, + simulateFn: SimulateFn, + viewFn: ViewFn | undefined, + accountNamespace: AccountNamespace, + idlTypes: IdlTypeDef[], + customResolver?: CustomAccountResolver + ): MethodsFn> { + return (...args) => + new MethodsBuilder( + args, + ixFn, + txFn, + rpcFn, + simulateFn, + viewFn, + provider, + programId, + idlIx, + accountNamespace, + idlTypes, + customResolver + ); + } } export class MethodsBuilder> { - private readonly _accounts: { [name: string]: PublicKey | null } = {}; - private _remainingAccounts: Array = []; - private _signers: Array = []; - private _preInstructions: Array = []; - private _postInstructions: Array = []; - private _accountsResolver: AccountsResolver; - private _autoResolveAccounts: boolean = true; - private _args: Array; + private readonly _accounts: { [name: string]: PublicKey | null } = {}; + private _remainingAccounts: Array = []; + private _signers: Array = []; + private _preInstructions: Array = []; + private _postInstructions: Array = []; + private _accountsResolver: AccountsResolver; + private _autoResolveAccounts: boolean = true; + private _args: Array; - constructor( - _args: Array, - private _ixFn: InstructionFn, - private _txFn: TransactionFn, - private _rpcFn: RpcFn, - private _simulateFn: SimulateFn, - private _viewFn: ViewFn | undefined, - _provider: Provider, - _programId: PublicKey, - _idlIx: AllInstructions, - _accountNamespace: AccountNamespace, - _idlTypes: IdlTypeDef[], - _customResolver?: CustomAccountResolver - ) { - this._args = _args; - this._accountsResolver = new AccountsResolver( - _args, - this._accounts, - _provider, - _programId, - _idlIx, - _accountNamespace, - _idlTypes, - _customResolver - ); - } + constructor( + _args: Array, + private _ixFn: InstructionFn, + private _txFn: TransactionFn, + private _rpcFn: RpcFn, + private _simulateFn: SimulateFn, + private _viewFn: ViewFn | undefined, + _provider: Provider, + _programId: PublicKey, + _idlIx: AllInstructions, + _accountNamespace: AccountNamespace, + _idlTypes: IdlTypeDef[], + _customResolver?: CustomAccountResolver + ) { + this._args = _args; + this._accountsResolver = new AccountsResolver( + _args, + this._accounts, + _provider, + _programId, + _idlIx, + _accountNamespace, + _idlTypes, + _customResolver + ); + } - public args(_args: Array): void { - this._args = _args; - this._accountsResolver.args(_args); - } + public args(_args: Array): void { + this._args = _args; + this._accountsResolver.args(_args); + } - public async pubkeys(): Promise>> { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); - } - return this._accounts as Partial>; + public async pubkeys(): Promise< + Partial> + > { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); } + return this._accounts as Partial>; + } - public accounts( - accounts: Partial> - ): MethodsBuilder { - this._autoResolveAccounts = true; - // Object.assign(this._accounts, accounts); - for (const accountName in accounts) { - if (accounts[accountName]) { - this._accounts[accountName] = accounts[accountName]; - } else if ( - accounts[accountName] === null && - this._accountsResolver.isOptional(accountName) - ) { - this._accounts[accountName] = null; - } - } - return this; + public accounts( + accounts: Partial> + ): MethodsBuilder { + this._autoResolveAccounts = true; + // Object.assign(this._accounts, accounts); + for (const accountName in accounts) { + if (accounts[accountName]) { + this._accounts[accountName] = accounts[accountName]; + } else if ( + accounts[accountName] === null && + this._accountsResolver.isOptional(accountName) + ) { + this._accounts[accountName] = null; + } } + return this; + } - public accountsStrict( - accounts: Accounts - ): MethodsBuilder { - this._autoResolveAccounts = false; - // Object.assign(this._accounts, accounts); - for (const accountName in accounts) { - if (accounts[accountName]) { - this._accounts[accountName] = accounts[accountName]; - } else if ( - accounts[accountName] === null && - this._accountsResolver.isOptional(accountName) - ) { - this._accounts[accountName] = null; - } - } - return this; + public accountsStrict( + accounts: Accounts + ): MethodsBuilder { + this._autoResolveAccounts = false; + // Object.assign(this._accounts, accounts); + for (const accountName in accounts) { + if (accounts[accountName]) { + this._accounts[accountName] = accounts[accountName]; + } else if ( + accounts[accountName] === null && + this._accountsResolver.isOptional(accountName) + ) { + this._accounts[accountName] = null; + } } + return this; + } - public signers(signers: Array): MethodsBuilder { - this._signers = this._signers.concat(signers); - return this; - } + public signers(signers: Array): MethodsBuilder { + this._signers = this._signers.concat(signers); + return this; + } - public remainingAccounts( - accounts: Array - ): MethodsBuilder { - this._remainingAccounts = this._remainingAccounts.concat(accounts); - return this; - } + public remainingAccounts( + accounts: Array + ): MethodsBuilder { + this._remainingAccounts = this._remainingAccounts.concat(accounts); + return this; + } - public preInstructions( - ixs: Array - ): MethodsBuilder { - this._preInstructions = this._preInstructions.concat(ixs); - return this; - } - - public postInstructions( - ixs: Array - ): MethodsBuilder { - this._postInstructions = this._postInstructions.concat(ixs); - return this; - } + public preInstructions( + ixs: Array + ): MethodsBuilder { + this._preInstructions = this._preInstructions.concat(ixs); + return this; + } - public async rpc(options?: ConfirmOptions): Promise { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); - } + public postInstructions( + ixs: Array + ): MethodsBuilder { + this._postInstructions = this._postInstructions.concat(ixs); + return this; + } - // @ts-ignore - return this._rpcFn(...this._args, { - accounts: this._accounts, - signers: this._signers, - remainingAccounts: this._remainingAccounts, - preInstructions: this._preInstructions, - postInstructions: this._postInstructions, - options: options, - }); + public async rpc(options?: ConfirmOptions): Promise { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); } - public async view(options?: ConfirmOptions): Promise { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); - } + // @ts-ignore + return this._rpcFn(...this._args, { + accounts: this._accounts, + signers: this._signers, + remainingAccounts: this._remainingAccounts, + preInstructions: this._preInstructions, + postInstructions: this._postInstructions, + options: options, + }); + } - if (!this._viewFn) { - throw new Error("Method does not support views"); - } + public async view(options?: ConfirmOptions): Promise { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); + } - // @ts-ignore - return this._viewFn(...this._args, { - accounts: this._accounts, - signers: this._signers, - remainingAccounts: this._remainingAccounts, - preInstructions: this._preInstructions, - postInstructions: this._postInstructions, - options: options, - }); + if (!this._viewFn) { + throw new Error("Method does not support views"); } - public async simulate( - options?: ConfirmOptions - ): Promise> { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); - } + // @ts-ignore + return this._viewFn(...this._args, { + accounts: this._accounts, + signers: this._signers, + remainingAccounts: this._remainingAccounts, + preInstructions: this._preInstructions, + postInstructions: this._postInstructions, + options: options, + }); + } - // @ts-ignore - return this._simulateFn(...this._args, { - accounts: this._accounts, - signers: this._signers, - remainingAccounts: this._remainingAccounts, - preInstructions: this._preInstructions, - postInstructions: this._postInstructions, - options: options, - }); + public async simulate( + options?: ConfirmOptions + ): Promise> { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); } - public async instruction(): Promise { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); - } + // @ts-ignore + return this._simulateFn(...this._args, { + accounts: this._accounts, + signers: this._signers, + remainingAccounts: this._remainingAccounts, + preInstructions: this._preInstructions, + postInstructions: this._postInstructions, + options: options, + }); + } - // @ts-ignore - return this._ixFn(...this._args, { - accounts: this._accounts, - signers: this._signers, - remainingAccounts: this._remainingAccounts, - preInstructions: this._preInstructions, - postInstructions: this._postInstructions, - }); + public async instruction(): Promise { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); } - /** - * Convenient shortcut to get instructions and pubkeys via - * const { pubkeys, instructions } = await prepare(); - */ - public async prepare(): Promise<{ - pubkeys: Partial>; - instruction: TransactionInstruction; - signers: Signer[]; - }> { - return { - instruction: await this.instruction(), - pubkeys: await this.pubkeys(), - signers: await this._signers, - }; - } + // @ts-ignore + return this._ixFn(...this._args, { + accounts: this._accounts, + signers: this._signers, + remainingAccounts: this._remainingAccounts, + preInstructions: this._preInstructions, + postInstructions: this._postInstructions, + }); + } - public async transaction(): Promise { - if (this._autoResolveAccounts) { - await this._accountsResolver.resolve(); - } + /** + * Convenient shortcut to get instructions and pubkeys via + * const { pubkeys, instructions } = await prepare(); + */ + public async prepare(): Promise<{ + pubkeys: Partial>; + instruction: TransactionInstruction; + signers: Signer[]; + }> { + return { + instruction: await this.instruction(), + pubkeys: await this.pubkeys(), + signers: await this._signers, + }; + } - // @ts-ignore - return this._txFn(...this._args, { - accounts: this._accounts, - signers: this._signers, - remainingAccounts: this._remainingAccounts, - preInstructions: this._preInstructions, - postInstructions: this._postInstructions, - }); + public async transaction(): Promise { + if (this._autoResolveAccounts) { + await this._accountsResolver.resolve(); } + + // @ts-ignore + return this._txFn(...this._args, { + accounts: this._accounts, + signers: this._signers, + remainingAccounts: this._remainingAccounts, + preInstructions: this._preInstructions, + postInstructions: this._postInstructions, + }); + } } From f6e67ebd5afa4cc428bc3c6661deeb26dd765d73 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sat, 24 Sep 2022 17:06:22 -0400 Subject: [PATCH 067/109] optional client tests --- client/example/src/main.rs | 127 ++++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 52 deletions(-) diff --git a/client/example/src/main.rs b/client/example/src/main.rs index c4e4e0f2cf..b4097f34dc 100644 --- a/client/example/src/main.rs +++ b/client/example/src/main.rs @@ -1,3 +1,6 @@ +use std::rc::Rc; +use std::time::Duration; + use anchor_client::solana_sdk::commitment_config::CommitmentConfig; use anchor_client::solana_sdk::pubkey::Pubkey; use anchor_client::solana_sdk::signature::read_keypair_file; @@ -5,30 +8,28 @@ use anchor_client::solana_sdk::signature::{Keypair, Signer}; use anchor_client::solana_sdk::system_instruction; use anchor_client::{Client, Cluster, EventContext}; use anyhow::Result; -use solana_sdk::system_program; // The `accounts` and `instructions` modules are generated by the framework. use basic_2::accounts as basic_2_accounts; use basic_2::instruction as basic_2_instruction; use basic_2::Counter; -use events::instruction as events_instruction; -use events::MyEvent; // 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 clap::Parser; +use composite::{DummyA, DummyB}; // The `accounts` and `instructions` modules are generated by the framework. use composite::accounts::{Bar, CompositeUpdate, Foo, Initialize}; use composite::instruction as composite_instruction; -use composite::{DummyA, DummyB}; +use events::instruction as events_instruction; +use events::MyEvent; +use rand::rngs::OsRng; +use solana_sdk::system_program; + // The `accounts` and `instructions` modules are generated by the framework. -use anchor_client::anchor_lang::ToAccountMetas; -use optional::accounts::{Close, Initialize as OptionalInitialize, Realloc, Update}; +use optional::accounts::Initialize as OptionalInitialize; use optional::instruction as optional_instruction; -use optional::{Data1, Data2}; -use rand::rngs::OsRng; -use std::rc::Rc; -use std::time::Duration; +use optional::state::{Data1, Data2}; #[derive(Parser, Debug)] pub struct Opts { @@ -39,6 +40,8 @@ pub struct Opts { #[clap(long)] basic_4_pid: Pubkey, #[clap(long)] + events_pid: Pubkey, + #[clap(long)] optional_pid: Pubkey, } @@ -72,7 +75,7 @@ fn main() -> Result<()> { Ok(()) } -// Runs a client for examples/tutorial/composite. +// Runs a client for tests/composite. // // Make sure to run a localnet with the program deploy to run this example. fn composite(client: &Client, pid: Pubkey) -> Result<()> { @@ -236,50 +239,70 @@ pub fn basic_4(client: &Client, pid: Pubkey) -> Result<()> { Ok(()) } -// Runs a client for examples/tutorial/composite. +// Runs a client for tests/optional. // // Make sure to run a localnet with the program deploy to run this example. fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { - // // Program client. - // let program = client.program(pid); - // - // // `Initialize` parameters. - // let optional1 = Keypair::generate(&mut OsRng); - // let optional2 = Keypair::generate(&mut OsRng); - // - // let initialize = OptionalInitialize { - // payer: Some(program.payer()), - // optional_2: None, - // required: Default::default(), - // system_program: Some(system_program::ID), - // optional_1: None - // }; - // - // // Build and send a transaction. - // program - // .request() - // .instruction() - // .signer(&optional1) - // .signer(&signer) - // // .signer(&optional2) - // .accounts(OptionalInitialize { - // payer: Some(program.payer()), - // optional_2: None, - // required: Default::default(), - // optional_1: None, - // system_program: Some(system_program::ID), - // }) - // .args(optional_instruction::Initialize { - // value: 10, - // key: optional1.pubkey(), - // }) - // .send()?; - // - // // Assert the transaction worked. - // let optional1_account: Data1 = program.account(optional1.pubkey())?; - // assert_eq!(optional1_account.data, 10); - // - // println!("Optional success!"); + // Program client. + let program = client.program(pid); + + // `Initialize` parameters. + let optional_2 = Keypair::new(); + + let optional2_key = optional_2.pubkey(); + + let optional1_seeds = &[Data1::PREFIX.as_ref(), optional2_key.as_ref()]; + let optional_1 = Pubkey::find_program_address(optional1_seeds, &pid).0; + let required = Keypair::new(); + let value: u64 = 10; + + // Build and send a transaction. + + program + .request() + .instruction(system_instruction::create_account( + &program.payer(), + &required.pubkey(), + program + .rpc() + .get_minimum_balance_for_rent_exemption(Data2::LEN)?, + Data2::LEN as u64, + &program.id(), + )) + .instruction(system_instruction::create_account( + &program.payer(), + &optional_2.pubkey(), + program + .rpc() + .get_minimum_balance_for_rent_exemption(Data2::LEN)?, + Data2::LEN as u64, + &program.id(), + )) + .signer(&optional_2) + .signer(&required) + .signer(&signer) + .accounts(OptionalInitialize { + payer: Some(program.payer()), + optional_1: Some(optional_1), + optional_2: Some(optional_2.pubkey()), + required: required.pubkey(), + system_program: Some(system_program::id()), + }) + .args(optional_instruction::Initialize { value, key: pid }) + .send() + .unwrap_err(); + + // Assert the transaction worked. + let optional1_account: Data1 = program.account(optional_1)?; + assert_eq!(optional1_account.data, value); + + let optional2_account: Data2 = program.account(optional_2.pubkey())?; + assert_eq!(optional2_account.optional_1, optional_1); + + let required: Data2 = program.account(required.pubkey())?; + assert_eq!(required.optional_1, Pubkey::default()); + + println!("Optional success!"); Ok(()) } From d6e43c1ed6a176caa60dfdabe406ec125c3d34cf Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 29 Sep 2022 01:02:34 -0500 Subject: [PATCH 068/109] fix other lints to make the test pass --- cli/src/config.rs | 2 +- cli/src/lib.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/src/config.rs b/cli/src/config.rs index 23ae6cb54f..6affc3f19b 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -323,7 +323,7 @@ pub struct WorkspaceConfig { pub types: String, } -#[derive(ArgEnum, Parser, Clone, PartialEq, Debug)] +#[derive(ArgEnum, Parser, Clone, PartialEq, Eq, Debug)] pub enum BootstrapMode { None, Debian, diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 71931bd034..b12b12bb9d 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -1353,7 +1353,7 @@ fn cd_member(cfg_override: &ConfigOverride, program_name: &str) -> Result<()> { return Ok(()); } } - return Err(anyhow!("{} is not part of the workspace", program_name,)); + Err(anyhow!("{} is not part of the workspace", program_name,)) } pub fn verify_bin(program_id: Pubkey, bin_path: &Path, cluster: &str) -> Result { @@ -1440,13 +1440,13 @@ pub fn verify_bin(program_id: Pubkey, bin_path: &Path, cluster: &str) -> Result< Ok(BinVerification { state, is_verified }) } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub struct BinVerification { pub state: BinVerificationState, pub is_verified: bool, } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub enum BinVerificationState { Buffer, ProgramData { From cb9c798964b4a4959395fabde4f04326e500426e Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 29 Sep 2022 15:00:42 -0500 Subject: [PATCH 069/109] remove comments --- ts/packages/anchor/src/program/namespace/methods.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/ts/packages/anchor/src/program/namespace/methods.ts b/ts/packages/anchor/src/program/namespace/methods.ts index d4194b05f8..a6eebd57f7 100644 --- a/ts/packages/anchor/src/program/namespace/methods.ts +++ b/ts/packages/anchor/src/program/namespace/methods.ts @@ -119,7 +119,6 @@ export class MethodsBuilder> { accounts: Partial> ): MethodsBuilder { this._autoResolveAccounts = true; - // Object.assign(this._accounts, accounts); for (const accountName in accounts) { if (accounts[accountName]) { this._accounts[accountName] = accounts[accountName]; @@ -137,7 +136,6 @@ export class MethodsBuilder> { accounts: Accounts ): MethodsBuilder { this._autoResolveAccounts = false; - // Object.assign(this._accounts, accounts); for (const accountName in accounts) { if (accounts[accountName]) { this._accounts[accountName] = accounts[accountName]; From f7fcbf3a027ea272930174d11fa24343a41af24f Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 3 Oct 2022 22:55:42 -0500 Subject: [PATCH 070/109] remove misc-optional for now --- tests/misc/programs/misc-optional/Cargo.toml | 22 - tests/misc/programs/misc-optional/Xargo.toml | 2 - .../programs/misc-optional/src/account.rs | 75 --- .../programs/misc-optional/src/context.rs | 577 ------------------ .../misc/programs/misc-optional/src/event.rs | 34 -- tests/misc/programs/misc-optional/src/lib.rs | 356 ----------- 6 files changed, 1066 deletions(-) delete mode 100644 tests/misc/programs/misc-optional/Cargo.toml delete mode 100644 tests/misc/programs/misc-optional/Xargo.toml delete mode 100644 tests/misc/programs/misc-optional/src/account.rs delete mode 100644 tests/misc/programs/misc-optional/src/context.rs delete mode 100644 tests/misc/programs/misc-optional/src/event.rs delete mode 100644 tests/misc/programs/misc-optional/src/lib.rs diff --git a/tests/misc/programs/misc-optional/Cargo.toml b/tests/misc/programs/misc-optional/Cargo.toml deleted file mode 100644 index 722320ebd4..0000000000 --- a/tests/misc/programs/misc-optional/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "misc-optional" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "misc-optional" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } -anchor-spl = { path = "../../../../spl" } -misc2 = { path = "../misc2", features = ["cpi"] } -spl-associated-token-account = "~1.0.3" diff --git a/tests/misc/programs/misc-optional/Xargo.toml b/tests/misc/programs/misc-optional/Xargo.toml deleted file mode 100644 index 1744f098ae..0000000000 --- a/tests/misc/programs/misc-optional/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/misc-optional/src/account.rs b/tests/misc/programs/misc-optional/src/account.rs deleted file mode 100644 index 893dac256b..0000000000 --- a/tests/misc/programs/misc-optional/src/account.rs +++ /dev/null @@ -1,75 +0,0 @@ -use anchor_lang::prelude::*; - -macro_rules! size { - ($name: ident, $size:expr) => { - impl $name { - pub const LEN: usize = $size; - } - }; -} - -pub const MAX_SIZE: usize = 10; -pub const MAX_SIZE_U8: u8 = 11; - -#[account] -pub struct Data { - pub udata: u128, // 16 - pub idata: i128, // 16 -} -size!(Data, 32); - -#[account] -pub struct DataU16 { - pub data: u16, // 2 -} -size!(DataU16, 32); - -#[account] -pub struct DataI8 { - pub data: i8, // 1 -} -size!(DataI8, 1); - -#[account] -pub struct DataI16 { - pub data: i16, // 2 -} -size!(DataI16, 2); - -#[account(zero_copy)] -pub struct DataZeroCopy { - pub data: u16, // 2 - pub _padding: u8, // 1 - pub bump: u8, // 1 -} -size!(DataZeroCopy, 4); - -#[account] -pub struct DataWithFilter { - pub authority: Pubkey, // 32 - pub filterable: Pubkey, // 32 -} -size!(DataWithFilter, 64); - -#[account] -pub struct DataMultidimensionalArray { - pub data: [[u8; 10]; 10], // 100 -} -size!(DataMultidimensionalArray, 100); - -#[account] -pub struct DataConstArraySize { - pub data: [u8; MAX_SIZE], // 10 -} -size!(DataConstArraySize, MAX_SIZE); - -#[account] -pub struct DataConstCastArraySize { - pub data_one: [u8; MAX_SIZE as usize], - pub data_two: [u8; MAX_SIZE_U8 as usize], -} - -#[account] -pub struct DataMultidimensionalArrayConstSizes { - pub data: [[u8; MAX_SIZE_U8 as usize]; MAX_SIZE], -} diff --git a/tests/misc/programs/misc-optional/src/context.rs b/tests/misc/programs/misc-optional/src/context.rs deleted file mode 100644 index 977ce68a8a..0000000000 --- a/tests/misc/programs/misc-optional/src/context.rs +++ /dev/null @@ -1,577 +0,0 @@ -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> { - #[account( - init, - seeds = [b"my-mint-seed".as_ref()], - bump, - payer = authority, - mint::decimals = 6, - mint::authority = authority, - )] - pub mint: Account<'info, Mint>, - #[account( - init, - seeds = [b"my-token-seed".as_ref()], - bump, - payer = authority, - token::mint = mint, - token::authority = authority, - )] - pub my_pda: Account<'info, TokenAccount>, - #[account(mut)] - /// CHECK: - pub authority: AccountInfo<'info>, - pub system_program: Program<'info, System>, - pub rent: Sysvar<'info, Rent>, - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct TestInitAssociatedToken<'info> { - #[account( - init, - associated_token::mint = mint, - payer = payer, - associated_token::authority = payer, - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - pub associated_token_program: Program<'info, AssociatedToken>, -} - -#[derive(Accounts)] -pub struct TestValidateAssociatedToken<'info> { - #[account( - associated_token::mint = mint, - associated_token::authority = wallet, - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - /// CHECK: - pub wallet: AccountInfo<'info>, -} - -#[derive(Accounts)] -#[instruction(nonce: u8)] -pub struct TestInstructionConstraint<'info> { - #[account( - seeds = [b"my-seed", my_account.key.as_ref()], - bump = nonce, - )] - /// CHECK: - pub my_pda: AccountInfo<'info>, - /// CHECK: - pub my_account: AccountInfo<'info>, -} - -#[derive(Accounts)] -#[instruction(domain: String, seed: Vec, bump: u8)] -pub struct TestPdaInit<'info> { - #[account( - init, - seeds = [b"my-seed", domain.as_bytes(), foo.key.as_ref(), &seed], - bump, - payer = my_payer, - space = DataU16::LEN + 8 - )] - pub my_pda: Account<'info, DataU16>, - #[account(mut)] - pub my_payer: Signer<'info>, - /// CHECK: - pub foo: AccountInfo<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestPdaInitZeroCopy<'info> { - #[account( - init, - seeds = [b"my-seed".as_ref()], - bump, - payer = my_payer, - space = DataZeroCopy::LEN + 8 - )] - pub my_pda: AccountLoader<'info, DataZeroCopy>, - #[account(mut)] - pub my_payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestPdaMutZeroCopy<'info> { - #[account( - mut, - seeds = [b"my-seed".as_ref()], - bump = my_pda.load()?.bump, - )] - pub my_pda: AccountLoader<'info, DataZeroCopy>, - /// CHECK: - pub my_payer: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct Ctor {} - -#[derive(Accounts)] -pub struct RemainingAccounts {} - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(zero)] - pub data: Account<'info, Data>, -} - -#[derive(Accounts)] -pub struct InitializeSkipRentExempt<'info> { - #[account(zero, rent_exempt = skip)] - pub data: Account<'info, Data>, -} - -#[derive(Accounts)] -pub struct InitializeNoRentExempt<'info> { - /// CHECK: - pub data: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestOwner<'info> { - #[account(owner = *misc.key)] - /// CHECK: - pub data: AccountInfo<'info>, - /// CHECK: - pub misc: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestExecutable<'info> { - #[account(executable)] - /// CHECK: - 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)] - pub data: Account<'info, Data>, - /// CHECK: - sol_dest: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestU16<'info> { - #[account(zero)] - pub my_account: Account<'info, DataU16>, -} - -#[derive(Accounts)] -pub struct TestI16<'info> { - #[account(zero)] - pub data: Account<'info, DataI16>, -} - -#[derive(Accounts)] -pub struct TestSimulate {} - -#[derive(Accounts)] -pub struct TestI8<'info> { - #[account(zero)] - pub data: Account<'info, DataI8>, -} - -#[derive(Accounts)] -pub struct TestInit<'info> { - #[account(init, payer = payer, space = DataI8::LEN + 8)] - pub data: Account<'info, DataI8>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestInitZeroCopy<'info> { - #[account(init, payer = payer, space = DataZeroCopy::LEN + 8)] - pub data: Loader<'info, DataZeroCopy>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestInitMint<'info> { - #[account(init, mint::decimals = 6, mint::authority = payer, mint::freeze_authority = payer, payer = payer, )] - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct TestInitToken<'info> { - #[account(init, token::mint = mint, token::authority = payer, payer = payer, )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct TestCompositePayer<'info> { - pub composite: TestInit<'info>, - #[account(init, payer = composite.payer, space = Data::LEN + 8)] - pub data: Account<'info, Data>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestFetchAll<'info> { - #[account(init, payer = authority, space = DataWithFilter::LEN + 8)] - pub data: Account<'info, DataWithFilter>, - #[account(mut)] - pub authority: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestInitWithEmptySeeds<'info> { - #[account(init, seeds = [], bump, payer = authority, space = Data::LEN + 8)] - pub pda: Account<'info, Data>, - #[account(mut)] - pub authority: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestEmptySeedsConstraint<'info> { - #[account(seeds = [], bump)] - /// CHECK: - pub pda: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitWithSpace<'info> { - #[account(init, payer = payer, space = DataU16::LEN + 8)] - pub data: Account<'info, DataU16>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestInitIfNeeded<'info> { - // intentionally using more space (+500) to check whether space is checked when using init_if_needed - #[account(init_if_needed, payer = payer, space = DataU16::LEN + 8 + 500)] - pub data: Account<'info, DataU16>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestInitIfNeededChecksOwner<'info> { - #[account(init_if_needed, payer = payer, space = 100, owner = *owner.key, seeds = [b"hello"], bump)] - /// CHECK: - pub data: UncheckedAccount<'info>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, - /// CHECK: - pub owner: AccountInfo<'info>, -} - -#[derive(Accounts)] -#[instruction(seed_data: String)] -pub struct TestInitIfNeededChecksSeeds<'info> { - #[account(init_if_needed, payer = payer, space = 100, seeds = [seed_data.as_bytes()], bump)] - /// CHECK: - pub data: UncheckedAccount<'info>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -#[instruction(decimals: u8)] -pub struct TestInitMintIfNeeded<'info> { - #[account(init_if_needed, mint::decimals = decimals, mint::authority = mint_authority, mint::freeze_authority = freeze_authority, payer = payer)] - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - /// CHECK: - pub mint_authority: AccountInfo<'info>, - /// CHECK: - pub freeze_authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestInitTokenIfNeeded<'info> { - #[account(init_if_needed, token::mint = mint, token::authority = authority, payer = payer, )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - /// CHECK: - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestInitAssociatedTokenIfNeeded<'info> { - #[account( - init_if_needed, - payer = payer, - associated_token::mint = mint, - associated_token::authority = authority - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - pub associated_token_program: Program<'info, AssociatedToken>, - /// CHECK: - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestMultidimensionalArray<'info> { - #[account(zero)] - pub data: Account<'info, DataMultidimensionalArray>, -} - -#[derive(Accounts)] -pub struct TestConstArraySize<'info> { - #[account(zero)] - pub data: Account<'info, DataConstArraySize>, -} - -#[derive(Accounts)] -pub struct TestConstIxDataSize<'info> { - #[account(zero)] - pub data: Account<'info, DataConstArraySize>, -} - -#[derive(Accounts)] -pub struct TestMultidimensionalArrayConstSizes<'info> { - #[account(zero)] - pub data: Account<'info, DataMultidimensionalArrayConstSizes>, -} - -#[derive(Accounts)] -pub struct NoRentExempt<'info> { - /// CHECK: - pub data: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct EnforceRentExempt<'info> { - #[account(rent_exempt = enforce)] - /// CHECK: - pub data: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitDecreaseLamports<'info> { - #[account(init, payer = user, space = 1000)] - /// CHECK: - pub data: AccountInfo<'info>, - #[account(mut)] - pub user: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct InitIfNeededChecksRentExemption<'info> { - #[account(init_if_needed, payer = user, space = 1000)] - /// CHECK: - pub data: AccountInfo<'info>, - #[account(mut)] - pub user: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -#[instruction(bump: u8, second_bump: u8)] -pub struct TestProgramIdConstraint<'info> { - // not a real associated token account - // just deriving like this for testing purposes - #[account(seeds = [b"seed"], bump = bump, seeds::program = anchor_spl::associated_token::ID)] - /// CHECK: - first: AccountInfo<'info>, - - #[account(seeds = [b"seed"], bump = second_bump, seeds::program = crate::ID)] - /// CHECK: - second: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestProgramIdConstraintUsingFindPda<'info> { - // not a real associated token account - // just deriving like this for testing purposes - #[account(seeds = [b"seed"], bump, seeds::program = anchor_spl::associated_token::ID)] - /// CHECK: - first: AccountInfo<'info>, - - #[account(seeds = [b"seed"], bump, seeds::program = crate::ID)] - /// CHECK: - second: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestUnsafeFieldSafetyErrors<'info> { - #[doc = "test"] - /// CHECK: - pub data: UncheckedAccount<'info>, - #[account(mut)] - /// CHECK: - pub data_two: UncheckedAccount<'info>, - #[account( - seeds = [b"my-seed", signer.key.as_ref()], - bump - )] - /// CHECK: - pub data_three: UncheckedAccount<'info>, - /// CHECK: - pub data_four: UncheckedAccount<'info>, - pub signer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestConstraintToken<'info> { - #[account( - token::mint = mint, - token::authority = payer - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - pub payer: Signer<'info>, -} - -#[derive(Accounts)] -pub struct TestAuthorityConstraint<'info> { - #[account( - token::mint = mint, - token::authority = fake_authority - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - pub fake_authority: AccountInfo<'info>, -} -#[derive(Accounts)] -pub struct TestOnlyAuthorityConstraint<'info> { - #[account( - token::authority = payer - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - pub payer: Signer<'info>, -} -#[derive(Accounts)] -pub struct TestOnlyMintConstraint<'info> { - #[account( - token::mint = mint, - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, -} - -#[derive(Accounts)] -#[instruction(decimals: u8)] -pub struct TestMintConstraint<'info> { - #[account( - mint::decimals = decimals, - mint::authority = mint_authority, - mint::freeze_authority = freeze_authority - )] - pub mint: Account<'info, Mint>, - pub mint_authority: AccountInfo<'info>, - pub freeze_authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -#[instruction(decimals: u8)] -pub struct TestMintOnlyDecimalsConstraint<'info> { - #[account( - mint::decimals = decimals, - )] - pub mint: Account<'info, Mint>, -} - -#[derive(Accounts)] -pub struct TestMintAuthorityConstraint<'info> { - #[account( - mint::authority = mint_authority, - mint::freeze_authority = freeze_authority - )] - pub mint: Account<'info, Mint>, - pub mint_authority: AccountInfo<'info>, - pub freeze_authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestMintOneAuthorityConstraint<'info> { - #[account( - mint::authority = mint_authority, - )] - pub mint: Account<'info, Mint>, - pub mint_authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -#[instruction(decimals: u8)] -pub struct TestMintMissMintAuthConstraint<'info> { - #[account( - mint::decimals = decimals, - mint::freeze_authority = freeze_authority, - )] - pub mint: Account<'info, Mint>, - pub freeze_authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestAssociatedToken<'info> { - #[account( - associated_token::mint = mint, - associated_token::authority = authority, - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - pub authority: AccountInfo<'info>, -} diff --git a/tests/misc/programs/misc-optional/src/event.rs b/tests/misc/programs/misc-optional/src/event.rs deleted file mode 100644 index 80b7dd8f88..0000000000 --- a/tests/misc/programs/misc-optional/src/event.rs +++ /dev/null @@ -1,34 +0,0 @@ -use anchor_lang::prelude::*; - -pub const MAX_EVENT_SIZE: usize = 10; -pub const MAX_EVENT_SIZE_U8: u8 = 11; - -#[event] -pub struct E1 { - pub data: u32, -} - -#[event] -pub struct E2 { - pub data: u32, -} - -#[event] -pub struct E3 { - pub data: u32, -} - -#[event] -pub struct E4 { - pub data: Pubkey, -} - -#[event] -pub struct E5 { - pub data: [u8; MAX_EVENT_SIZE], -} - -#[event] -pub struct E6 { - pub data: [u8; MAX_EVENT_SIZE_U8 as usize], -} diff --git a/tests/misc/programs/misc-optional/src/lib.rs b/tests/misc/programs/misc-optional/src/lib.rs deleted file mode 100644 index 44893563a1..0000000000 --- a/tests/misc/programs/misc-optional/src/lib.rs +++ /dev/null @@ -1,356 +0,0 @@ -//! Misc example is a catchall program for testing unrelated features. -//! It's not too instructive/coherent by itself, so please see other examples. - -use account::MAX_SIZE; -use anchor_lang::prelude::*; -use context::*; -use event::*; -use misc2::Auth; - -mod account; -mod context; -mod event; - -declare_id!("3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh"); - -#[constant] -pub const BASE: u128 = 1_000_000; -#[constant] -pub const DECIMALS: u8 = 6; -pub const NO_IDL: u16 = 55; - -#[program] -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; - Ok(()) - } - - pub fn initialize_no_rent_exempt(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn initialize_skip_rent_exempt(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_owner(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_executable(_ctx: Context) -> Result<()> { - 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(()) - } - - pub fn test_simulate(_ctx: Context, data: u32) -> Result<()> { - emit!(E1 { data }); - emit!(E2 { data: 1234 }); - emit!(E3 { data: 9 }); - emit!(E5 { - data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - }); - emit!(E6 { - data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - }); - Ok(()) - } - - pub fn test_i8(ctx: Context, data: i8) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_i16(ctx: Context, data: i16) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_const_array_size(ctx: Context, data: u8) -> Result<()> { - ctx.accounts.data.data[0] = data; - Ok(()) - } - - pub fn test_const_ix_data_size( - ctx: Context, - data: [u8; MAX_SIZE], - ) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_close(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_instruction_constraint( - _ctx: Context, - _nonce: u8, - ) -> Result<()> { - Ok(()) - } - - pub fn test_pda_init( - ctx: Context, - _domain: String, - _seed: Vec, - _bump: u8, - ) -> Result<()> { - ctx.accounts.my_pda.data = 6; - Ok(()) - } - - pub fn test_pda_init_zero_copy(ctx: Context) -> Result<()> { - let mut acc = ctx.accounts.my_pda.load_init()?; - acc.data = 9; - acc.bump = *ctx.bumps.get("my_pda").unwrap(); - Ok(()) - } - - pub fn test_pda_mut_zero_copy(ctx: Context) -> Result<()> { - let mut acc = ctx.accounts.my_pda.load_mut()?; - acc.data = 1234; - Ok(()) - } - - pub fn test_token_seeds_init(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn default<'info>( - _program_id: &Pubkey, - _accounts: &[AccountInfo<'info>], - _data: &[u8], - ) -> Result<()> { - Err(ProgramError::Custom(1234).into()) - } - - pub fn test_init(ctx: Context) -> Result<()> { - ctx.accounts.data.data = 3; - Ok(()) - } - - pub fn test_init_zero_copy(ctx: Context) -> Result<()> { - let mut data = ctx.accounts.data.load_init()?; - data.data = 10; - data.bump = 2; - Ok(()) - } - - pub fn test_init_mint(ctx: Context) -> Result<()> { - assert!(ctx.accounts.mint.decimals == 6); - Ok(()) - } - - pub fn test_init_token(ctx: Context) -> Result<()> { - assert!(ctx.accounts.token.mint == ctx.accounts.mint.key()); - Ok(()) - } - - pub fn test_composite_payer(ctx: Context) -> Result<()> { - ctx.accounts.composite.data.data = 1; - ctx.accounts.data.udata = 2; - ctx.accounts.data.idata = 3; - Ok(()) - } - - pub fn test_init_associated_token(ctx: Context) -> Result<()> { - assert!(ctx.accounts.token.mint == ctx.accounts.mint.key()); - Ok(()) - } - - pub fn test_validate_associated_token( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_fetch_all(ctx: Context, filterable: Pubkey) -> Result<()> { - ctx.accounts.data.authority = ctx.accounts.authority.key(); - ctx.accounts.data.filterable = filterable; - Ok(()) - } - - pub fn test_init_with_empty_seeds(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_empty_seeds_constraint(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_init_if_needed(ctx: Context, data: u16) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_init_if_needed_checks_owner( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_init_if_needed_checks_seeds( - _ctx: Context, - _seed_data: String, - ) -> Result<()> { - Ok(()) - } - - pub fn test_init_mint_if_needed( - _ctx: Context, - _decimals: u8, - ) -> Result<()> { - Ok(()) - } - - pub fn test_init_token_if_needed(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_init_associated_token_if_needed( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn init_with_space(_ctx: Context, data: u16) -> Result<()> { - Ok(()) - } - - pub fn test_multidimensional_array( - ctx: Context, - data: [[u8; 10]; 10], - ) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_multidimensional_array_const_sizes( - ctx: Context, - data: [[u8; 11]; 10], - ) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_no_rent_exempt(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_enforce_rent_exempt(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn init_decrease_lamports(ctx: Context) -> Result<()> { - **ctx.accounts.data.try_borrow_mut_lamports()? -= 1; - **ctx.accounts.user.try_borrow_mut_lamports()? += 1; - Ok(()) - } - - pub fn init_if_needed_checks_rent_exemption( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_program_id_constraint( - _ctx: Context, - _bump: u8, - _second_bump: u8, - ) -> Result<()> { - Ok(()) - } - - pub fn test_program_id_constraint_find_pda( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_token_constraint(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_token_auth_constraint(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_only_auth_constraint(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_only_mint_constraint(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_mint_constraint(_ctx: Context, _decimals: u8) -> Result<()> { - Ok(()) - } - - pub fn test_mint_only_decimals_constraint( - _ctx: Context, - _decimals: u8, - ) -> Result<()> { - Ok(()) - } - - pub fn test_mint_only_auth_constraint( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_mint_only_one_auth_constraint( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_mint_miss_mint_auth_constraint( - _ctx: Context, - _decimals: u8, - ) -> Result<()> { - Ok(()) - } - - pub fn test_associated_constraint(_ctx: Context) -> Result<()> { - Ok(()) - } -} From 49bb58ff8b82e56467767cc0e18c519f516680ba Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 3 Oct 2022 23:01:47 -0500 Subject: [PATCH 071/109] update optional program --- client/example/src/main.rs | 36 +++++------ .../optional/programs/optional/src/account.rs | 20 +++++++ .../optional/programs/optional/src/context.rs | 49 +++++++++++++++ .../optional/programs/optional/src/errors.rs | 7 --- tests/optional/programs/optional/src/lib.rs | 59 +++++++++++++++---- .../programs/optional/src/processor/close.rs | 17 ------ .../optional/src/processor/initialize.rs | 37 ------------ .../programs/optional/src/processor/mod.rs | 9 --- .../optional/src/processor/realloc.rs | 25 -------- .../programs/optional/src/processor/update.rs | 25 -------- tests/optional/programs/optional/src/state.rs | 20 ------- 11 files changed, 136 insertions(+), 168 deletions(-) create mode 100644 tests/optional/programs/optional/src/account.rs create mode 100644 tests/optional/programs/optional/src/context.rs delete mode 100644 tests/optional/programs/optional/src/errors.rs delete mode 100644 tests/optional/programs/optional/src/processor/close.rs delete mode 100644 tests/optional/programs/optional/src/processor/initialize.rs delete mode 100644 tests/optional/programs/optional/src/processor/mod.rs delete mode 100644 tests/optional/programs/optional/src/processor/realloc.rs delete mode 100644 tests/optional/programs/optional/src/processor/update.rs delete mode 100644 tests/optional/programs/optional/src/state.rs diff --git a/client/example/src/main.rs b/client/example/src/main.rs index b4097f34dc..4b9ebfb318 100644 --- a/client/example/src/main.rs +++ b/client/example/src/main.rs @@ -27,9 +27,9 @@ use rand::rngs::OsRng; use solana_sdk::system_program; // The `accounts` and `instructions` modules are generated by the framework. +use optional::account::*; use optional::accounts::Initialize as OptionalInitialize; use optional::instruction as optional_instruction; -use optional::state::{Data1, Data2}; #[derive(Parser, Debug)] pub struct Opts { @@ -247,12 +247,12 @@ fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { let program = client.program(pid); // `Initialize` parameters. - let optional_2 = Keypair::new(); + let data_account = Keypair::new(); - let optional2_key = optional_2.pubkey(); + let data_account_key = data_account.pubkey(); - let optional1_seeds = &[Data1::PREFIX.as_ref(), optional2_key.as_ref()]; - let optional_1 = Pubkey::find_program_address(optional1_seeds, &pid).0; + let data_pda_seeds = &[DataPda::PREFIX.as_ref(), data_account_key.as_ref()]; + let data_pda = Pubkey::find_program_address(data_pda_seeds, &pid).0; let required = Keypair::new(); let value: u64 = 10; @@ -265,42 +265,42 @@ fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { &required.pubkey(), program .rpc() - .get_minimum_balance_for_rent_exemption(Data2::LEN)?, - Data2::LEN as u64, + .get_minimum_balance_for_rent_exemption(DataAccount::LEN)?, + DataAccount::LEN as u64, &program.id(), )) .instruction(system_instruction::create_account( &program.payer(), - &optional_2.pubkey(), + &data_account.pubkey(), program .rpc() - .get_minimum_balance_for_rent_exemption(Data2::LEN)?, - Data2::LEN as u64, + .get_minimum_balance_for_rent_exemption(DataAccount::LEN)?, + DataAccount::LEN as u64, &program.id(), )) - .signer(&optional_2) + .signer(&data_account) .signer(&required) .signer(&signer) .accounts(OptionalInitialize { payer: Some(program.payer()), - optional_1: Some(optional_1), - optional_2: Some(optional_2.pubkey()), required: required.pubkey(), system_program: Some(system_program::id()), + optional_account: Some(data_pda), + optional_pda: Some(data_account.pubkey()), }) .args(optional_instruction::Initialize { value, key: pid }) .send() .unwrap_err(); // Assert the transaction worked. - let optional1_account: Data1 = program.account(optional_1)?; + let optional1_account: DataPda = program.account(data_pda)?; assert_eq!(optional1_account.data, value); - let optional2_account: Data2 = program.account(optional_2.pubkey())?; - assert_eq!(optional2_account.optional_1, optional_1); + let optional2_account: DataAccount = program.account(data_account.pubkey())?; + assert_eq!(optional2_account.data_pda, data_pda); - let required: Data2 = program.account(required.pubkey())?; - assert_eq!(required.optional_1, Pubkey::default()); + let required: DataAccount = program.account(required.pubkey())?; + assert_eq!(required.data_pda, Pubkey::default()); println!("Optional success!"); diff --git a/tests/optional/programs/optional/src/account.rs b/tests/optional/programs/optional/src/account.rs new file mode 100644 index 0000000000..d9e519089d --- /dev/null +++ b/tests/optional/programs/optional/src/account.rs @@ -0,0 +1,20 @@ +use anchor_lang::prelude::*; + +#[account] +pub struct DataPda { + pub data: u64, +} + +impl DataPda { + pub const LEN: usize = 8 + 8; + pub const PREFIX: &'static str = "data_pda"; +} + +#[account] +pub struct DataAccount { + pub data_pda: Pubkey, +} + +impl DataAccount { + pub const LEN: usize = 8 + 32; +} diff --git a/tests/optional/programs/optional/src/context.rs b/tests/optional/programs/optional/src/context.rs new file mode 100644 index 0000000000..5e6a439983 --- /dev/null +++ b/tests/optional/programs/optional/src/context.rs @@ -0,0 +1,49 @@ +use crate::account::*; +use anchor_lang::prelude::*; + +#[derive(Accounts)] +pub struct Initialize<'info> { + #[account(mut)] + pub payer: Option>, + #[account(init, payer = payer, space = DataAccount::LEN)] + pub optional_account: Option>, + pub system_program: Option>, + #[account(zero)] + pub required: Account<'info, DataAccount>, + #[account(init, seeds=[DataPda::PREFIX.as_ref(), optional_account.as_ref().unwrap().key().as_ref()], bump, payer=payer, space=DataPda::LEN, constraint = payer.is_some())] + pub optional_pda: Option>, +} + +#[derive(Accounts)] +#[instruction(pda_bump: u8)] +pub struct Update<'info> { + #[account(mut)] + pub payer: Option>, + #[account(mut, seeds=[DataPda::PREFIX.as_ref(), optional_account.as_ref().unwrap().key().as_ref()], bump = pda_bump)] + pub optional_pda: Option>, + #[account(mut, constraint = payer.is_some())] + pub optional_account: Option>, +} + +#[derive(Accounts)] +pub struct Realloc<'info> { + #[account(mut)] + pub payer: Option>, + #[account(mut, realloc = 50, realloc::payer = payer, realloc::zero = false)] + pub optional_pda: Option>, + pub required: Account<'info, DataAccount>, + pub system_program: Option>, + #[account(mut, realloc = 50, realloc::payer = payer, realloc::zero = false)] + pub optional_account: Option>, +} + +#[derive(Accounts)] +pub struct Close<'info> { + #[account(mut)] + pub payer: Option>, + #[account(mut, close = payer, constraint = system_program.is_some())] + pub data_pda: Option>, + #[account(mut, close = payer, has_one = data_pda, constraint = payer.is_some())] + pub optional_account: Option>, + pub system_program: Option>, +} diff --git a/tests/optional/programs/optional/src/errors.rs b/tests/optional/programs/optional/src/errors.rs deleted file mode 100644 index 39f993e298..0000000000 --- a/tests/optional/programs/optional/src/errors.rs +++ /dev/null @@ -1,7 +0,0 @@ -use anchor_lang::prelude::*; - -#[error_code] -pub enum OptionalErrors { - #[msg("Failed realloc")] - ReallocFailed, -} diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index 1f91688779..e8f8378c5b 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -2,12 +2,11 @@ //! structs deriving `Accounts`. use anchor_lang::prelude::*; -use processor::*; +use anchor_lang::AccountsClose; +pub use context::*; -pub mod errors; -pub use errors::OptionalErrors; -pub mod processor; -pub mod state; +pub mod account; +pub mod context; declare_id!("FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG"); #[program] @@ -15,18 +14,58 @@ mod optional { use super::*; pub fn initialize(ctx: Context, value: u64, key: Pubkey) -> Result<()> { - handle_initialize(ctx, value, key) + let optional_pda = &mut ctx.accounts.optional_pda; + let optional_account = &mut ctx.accounts.optional_account; + let required = &mut ctx.accounts.required; + + required.data_pda = Pubkey::default(); + + if let Some(data) = optional_pda { + data.data = value; + } + + if let Some(data2) = optional_account { + if let Some(optional) = optional_pda { + data2.data_pda = optional.key(); + } else { + data2.data_pda = key; + } + } + + Ok(()) } - pub fn update(ctx: Context, value: u64, key: Pubkey) -> Result<()> { - handle_update(ctx, value, key) + pub fn update(ctx: Context, value: u64, key: Pubkey, _pda_bump: u8) -> Result<()> { + if let Some(data_pda) = &mut ctx.accounts.optional_pda { + data_pda.data = value; + } + if let Some(data_account) = &mut ctx.accounts.optional_account { + data_account.data_pda = key; + } + Ok(()) } pub fn realloc(ctx: Context) -> Result<()> { - handle_realloc(ctx) + let optional_pda = &ctx.accounts.optional_pda; + if let Some(acc) = optional_pda { + let len = acc.to_account_info().data_len(); + if len != 50 { + return err!(OptionalErrors::ReallocFailed); + } + } + Ok(()) } pub fn close(ctx: Context) -> Result<()> { - handle_close(ctx) + if let Some(data_account) = &ctx.accounts.data_pda { + data_account.close(ctx.accounts.payer.as_ref().unwrap().to_account_info())?; + } + Ok(()) } } + +#[error_code] +pub enum OptionalErrors { + #[msg("Failed realloc")] + ReallocFailed, +} diff --git a/tests/optional/programs/optional/src/processor/close.rs b/tests/optional/programs/optional/src/processor/close.rs deleted file mode 100644 index 3adbccfc39..0000000000 --- a/tests/optional/programs/optional/src/processor/close.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::state::*; -use anchor_lang::prelude::*; - -#[derive(Accounts)] -pub struct Close<'info> { - #[account(mut)] - pub payer: Option>, - #[account(mut, close = payer, constraint = system_program.is_some())] - pub optional_1: Option>, - #[account(mut, close = payer, has_one = optional_1, constraint = payer.is_some())] - pub optional_2: Option>, - pub system_program: Option>, -} - -pub fn handle_close(_ctx: Context) -> Result<()> { - Ok(()) -} diff --git a/tests/optional/programs/optional/src/processor/initialize.rs b/tests/optional/programs/optional/src/processor/initialize.rs deleted file mode 100644 index 470ca32333..0000000000 --- a/tests/optional/programs/optional/src/processor/initialize.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::state::*; -use crate::OptionalErrors; -use anchor_lang::prelude::*; - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(mut)] - pub payer: Option>, - #[account(zero)] - pub optional_2: Option>, - #[account(zero)] - pub required: Account<'info, Data2>, - #[account(init, seeds=[Data1::PREFIX.as_ref(), optional_2.unwrap().key().as_ref()], bump, payer=payer, space=Data1::LEN, constraint = payer.is_some())] - pub optional_1: Option>, - pub system_program: Option>, -} - -pub fn handle_initialize(ctx: Context, value: u64, key: Pubkey) -> Result<()> { - let optional_1 = &mut ctx.accounts.optional_1; - let optional_2 = &mut ctx.accounts.optional_2; - let required = &mut ctx.accounts.required; - - required.optional_1 = Pubkey::default(); - - if let Some(data) = optional_1 { - data.data = value; - } - if let Some(data2) = optional_2 { - if let Some(optional) = optional_1 { - data2.optional_1 = optional.key(); - } else { - data2.optional_1 = key; - } - } - - Ok(()) -} diff --git a/tests/optional/programs/optional/src/processor/mod.rs b/tests/optional/programs/optional/src/processor/mod.rs deleted file mode 100644 index 31c2c401e6..0000000000 --- a/tests/optional/programs/optional/src/processor/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod close; -mod initialize; -mod realloc; -mod update; - -pub use close::*; -pub use initialize::*; -pub use realloc::*; -pub use update::*; diff --git a/tests/optional/programs/optional/src/processor/realloc.rs b/tests/optional/programs/optional/src/processor/realloc.rs deleted file mode 100644 index 3e7da9df3d..0000000000 --- a/tests/optional/programs/optional/src/processor/realloc.rs +++ /dev/null @@ -1,25 +0,0 @@ -use anchor_lang::prelude::*; - -use crate::state::*; -use crate::OptionalErrors; - -#[derive(Accounts)] -pub struct Realloc<'info> { - #[account(mut)] - pub payer: Signer<'info>, - #[account(mut, realloc = 20, realloc::payer = payer, realloc::zero = false)] - pub optional_1: Option>, - pub required: Account<'info, Data2>, - pub system_program: Option>, -} - -pub fn handle_realloc(ctx: Context) -> Result<()> { - let optional = &ctx.accounts.optional_1; - if let Some(acc) = optional { - let len = acc.to_account_info().data_len(); - if len != 20 { - return err!(OptionalErrors::ReallocFailed); - } - } - Ok(()) -} diff --git a/tests/optional/programs/optional/src/processor/update.rs b/tests/optional/programs/optional/src/processor/update.rs deleted file mode 100644 index 878a2f13f8..0000000000 --- a/tests/optional/programs/optional/src/processor/update.rs +++ /dev/null @@ -1,25 +0,0 @@ -use anchor_lang::prelude::*; - -use crate::state::*; - -#[derive(Accounts)] -pub struct Update<'info> { - #[account(mut)] - pub payer: Option>, - #[account(mut, seeds=[Data1::PREFIX.as_ref(), optional_2.as_ref().unwrap().key().as_ref()], bump)] - pub optional_1: Option>, - #[account(mut, constraint = payer.is_some())] - pub optional_2: Option>, - #[account(constraint = if optional_2.is_none() {optional_1.is_none()} else {true})] - pub required: Program<'info, System>, -} - -pub fn handle_update(ctx: Context, value: u64, key: Pubkey) -> Result<()> { - if let Some(data) = &mut ctx.accounts.optional_1 { - data.data = value; - }; - if let Some(data2) = &mut ctx.accounts.optional_2 { - data2.optional_1 = key; - }; - Ok(()) -} diff --git a/tests/optional/programs/optional/src/state.rs b/tests/optional/programs/optional/src/state.rs deleted file mode 100644 index 7a45ed0f39..0000000000 --- a/tests/optional/programs/optional/src/state.rs +++ /dev/null @@ -1,20 +0,0 @@ -use anchor_lang::prelude::*; - -#[account] -pub struct Data1 { - pub data: u64, -} - -impl Data1 { - pub const LEN: usize = 8 + 8; - pub const PREFIX: &'static str = "data1"; -} - -#[account] -pub struct Data2 { - pub optional_1: Pubkey, -} - -impl Data2 { - pub const LEN: usize = 8 + 32; -} From 34bcba126b0a4ad8c108eb2e477f0ee0054b3897 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 4 Oct 2022 18:52:12 -0500 Subject: [PATCH 072/109] update optional program and client tests again --- client/example/src/main.rs | 34 +++++++++---------- .../optional/programs/optional/src/account.rs | 8 ++--- .../optional/programs/optional/src/context.rs | 12 +++---- tests/optional/programs/optional/src/lib.rs | 26 +++++++------- 4 files changed, 40 insertions(+), 40 deletions(-) diff --git a/client/example/src/main.rs b/client/example/src/main.rs index 4b9ebfb318..108157e612 100644 --- a/client/example/src/main.rs +++ b/client/example/src/main.rs @@ -247,13 +247,13 @@ fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { let program = client.program(pid); // `Initialize` parameters. - let data_account = Keypair::new(); + let data_account_keypair = Keypair::new(); - let data_account_key = data_account.pubkey(); + let data_account_key = data_account_keypair.pubkey(); let data_pda_seeds = &[DataPda::PREFIX.as_ref(), data_account_key.as_ref()]; - let data_pda = Pubkey::find_program_address(data_pda_seeds, &pid).0; - let required = Keypair::new(); + let data_pda_key = Pubkey::find_program_address(data_pda_seeds, &pid).0; + let required_keypair = Keypair::new(); let value: u64 = 10; // Build and send a transaction. @@ -262,7 +262,7 @@ fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { .request() .instruction(system_instruction::create_account( &program.payer(), - &required.pubkey(), + &required_keypair.pubkey(), program .rpc() .get_minimum_balance_for_rent_exemption(DataAccount::LEN)?, @@ -271,36 +271,36 @@ fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { )) .instruction(system_instruction::create_account( &program.payer(), - &data_account.pubkey(), + &data_account_keypair.pubkey(), program .rpc() .get_minimum_balance_for_rent_exemption(DataAccount::LEN)?, DataAccount::LEN as u64, &program.id(), )) - .signer(&data_account) - .signer(&required) + .signer(&data_account_keypair) + .signer(&required_keypair) .signer(&signer) .accounts(OptionalInitialize { payer: Some(program.payer()), - required: required.pubkey(), + required: required_keypair.pubkey(), system_program: Some(system_program::id()), - optional_account: Some(data_pda), - optional_pda: Some(data_account.pubkey()), + optional_account: Some(data_account_keypair.pubkey()), + optional_pda: None, }) .args(optional_instruction::Initialize { value, key: pid }) .send() .unwrap_err(); // Assert the transaction worked. - let optional1_account: DataPda = program.account(data_pda)?; - assert_eq!(optional1_account.data, value); + let required: DataAccount = program.account(required_keypair.pubkey())?; + assert_eq!(required.data, 0); - let optional2_account: DataAccount = program.account(data_account.pubkey())?; - assert_eq!(optional2_account.data_pda, data_pda); + let optional_pda = program.account::(data_pda_key); + assert!(optional_pda.is_err()); - let required: DataAccount = program.account(required.pubkey())?; - assert_eq!(required.data_pda, Pubkey::default()); + let optional_account: DataAccount = program.account(data_account_keypair.pubkey())?; + assert_eq!(optional_account.data, value); println!("Optional success!"); diff --git a/tests/optional/programs/optional/src/account.rs b/tests/optional/programs/optional/src/account.rs index d9e519089d..cd591fee63 100644 --- a/tests/optional/programs/optional/src/account.rs +++ b/tests/optional/programs/optional/src/account.rs @@ -2,19 +2,19 @@ use anchor_lang::prelude::*; #[account] pub struct DataPda { - pub data: u64, + pub data_account: Pubkey, } impl DataPda { - pub const LEN: usize = 8 + 8; + pub const LEN: usize = 8 + 32; pub const PREFIX: &'static str = "data_pda"; } #[account] pub struct DataAccount { - pub data_pda: Pubkey, + pub data: u64, } impl DataAccount { - pub const LEN: usize = 8 + 32; + pub const LEN: usize = 8 + 8; } diff --git a/tests/optional/programs/optional/src/context.rs b/tests/optional/programs/optional/src/context.rs index 5e6a439983..3b02702300 100644 --- a/tests/optional/programs/optional/src/context.rs +++ b/tests/optional/programs/optional/src/context.rs @@ -5,12 +5,12 @@ use anchor_lang::prelude::*; pub struct Initialize<'info> { #[account(mut)] pub payer: Option>, - #[account(init, payer = payer, space = DataAccount::LEN)] + #[account(init, payer = payer, space = DataAccount::LEN, constraint = payer.is_some())] pub optional_account: Option>, pub system_program: Option>, #[account(zero)] pub required: Account<'info, DataAccount>, - #[account(init, seeds=[DataPda::PREFIX.as_ref(), optional_account.as_ref().unwrap().key().as_ref()], bump, payer=payer, space=DataPda::LEN, constraint = payer.is_some())] + #[account(init, seeds=[DataPda::PREFIX.as_ref(), optional_account.as_ref().unwrap().key().as_ref()], bump, payer=payer, space=DataPda::LEN)] pub optional_pda: Option>, } @@ -41,9 +41,9 @@ pub struct Realloc<'info> { pub struct Close<'info> { #[account(mut)] pub payer: Option>, - #[account(mut, close = payer, constraint = system_program.is_some())] - pub data_pda: Option>, - #[account(mut, close = payer, has_one = data_pda, constraint = payer.is_some())] - pub optional_account: Option>, + #[account(mut, close = payer, has_one = data_account)] + pub optional_pda: Option>, + #[account(mut, close = payer, constraint = payer.is_some())] + pub data_account: Option>, pub system_program: Option>, } diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index e8f8378c5b..2ec71a5f32 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -18,17 +18,17 @@ mod optional { let optional_account = &mut ctx.accounts.optional_account; let required = &mut ctx.accounts.required; - required.data_pda = Pubkey::default(); + required.data = 0; - if let Some(data) = optional_pda { - data.data = value; + if let Some(data_account) = optional_account { + data_account.data = value; } - if let Some(data2) = optional_account { - if let Some(optional) = optional_pda { - data2.data_pda = optional.key(); + if let Some(data_pda) = optional_pda { + if let Some(data_account) = optional_account { + data_pda.data_account = data_account.key(); } else { - data2.data_pda = key; + data_pda.data_account = key; } } @@ -36,11 +36,11 @@ mod optional { } pub fn update(ctx: Context, value: u64, key: Pubkey, _pda_bump: u8) -> Result<()> { - if let Some(data_pda) = &mut ctx.accounts.optional_pda { - data_pda.data = value; - } if let Some(data_account) = &mut ctx.accounts.optional_account { - data_account.data_pda = key; + data_account.data = value; + } + if let Some(data_account) = &mut ctx.accounts.optional_pda { + data_account.data_account = key; } Ok(()) } @@ -57,8 +57,8 @@ mod optional { } pub fn close(ctx: Context) -> Result<()> { - if let Some(data_account) = &ctx.accounts.data_pda { - data_account.close(ctx.accounts.payer.as_ref().unwrap().to_account_info())?; + if let Some(data_pda) = &ctx.accounts.optional_pda { + data_pda.close(ctx.accounts.payer.as_ref().unwrap().to_account_info())?; } Ok(()) } From 0a7e988b0832e0cadedbf067ca2fbc4d99798f56 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 4 Oct 2022 20:08:43 -0500 Subject: [PATCH 073/109] update optional program and client tests again again --- client/example/src/main.rs | 2 +- tests/optional/programs/optional/src/lib.rs | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/client/example/src/main.rs b/client/example/src/main.rs index 108157e612..d8bdc68a69 100644 --- a/client/example/src/main.rs +++ b/client/example/src/main.rs @@ -300,7 +300,7 @@ fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { assert!(optional_pda.is_err()); let optional_account: DataAccount = program.account(data_account_keypair.pubkey())?; - assert_eq!(optional_account.data, value); + assert_eq!(optional_account.data, value * 2); println!("Optional success!"); diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index 2ec71a5f32..b1f86fb833 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -21,14 +21,11 @@ mod optional { required.data = 0; if let Some(data_account) = optional_account { - data_account.data = value; - } - - if let Some(data_pda) = optional_pda { - if let Some(data_account) = optional_account { - data_pda.data_account = data_account.key(); - } else { + if let Some(data_pda) = optional_pda { data_pda.data_account = key; + data_account.data = value; + } else { + data_account.data = value * 2; } } From b2d118cdea1fa0ce27c66a945cd84021475db448 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 4 Oct 2022 21:16:08 -0500 Subject: [PATCH 074/109] added initialize tests that should pass --- tests/optional/tests/optional.ts | 479 ++++++++++++++++++------------- tests/optional/tsconfig.json | 3 +- 2 files changed, 279 insertions(+), 203 deletions(-) diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index 76afa5296f..0155894f1c 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -1,11 +1,11 @@ import * as anchor from "@project-serum/anchor"; import { + Program, + web3, + BN, AnchorError, LangErrorCode, LangErrorMessage, - Program, - ProgramError, - web3, } from "@project-serum/anchor"; import { Optional } from "../target/types/optional"; import { assert, expect } from "chai"; @@ -13,235 +13,310 @@ import { assert, expect } from "chai"; describe("Optional", () => { // configure the client to use the local cluster anchor.setProvider(anchor.AnchorProvider.env()); - let optional2Keypair = web3.Keypair.generate(); - let requiredKeypair1 = web3.Keypair.generate(); - let requiredKeypair2 = web3.Keypair.generate(); const program = anchor.workspace.Optional as Program; - // payer of the transactions - const payer = (program.provider as anchor.AnchorProvider).wallet; - let initValue = new anchor.BN(10); - - // optional pda - let seeds = [Buffer.from("data1"), optional2Keypair.publicKey.toBuffer()]; - let optional1Pubkey = web3.PublicKey.findProgramAddressSync( - seeds, - program.programId - )[0]; - let createOptional2; - let createRequired2; - - it("Initialize with optionals null works", async () => { - let createRequired = await program.account.data2.createInstruction( - requiredKeypair1 - ); - await program.methods - .initialize(new anchor.BN(10), optional2Keypair.publicKey) - .preInstructions([createRequired]) - .accounts({ - payer: payer.publicKey, - systemProgram: web3.SystemProgram.programId, - required: requiredKeypair1.publicKey, - optional1: null, - optional2: null, - }) - .signers([requiredKeypair1]) - .rpc({ skipPreflight: true }); - - let required1 = await program.account.data2.fetchNullable( - requiredKeypair1.publicKey + const DATA_PDA_PREFIX = "data_pda"; + + const findDataPda = ( + dataAccount: web3.PublicKey + ): [web3.PublicKey, number] => { + return web3.PublicKey.findProgramAddressSync( + [Buffer.from(DATA_PDA_PREFIX), dataAccount.toBuffer()], + program.programId ); - expect(required1.optional1.toString()).to.equal( - web3.PublicKey.default.toString() + }; + + // payer of the transactions + const payerWallet = (program.provider as anchor.AnchorProvider).wallet; + const payer = payerWallet.publicKey; + const systemProgram = web3.SystemProgram.programId; + + let requiredKeypair1 = web3.Keypair.generate(); + let requiredKeypair2 = web3.Keypair.generate(); + + let createRequiredIx1: web3.TransactionInstruction; + let createRequiredIx2: web3.TransactionInstruction; + + let dataAccountKeypair1 = web3.Keypair.generate(); + let dataAccountKeypair2 = web3.Keypair.generate(); + + let dataPda1 = findDataPda(dataAccountKeypair1.publicKey); + let dataPda2 = findDataPda(dataAccountKeypair2.publicKey); + + const initializeValue1 = new BN(10); + const initializeValue2 = new BN(100); + const initializeKey = web3.PublicKey.default; + + const createRequired = async ( + requiredKeypair?: web3.Keypair + ): Promise<[web3.Keypair, web3.TransactionInstruction]> => { + const keypair = requiredKeypair ?? new web3.Keypair(); + const createIx = await program.account.dataAccount.createInstruction( + keypair ); + return [keypair, createIx]; + }; + + before("Setup async stuff", async () => { + createRequiredIx1 = (await createRequired(requiredKeypair1))[1]; + createRequiredIx2 = (await createRequired(requiredKeypair2))[1]; }); - it("Initialize missing optional2 fails", async () => { - try { - let x = await program.methods - .initialize(initValue, optional2Keypair.publicKey) - .preInstructions([await createRequired2]) + describe("Initialize tests", async () => { + it("Initialize with required null fails anchor-ts validation", async () => { + const [requiredKeypair, createRequiredIx] = await createRequired(); + try { + await program.methods + .initialize(initializeValue1, initializeKey) + .preInstructions([createRequiredIx]) + .accounts({ + payer, + systemProgram, + // @ts-ignore + required: null, //requiredKeypair.publicKey, + optionalPda: null, + optionalAccount: null, + }) + .signers([requiredKeypair]) + .rpc({ skipPreflight: true }); + assert.ok(false); + } catch (e) { + const errMsg = "Invalid arguments: required not provided"; + // @ts-ignore + let error: string = e.toString(); + assert(error.includes(errMsg), `Unexpected error: ${e}`); + } + }); + + it("Can initialize with no payer and no optionals", async () => { + const [requiredKeypair, createRequiredIx] = await createRequired(); + await program.methods + .initialize(initializeValue1, initializeKey) + .preInstructions([createRequiredIx]) .accounts({ - payer: payer.publicKey, - systemProgram: web3.SystemProgram.programId, - required: requiredKeypair2.publicKey, - optional1: optional1Pubkey, - optional2: null, + payer: null, + systemProgram, + required: requiredKeypair.publicKey, + optionalPda: null, + optionalAccount: null, }) - .signers([requiredKeypair2]) - .transaction(); - console.log("hi"); - assert.ok(false); - } catch (e) { - const errMsg1 = "ProgramFailedToComplete"; - const errMsg2 = "Program failed to complete"; - let error: string = e.toString(); - console.log("Error:", error); - assert( - error.includes(errMsg1) || error.includes(errMsg2), - "Program didn't fail to complete!!" - ); - } - }); + .signers([requiredKeypair]) + .rpc({ skipPreflight: true }); - it("Initialize missing payer fails", async () => { - createOptional2 = await program.account.data2.createInstruction( - optional2Keypair - ); - createRequired2 = await program.account.data2.createInstruction( - requiredKeypair2 - ); + let required = await program.account.dataAccount.fetch( + requiredKeypair.publicKey + ); + expect(required.data.toNumber()).to.equal(0); + }); - try { + it("Can initialize with no optionals", async () => { + const [requiredKeypair, createRequiredIx] = await createRequired(); await program.methods - .initialize(initValue, optional2Keypair.publicKey) - .preInstructions([createOptional2, createRequired2]) + .initialize(initializeValue1, initializeKey) + .preInstructions([createRequiredIx]) .accounts({ payer: null, - systemProgram: web3.SystemProgram.programId, - required: requiredKeypair2.publicKey, - optional1: optional1Pubkey, - optional2: optional2Keypair.publicKey, + systemProgram: null, + required: requiredKeypair.publicKey, + optionalPda: null, + optionalAccount: null, }) - .signers([requiredKeypair2, optional2Keypair]) + .signers([requiredKeypair]) .rpc({ skipPreflight: true }); - assert.ok(false); - } catch (e) { - assert.isTrue(e instanceof ProgramError); - const err: ProgramError = e; - assert.strictEqual( - err.msg, - LangErrorMessage.get(LangErrorCode.ConstraintAccountIsNone) + + let required = await program.account.dataAccount.fetch( + requiredKeypair.publicKey ); - assert.strictEqual(err.code, LangErrorCode.ConstraintAccountIsNone); - } - }); + expect(required.data.toNumber()).to.equal(0); + }); - it("Initialize with bad PDA fails", async () => { - try { - // bad optional pda - let seeds = [ - Buffer.from("fakedata1"), - optional2Keypair.publicKey.toBuffer(), - ]; - let badOptional1Pubkey = web3.PublicKey.findProgramAddressSync( - seeds, - program.programId - )[0]; + it("Initialize with optionals and missing system program fails optional checks", async () => { + const [requiredKeypair, createRequiredIx] = await createRequired(); + const dataAccount = new web3.Keypair(); + try { + await program.methods + .initialize(initializeValue1, initializeKey) + .preInstructions([createRequiredIx]) + .accounts({ + payer, + systemProgram: null, + required: requiredKeypair.publicKey, + optionalPda: null, + optionalAccount: dataAccount.publicKey, + }) + .signers([requiredKeypair, dataAccount]) + .rpc({ skipPreflight: true }); + assert.ok(false); + } catch (e) { + assert.isTrue(e instanceof AnchorError); + const err: AnchorError = e; + const errorCode = LangErrorCode.ConstraintAccountIsNone; + assert.strictEqual( + err.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(err.error.errorCode.number, errorCode); + } + }); + + it("Panics with reference to None account in constraint", async () => { + const [requiredKeypair, createRequiredIx] = await createRequired(); + const dataAccount = new web3.Keypair(); + const [dataPda] = findDataPda(dataAccount.publicKey); + try { + await program.methods + .initialize(initializeValue1, initializeKey) + .preInstructions([createRequiredIx]) + .accounts({ + payer, + systemProgram, + required: requiredKeypair.publicKey, + optionalPda: dataPda, + optionalAccount: null, + }) + .signers([requiredKeypair]) + .rpc({ skipPreflight: true }); + assert.ok(false); + } catch (e) { + const errMsg = "ProgramFailedToComplete"; + // @ts-ignore + let error: string = e.toString(); + assert(error.includes(errMsg), `Unexpected error: ${e}`); + } + }); + + it("Can initialize with required and optional account", async () => { await program.methods - .initialize(initValue, optional2Keypair.publicKey) - .preInstructions([createOptional2, createRequired2]) + .initialize(initializeValue1, initializeKey) + .preInstructions([createRequiredIx1]) .accounts({ - systemProgram: web3.SystemProgram.programId, - required: requiredKeypair2.publicKey, - optional1: badOptional1Pubkey, - optional2: optional2Keypair.publicKey, + payer, + systemProgram, + required: requiredKeypair1.publicKey, + optionalPda: null, + optionalAccount: dataAccountKeypair1.publicKey, }) - .signers([requiredKeypair2, optional2Keypair]) + .signers([requiredKeypair1, dataAccountKeypair1]) .rpc({ skipPreflight: true }); - assert.ok(false); - } catch (e) { - assert.isTrue(e instanceof ProgramError); - const err: ProgramError = e; - assert.strictEqual( - err.msg, - LangErrorMessage.get(LangErrorCode.ConstraintSeeds) + + const requiredDataAccount = await program.account.dataAccount.fetch( + requiredKeypair1.publicKey ); - assert.strictEqual(err.code, LangErrorCode.ConstraintSeeds); - } - }); + expect(requiredDataAccount.data.toNumber()).to.equal(0); - it("Initialize with all valid accounts works", async () => { - await program.methods - .initialize(initValue, optional2Keypair.publicKey) - .preInstructions([createOptional2, createRequired2]) - .accounts({ - payer: payer.publicKey, - systemProgram: web3.SystemProgram.programId, - required: requiredKeypair2.publicKey, - optional1: optional1Pubkey, - optional2: optional2Keypair.publicKey, - }) - .signers([requiredKeypair2, optional2Keypair]) - .rpc({ skipPreflight: true }); - - let required2 = await program.account.data2.fetchNullable( - requiredKeypair2.publicKey - ); - let optional1 = await program.account.data1.fetchNullable(optional1Pubkey); - let optional2 = await program.account.data2.fetchNullable( - optional2Keypair.publicKey - ); + const optionalDataAccount = await program.account.dataAccount.fetch( + dataAccountKeypair1.publicKey + ); + expect(optionalDataAccount.data.toNumber()).to.equal( + initializeValue1.muln(2).toNumber() + ); + }); - expect(optional1.data.toNumber()).to.equal(initValue.toNumber()); - expect(optional2.optional1.toString()).to.equal(optional1Pubkey.toString()); - expect(required2.optional1.toString()).to.equal( - web3.PublicKey.default.toString() - ); - }); + it("Initialize with everything with invalid seeds fails", async () => { + try { + await program.methods + .initialize(initializeValue2, initializeKey) + .preInstructions([createRequiredIx2]) + .accounts({ + payer, + systemProgram, + required: requiredKeypair2.publicKey, + optionalPda: dataPda1[0], + optionalAccount: dataAccountKeypair2.publicKey, + }) + .signers([requiredKeypair2, dataAccountKeypair2]) + .rpc({ skipPreflight: true }); + assert.ok(false); + } catch (e) { + assert.isTrue(e instanceof AnchorError); + const err: AnchorError = e; + const errorCode = LangErrorCode.ConstraintSeeds; + assert.strictEqual( + err.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(err.error.errorCode.number, errorCode); + } + }); - it("realloc_with_constraints", async () => { - try { + it("Initialize with everything succeeds", async () => { await program.methods - .realloc() + .initialize(initializeValue2, initializeKey) + .preInstructions([createRequiredIx2]) .accounts({ - payer: payer.publicKey, - optional1: optional1Pubkey, - required: optional2Keypair.publicKey, - systemProgram: null, + payer, + systemProgram, + required: requiredKeypair2.publicKey, + optionalPda: dataPda2[0], + optionalAccount: dataAccountKeypair2.publicKey, }) + .signers([requiredKeypair2, dataAccountKeypair2]) .rpc({ skipPreflight: true }); - assert.ok(false); - } catch (e) { - assert.isTrue(e instanceof AnchorError); - const err: AnchorError = e; - const errorCode = LangErrorCode.ConstraintHasOne; - assert.strictEqual( - err.error.errorMessage, - LangErrorMessage.get(errorCode) + const requiredDataAccount = await program.account.dataAccount.fetch( + requiredKeypair2.publicKey ); - assert.strictEqual(err.error.errorCode.number, errorCode); - } - // - // try { - // await program.methods - // .realloc() - // .accounts({ - // payer: payer.publicKey, - // optional1: optional1Pubkey, - // required: optional2Keypair.publicKey, - // systemProgram: null, - // }) - // .rpc({skipPreflight: true}); - // - // assert.ok(false); - // } catch (e) { - // assert.isTrue(e instanceof AnchorError); - // const err: AnchorError = e; - // const errorCode = LangErrorCode.ConstraintHasOne; - // assert.strictEqual(err.error.errorMessage, LangErrorMessage.get(errorCode)); - // assert.strictEqual(err.error.errorCode.number, errorCode); - // } - // - // try { - // await program.methods - // .realloc() - // .accounts({ - // payer: payer.publicKey, - // optional1: optional1Pubkey, - // required: optional2Keypair.publicKey, - // systemProgram: null, - // }) - // .rpc({skipPreflight: true}); - // - // assert.ok(false); - // } catch (e) { - // assert.isTrue(e instanceof AnchorError); - // const err: AnchorError = e; - // const errorCode = LangErrorCode.ConstraintHasOne; - // assert.strictEqual(err.error.errorMessage, LangErrorMessage.get(errorCode)); - // assert.strictEqual(err.error.errorCode.number, errorCode); - // } + expect(requiredDataAccount.data.toNumber()).to.equal(0); + + const optionalDataAccount = await program.account.dataAccount.fetch( + dataAccountKeypair2.publicKey + ); + expect(optionalDataAccount.data.toNumber()).to.equal( + initializeValue2.toNumber() + ); + + const optionalDataPda = await program.account.dataPda.fetch(dataPda2[0]); + expect(optionalDataPda.dataAccount.toString()).to.equal( + initializeKey.toString() + ); + }); }); + + // describe("Update tests", async () => { + // it("Initialize with required null fails anchor-ts validation", async () => { + // const [requiredKeypair, createRequiredIx] = await createRequired(); + // try { + // await program.methods + // .initialize(initializeValue1, initializeKey) + // .preInstructions([createRequiredIx]) + // .accounts({ + // payer, + // systemProgram, + // // @ts-ignore + // required: null, //requiredKeypair.publicKey, + // optionalPda: null, + // optionalAccount: null, + // }) + // .signers([requiredKeypair]) + // .rpc({ skipPreflight: true }); + // assert.ok(false); + // } catch (e) { + // const errMsg = "Invalid arguments: required not provided"; + // // @ts-ignore + // let error: string = e.toString(); + // assert(error.includes(errMsg), `Unexpected error: ${e}`); + // } + // }); + // + // it("Can initialize with no payer and no optionals", async () => { + // const [requiredKeypair, createRequiredIx] = await createRequired(); + // await program.methods + // .initialize(initializeValue1, initializeKey) + // .preInstructions([createRequiredIx]) + // .accounts({ + // payer: null, + // systemProgram, + // required: requiredKeypair.publicKey, + // optionalPda: null, + // optionalAccount: null, + // }) + // .signers([requiredKeypair]) + // .rpc({ skipPreflight: true }); + // + // let required = await program.account.dataAccount.fetch( + // requiredKeypair.publicKey + // ); + // expect(required.data.toNumber()).to.equal(0); + // }); + // }); }); diff --git a/tests/optional/tsconfig.json b/tests/optional/tsconfig.json index b3b6656d38..35eaf7330e 100644 --- a/tests/optional/tsconfig.json +++ b/tests/optional/tsconfig.json @@ -6,6 +6,7 @@ "module": "commonjs", "target": "es6", "esModuleInterop": true, - "skipLibCheck": true + "skipLibCheck": true, + "strict": true } } From 5323658a709b582989bb6c4671991d1fe9dd8553 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 4 Oct 2022 21:22:03 -0500 Subject: [PATCH 075/109] undo unrelated anchor.toml change --- tests/cashiers-check/Anchor.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/cashiers-check/Anchor.toml b/tests/cashiers-check/Anchor.toml index 8a6f8e3c06..5a733e1a35 100644 --- a/tests/cashiers-check/Anchor.toml +++ b/tests/cashiers-check/Anchor.toml @@ -9,6 +9,3 @@ cashiers_check = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" test = "yarn run mocha -t 1000000 tests/" [features] - -[test.validator] -limit_ledger_size = "100000" \ No newline at end of file From 47237ba63e63f49022aa0a0b6aaee4e600b97342 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Wed, 5 Oct 2022 20:37:58 -0500 Subject: [PATCH 076/109] update close on optional program and improve tests --- tests/optional/programs/optional/src/lib.rs | 5 +-- tests/optional/tests/optional.ts | 39 ++++++++++++--------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index b1f86fb833..32febb6332 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -53,10 +53,7 @@ mod optional { Ok(()) } - pub fn close(ctx: Context) -> Result<()> { - if let Some(data_pda) = &ctx.accounts.optional_pda { - data_pda.close(ctx.accounts.payer.as_ref().unwrap().to_account_info())?; - } + pub fn close(_ctx: Context) -> Result<()> { Ok(()) } } diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index 0155894f1c..ca8a9da722 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -78,8 +78,10 @@ describe("Optional", () => { optionalAccount: null, }) .signers([requiredKeypair]) - .rpc({ skipPreflight: true }); - assert.ok(false); + .rpc(); + assert.fail( + "Unexpected success in creating a transaction that should have failed at the client level" + ); } catch (e) { const errMsg = "Invalid arguments: required not provided"; // @ts-ignore @@ -101,7 +103,7 @@ describe("Optional", () => { optionalAccount: null, }) .signers([requiredKeypair]) - .rpc({ skipPreflight: true }); + .rpc(); let required = await program.account.dataAccount.fetch( requiredKeypair.publicKey @@ -122,7 +124,7 @@ describe("Optional", () => { optionalAccount: null, }) .signers([requiredKeypair]) - .rpc({ skipPreflight: true }); + .rpc(); let required = await program.account.dataAccount.fetch( requiredKeypair.publicKey @@ -145,8 +147,10 @@ describe("Optional", () => { optionalAccount: dataAccount.publicKey, }) .signers([requiredKeypair, dataAccount]) - .rpc({ skipPreflight: true }); - assert.ok(false); + .rpc(); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `ConstraintAccountIsNone` error" + ); } catch (e) { assert.isTrue(e instanceof AnchorError); const err: AnchorError = e; @@ -175,10 +179,12 @@ describe("Optional", () => { optionalAccount: null, }) .signers([requiredKeypair]) - .rpc({ skipPreflight: true }); - assert.ok(false); + .rpc(); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `ProgramFailedToComplete` error" + ); } catch (e) { - const errMsg = "ProgramFailedToComplete"; + const errMsg = "Program failed to complete"; // @ts-ignore let error: string = e.toString(); assert(error.includes(errMsg), `Unexpected error: ${e}`); @@ -197,7 +203,7 @@ describe("Optional", () => { optionalAccount: dataAccountKeypair1.publicKey, }) .signers([requiredKeypair1, dataAccountKeypair1]) - .rpc({ skipPreflight: true }); + .rpc(); const requiredDataAccount = await program.account.dataAccount.fetch( requiredKeypair1.publicKey @@ -225,8 +231,10 @@ describe("Optional", () => { optionalAccount: dataAccountKeypair2.publicKey, }) .signers([requiredKeypair2, dataAccountKeypair2]) - .rpc({ skipPreflight: true }); - assert.ok(false); + .rpc(); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `ConstraintSeeds` error" + ); } catch (e) { assert.isTrue(e instanceof AnchorError); const err: AnchorError = e; @@ -251,7 +259,7 @@ describe("Optional", () => { optionalAccount: dataAccountKeypair2.publicKey, }) .signers([requiredKeypair2, dataAccountKeypair2]) - .rpc({ skipPreflight: true }); + .rpc(); const requiredDataAccount = await program.account.dataAccount.fetch( requiredKeypair2.publicKey @@ -288,7 +296,7 @@ describe("Optional", () => { // optionalAccount: null, // }) // .signers([requiredKeypair]) - // .rpc({ skipPreflight: true }); + // .rpc(); // assert.ok(false); // } catch (e) { // const errMsg = "Invalid arguments: required not provided"; @@ -297,7 +305,6 @@ describe("Optional", () => { // assert(error.includes(errMsg), `Unexpected error: ${e}`); // } // }); - // // it("Can initialize with no payer and no optionals", async () => { // const [requiredKeypair, createRequiredIx] = await createRequired(); // await program.methods @@ -311,7 +318,7 @@ describe("Optional", () => { // optionalAccount: null, // }) // .signers([requiredKeypair]) - // .rpc({ skipPreflight: true }); + // .rpc(); // // let required = await program.account.dataAccount.fetch( // requiredKeypair.publicKey From e8b18dfbf860ca77476743816f56435936800796 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Wed, 12 Oct 2022 13:37:50 -0400 Subject: [PATCH 077/109] update optional program again. --- tests/optional/programs/optional/src/context.rs | 6 +++--- tests/optional/programs/optional/src/lib.rs | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/optional/programs/optional/src/context.rs b/tests/optional/programs/optional/src/context.rs index 3b02702300..f7817c1b4a 100644 --- a/tests/optional/programs/optional/src/context.rs +++ b/tests/optional/programs/optional/src/context.rs @@ -21,7 +21,7 @@ pub struct Update<'info> { pub payer: Option>, #[account(mut, seeds=[DataPda::PREFIX.as_ref(), optional_account.as_ref().unwrap().key().as_ref()], bump = pda_bump)] pub optional_pda: Option>, - #[account(mut, constraint = payer.is_some())] + #[account(mut, signer, constraint = payer.is_some())] pub optional_account: Option>, } @@ -33,7 +33,7 @@ pub struct Realloc<'info> { pub optional_pda: Option>, pub required: Account<'info, DataAccount>, pub system_program: Option>, - #[account(mut, realloc = 50, realloc::payer = payer, realloc::zero = false)] + #[account(mut, signer, realloc = 50, realloc::payer = payer, realloc::zero = false)] pub optional_account: Option>, } @@ -43,7 +43,7 @@ pub struct Close<'info> { pub payer: Option>, #[account(mut, close = payer, has_one = data_account)] pub optional_pda: Option>, - #[account(mut, close = payer, constraint = payer.is_some())] + #[account(mut, signer, close = payer, constraint = payer.is_some())] pub data_account: Option>, pub system_program: Option>, } diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index 32febb6332..1b05ab60b9 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -2,7 +2,6 @@ //! structs deriving `Accounts`. use anchor_lang::prelude::*; -use anchor_lang::AccountsClose; pub use context::*; pub mod account; From ec50c2adf4607e664fa64ee63a9044a26690bd1a Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Wed, 12 Oct 2022 23:28:41 -0400 Subject: [PATCH 078/109] update optional program and optional tests --- .../optional/programs/optional/src/context.rs | 9 +- tests/optional/programs/optional/src/lib.rs | 15 +- tests/optional/tests/optional.ts | 571 ++++++++++++++++-- 3 files changed, 529 insertions(+), 66 deletions(-) diff --git a/tests/optional/programs/optional/src/context.rs b/tests/optional/programs/optional/src/context.rs index f7817c1b4a..f408b74271 100644 --- a/tests/optional/programs/optional/src/context.rs +++ b/tests/optional/programs/optional/src/context.rs @@ -15,7 +15,7 @@ pub struct Initialize<'info> { } #[derive(Accounts)] -#[instruction(pda_bump: u8)] +#[instruction(value: u64, key: Pubkey, pda_bump: u8)] pub struct Update<'info> { #[account(mut)] pub payer: Option>, @@ -26,14 +26,15 @@ pub struct Update<'info> { } #[derive(Accounts)] +#[instruction(new_size: usize)] pub struct Realloc<'info> { #[account(mut)] pub payer: Option>, - #[account(mut, realloc = 50, realloc::payer = payer, realloc::zero = false)] + #[account(mut, realloc = new_size, realloc::payer = payer, realloc::zero = false)] pub optional_pda: Option>, pub required: Account<'info, DataAccount>, pub system_program: Option>, - #[account(mut, signer, realloc = 50, realloc::payer = payer, realloc::zero = false)] + #[account(mut, signer, realloc = new_size, realloc::payer = payer, realloc::zero = true)] pub optional_account: Option>, } @@ -43,7 +44,7 @@ pub struct Close<'info> { pub payer: Option>, #[account(mut, close = payer, has_one = data_account)] pub optional_pda: Option>, - #[account(mut, signer, close = payer, constraint = payer.is_some())] + #[account(mut, signer, close = payer)] pub data_account: Option>, pub system_program: Option>, } diff --git a/tests/optional/programs/optional/src/lib.rs b/tests/optional/programs/optional/src/lib.rs index 1b05ab60b9..9fefb1ce2f 100644 --- a/tests/optional/programs/optional/src/lib.rs +++ b/tests/optional/programs/optional/src/lib.rs @@ -41,11 +41,18 @@ mod optional { Ok(()) } - pub fn realloc(ctx: Context) -> Result<()> { + pub fn realloc(ctx: Context, new_size: u64) -> Result<()> { let optional_pda = &ctx.accounts.optional_pda; - if let Some(acc) = optional_pda { - let len = acc.to_account_info().data_len(); - if len != 50 { + let optional_account = &ctx.accounts.optional_account; + if let Some(data_pda) = optional_pda { + let len = data_pda.to_account_info().data_len(); + if len != new_size as usize { + return err!(OptionalErrors::ReallocFailed); + } + } + if let Some(data_account) = optional_account { + let len = data_account.to_account_info().data_len(); + if len != new_size as usize { return err!(OptionalErrors::ReallocFailed); } } diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index ca8a9da722..18d6d017d1 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -13,15 +13,20 @@ import { assert, expect } from "chai"; describe("Optional", () => { // configure the client to use the local cluster anchor.setProvider(anchor.AnchorProvider.env()); + const anchorProvider = anchor.AnchorProvider.env(); const program = anchor.workspace.Optional as Program; const DATA_PDA_PREFIX = "data_pda"; + const makeDataPdaSeeds = (dataAccount: web3.PublicKey) => { + return [Buffer.from(DATA_PDA_PREFIX), dataAccount.toBuffer()]; + }; + const findDataPda = ( dataAccount: web3.PublicKey ): [web3.PublicKey, number] => { return web3.PublicKey.findProgramAddressSync( - [Buffer.from(DATA_PDA_PREFIX), dataAccount.toBuffer()], + makeDataPdaSeeds(dataAccount), program.programId ); }; @@ -31,17 +36,17 @@ describe("Optional", () => { const payer = payerWallet.publicKey; const systemProgram = web3.SystemProgram.programId; - let requiredKeypair1 = web3.Keypair.generate(); - let requiredKeypair2 = web3.Keypair.generate(); + const requiredKeypair1 = web3.Keypair.generate(); + const requiredKeypair2 = web3.Keypair.generate(); let createRequiredIx1: web3.TransactionInstruction; let createRequiredIx2: web3.TransactionInstruction; - let dataAccountKeypair1 = web3.Keypair.generate(); - let dataAccountKeypair2 = web3.Keypair.generate(); + const dataAccountKeypair1 = web3.Keypair.generate(); + const dataAccountKeypair2 = web3.Keypair.generate(); - let dataPda1 = findDataPda(dataAccountKeypair1.publicKey); - let dataPda2 = findDataPda(dataAccountKeypair2.publicKey); + const dataPda1 = findDataPda(dataAccountKeypair1.publicKey); + const dataPda2 = findDataPda(dataAccountKeypair2.publicKey); const initializeValue1 = new BN(10); const initializeValue2 = new BN(100); @@ -152,7 +157,8 @@ describe("Optional", () => { "Unexpected success in creating a transaction that should have failed with `ConstraintAccountIsNone` error" ); } catch (e) { - assert.isTrue(e instanceof AnchorError); + // @ts-ignore + assert.isTrue(e instanceof AnchorError, e.toString()); const err: AnchorError = e; const errorCode = LangErrorCode.ConstraintAccountIsNone; assert.strictEqual( @@ -163,7 +169,7 @@ describe("Optional", () => { } }); - it("Panics with reference to None account in constraint", async () => { + it("Unwrapping None account in constraint panics", async () => { const [requiredKeypair, createRequiredIx] = await createRequired(); const dataAccount = new web3.Keypair(); const [dataPda] = findDataPda(dataAccount.publicKey); @@ -218,7 +224,7 @@ describe("Optional", () => { ); }); - it("Initialize with everything with invalid seeds fails", async () => { + it("Invalid seeds with all accounts provided fails", async () => { try { await program.methods .initialize(initializeValue2, initializeKey) @@ -236,7 +242,8 @@ describe("Optional", () => { "Unexpected success in creating a transaction that should have failed with `ConstraintSeeds` error" ); } catch (e) { - assert.isTrue(e instanceof AnchorError); + // @ts-ignore + assert.isTrue(e instanceof AnchorError, e.toString()); const err: AnchorError = e; const errorCode = LangErrorCode.ConstraintSeeds; assert.strictEqual( @@ -247,7 +254,7 @@ describe("Optional", () => { } }); - it("Initialize with everything succeeds", async () => { + it("Can initialize with all accounts provided", async () => { await program.methods .initialize(initializeValue2, initializeKey) .preInstructions([createRequiredIx2]) @@ -280,50 +287,498 @@ describe("Optional", () => { }); }); - // describe("Update tests", async () => { - // it("Initialize with required null fails anchor-ts validation", async () => { - // const [requiredKeypair, createRequiredIx] = await createRequired(); - // try { - // await program.methods - // .initialize(initializeValue1, initializeKey) - // .preInstructions([createRequiredIx]) - // .accounts({ - // payer, - // systemProgram, - // // @ts-ignore - // required: null, //requiredKeypair.publicKey, - // optionalPda: null, - // optionalAccount: null, - // }) - // .signers([requiredKeypair]) - // .rpc(); - // assert.ok(false); - // } catch (e) { - // const errMsg = "Invalid arguments: required not provided"; - // // @ts-ignore - // let error: string = e.toString(); - // assert(error.includes(errMsg), `Unexpected error: ${e}`); - // } - // }); - // it("Can initialize with no payer and no optionals", async () => { - // const [requiredKeypair, createRequiredIx] = await createRequired(); - // await program.methods - // .initialize(initializeValue1, initializeKey) - // .preInstructions([createRequiredIx]) - // .accounts({ - // payer: null, - // systemProgram, - // required: requiredKeypair.publicKey, - // optionalPda: null, - // optionalAccount: null, - // }) - // .signers([requiredKeypair]) - // .rpc(); - // - // let required = await program.account.dataAccount.fetch( - // requiredKeypair.publicKey - // ); - // expect(required.data.toNumber()).to.equal(0); - // }); - // }); + describe("Update tests", async () => { + it("Can update with invalid explicit pda bump with no pda", async () => { + await program.methods + .update(initializeValue2, initializeKey, dataPda2[1] - 1) + .accounts({ + payer, + optionalPda: null, + optionalAccount: null, + }) + .rpc(); + }); + + it("Errors with invalid explicit pda bump with pda included", async () => { + try { + await program.methods + .update(initializeValue2, initializeKey, dataPda2[1] - 1) + .accounts({ + payer, + optionalPda: dataPda2[0], + optionalAccount: dataAccountKeypair2.publicKey, + }) + .signers([dataAccountKeypair2]) + .rpc(); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `ConstraintSeeds` error" + ); + } catch (e) { + // @ts-ignore + assert.isTrue(e instanceof AnchorError, e.toString()); + const err: AnchorError = e; + const errorCode = LangErrorCode.ConstraintSeeds; + assert.strictEqual( + err.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(err.error.errorCode.number, errorCode); + } + }); + + it("Fails with a missing signer", async () => { + try { + let txn = await program.methods + .update(initializeValue2, initializeKey, dataPda2[1]) + .accounts({ + payer, + optionalPda: dataPda2[0], + optionalAccount: dataAccountKeypair2.publicKey, + }) + .transaction(); + txn.instructions[0].keys.forEach((meta) => { + if (meta.pubkey.equals(dataAccountKeypair2.publicKey)) { + meta.isSigner = false; + } + }); + await anchorProvider.sendAndConfirm(txn); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `ConstraintSigner` error" + ); + } catch (e) { + // @ts-ignore + assert.isTrue(e instanceof web3.SendTransactionError, e.toString()); + const err: web3.SendTransactionError = e; + const anchorError = AnchorError.parse(err.logs!)!; + const errorCode = LangErrorCode.ConstraintSigner; + assert.strictEqual( + anchorError.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(anchorError.error.errorCode.number, errorCode); + } + }); + + it("Can trigger raw constraint violations with references to optional accounts", async () => { + try { + await program.methods + .update(initializeValue2, initializeKey, dataPda2[1]) + .accounts({ + payer: null, + optionalPda: dataPda2[0], + optionalAccount: dataAccountKeypair2.publicKey, + }) + .signers([dataAccountKeypair2]) + .rpc(); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `ConstraintRaw` error" + ); + } catch (e) { + // @ts-ignore + assert.isTrue(e instanceof AnchorError, e.toString()); + const err: AnchorError = e; + const errorCode = LangErrorCode.ConstraintRaw; + assert.strictEqual( + err.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(err.error.errorCode.number, errorCode); + } + }); + + it("Can update an optional account", async () => { + await program.methods + .update(initializeValue2.muln(3), initializeKey, dataPda2[1]) + .accounts({ + payer, + optionalPda: null, + optionalAccount: dataAccountKeypair2.publicKey, + }) + .signers([dataAccountKeypair2]) + .rpc(); + + const dataAccount = await program.account.dataAccount.fetch( + dataAccountKeypair2.publicKey + ); + expect(dataAccount.data.toNumber()).to.equal( + initializeValue2.muln(3).toNumber() + ); + }); + + it("Can update both accounts", async () => { + const newKey = web3.PublicKey.unique(); + await program.methods + .update(initializeValue2, newKey, dataPda2[1]) + .accounts({ + payer, + optionalPda: dataPda2[0], + optionalAccount: dataAccountKeypair2.publicKey, + }) + .signers([dataAccountKeypair2]) + .rpc(); + + const dataPda = await program.account.dataPda.fetch(dataPda2[0]); + expect(dataPda.dataAccount.toString()).to.equal(newKey.toString()); + + const dataAccount = await program.account.dataAccount.fetch( + dataAccountKeypair2.publicKey + ); + expect(dataAccount.data.toNumber()).to.equal(initializeValue2.toNumber()); + }); + }); + + describe("Realloc tests", async () => { + it("Realloc with no payer fails", async () => { + try { + await program.methods + .realloc(new BN(100)) + .accounts({ + payer: null, + required: dataAccountKeypair1.publicKey, + optionalPda: null, + optionalAccount: dataAccountKeypair2.publicKey, + systemProgram, + }) + .signers([dataAccountKeypair2]) + .rpc(); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `ConstraintAccountIsNone` error" + ); + } catch (e) { + // @ts-ignore + assert.isTrue(e instanceof AnchorError, e.toString()); + const err: AnchorError = e; + const errorCode = LangErrorCode.ConstraintAccountIsNone; + assert.strictEqual( + err.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(err.error.errorCode.number, errorCode); + } + }); + + it("Realloc with no system program fails", async () => { + try { + await program.methods + .realloc(new BN(100)) + .accounts({ + payer, + required: dataAccountKeypair1.publicKey, + optionalPda: null, + optionalAccount: dataAccountKeypair2.publicKey, + systemProgram: null, + }) + .signers([dataAccountKeypair2]) + .rpc(); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `ConstraintAccountIsNone` error" + ); + } catch (e) { + // @ts-ignore + assert.isTrue(e instanceof AnchorError, e.toString()); + const err: AnchorError = e; + const errorCode = LangErrorCode.ConstraintAccountIsNone; + assert.strictEqual( + err.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(err.error.errorCode.number, errorCode); + } + }); + + it("Wrong type of account is caught for optional accounts", async () => { + try { + await program.methods + .realloc(new BN(100)) + .accounts({ + payer, + required: dataAccountKeypair1.publicKey, + optionalPda: dataAccountKeypair2.publicKey, + optionalAccount: null, + systemProgram, + }) + .rpc(); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `AccountDiscriminatorMismatch` error" + ); + } catch (e) { + // @ts-ignore + assert.isTrue(e instanceof AnchorError, e.toString()); + const err: AnchorError = e; + const errorCode = LangErrorCode.AccountDiscriminatorMismatch; + assert.strictEqual( + err.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(err.error.errorCode.number, errorCode); + } + }); + + it("Can realloc with optional accounts", async () => { + const newLength = 100; + await program.methods + .realloc(new BN(newLength)) + .accounts({ + payer, + required: dataAccountKeypair1.publicKey, + optionalPda: null, + optionalAccount: dataAccountKeypair2.publicKey, + systemProgram, + }) + .signers([dataAccountKeypair2]) + .rpc(); + const dataAccount = await program.provider.connection.getAccountInfo( + dataAccountKeypair2.publicKey + ); + assert.exists(dataAccount); + expect(dataAccount!.data.length).to.equal(newLength); + }); + + it("Can realloc back to original size with optional accounts", async () => { + const newLength = program.account.dataAccount.size; + await program.methods + .realloc(new BN(newLength)) + .accounts({ + payer, + required: dataAccountKeypair1.publicKey, + optionalPda: null, + optionalAccount: dataAccountKeypair2.publicKey, + systemProgram, + }) + .signers([dataAccountKeypair2]) + .rpc(); + const dataAccount = await program.provider.connection.getAccountInfo( + dataAccountKeypair2.publicKey + ); + assert.exists(dataAccount); + expect(dataAccount!.data.length).to.equal(newLength); + }); + + it("Can realloc multiple optional accounts", async () => { + const newLength = 100; + await program.methods + .realloc(new BN(newLength)) + .accounts({ + payer, + required: dataAccountKeypair1.publicKey, + optionalPda: dataPda2[0], + optionalAccount: dataAccountKeypair2.publicKey, + systemProgram, + }) + .signers([dataAccountKeypair2]) + .rpc(); + const dataAccount = await program.provider.connection.getAccountInfo( + dataAccountKeypair2.publicKey + ); + assert.exists(dataAccount); + expect(dataAccount!.data.length).to.equal(newLength); + + const dataPda = await program.provider.connection.getAccountInfo( + dataPda2[0] + ); + assert.exists(dataPda); + expect(dataPda!.data.length).to.equal(newLength); + }); + }); + + describe("Close tests", async () => { + const requiredKeypair3 = web3.Keypair.generate(); + const requiredKeypair4 = web3.Keypair.generate(); + + let createRequiredIx3: web3.TransactionInstruction; + let createRequiredIx4: web3.TransactionInstruction; + + const dataAccountKeypair3 = web3.Keypair.generate(); + const dataAccountKeypair4 = web3.Keypair.generate(); + + const dataPda3 = findDataPda(dataAccountKeypair3.publicKey); + const dataPda4 = findDataPda(dataAccountKeypair4.publicKey); + + const initializeValue3 = new BN(50); + const initializeValue4 = new BN(1000); + + before("Setup additional accounts", async () => { + createRequiredIx3 = (await createRequired(requiredKeypair3))[1]; + createRequiredIx4 = (await createRequired(requiredKeypair4))[1]; + const assertInitSuccess = async ( + requiredPubkey: web3.PublicKey, + dataPdaPubkey: web3.PublicKey, + dataAccountPubkey: web3.PublicKey, + initializeValue: BN + ) => { + const requiredDataAccount = await program.account.dataAccount.fetch( + requiredPubkey + ); + expect(requiredDataAccount.data.toNumber()).to.equal(0); + + const optionalDataAccount = await program.account.dataAccount.fetch( + dataAccountPubkey + ); + expect(optionalDataAccount.data.toNumber()).to.equal( + initializeValue.toNumber() + ); + + const optionalDataPda = await program.account.dataPda.fetch( + dataPdaPubkey + ); + expect(optionalDataPda.dataAccount.toString()).to.equal( + initializeKey.toString() + ); + }; + + await program.methods + .initialize(initializeValue3, initializeKey) + .preInstructions([createRequiredIx3]) + .accounts({ + payer, + systemProgram, + required: requiredKeypair3.publicKey, + optionalPda: dataPda3[0], + optionalAccount: dataAccountKeypair3.publicKey, + }) + .signers([requiredKeypair3, dataAccountKeypair3]) + .rpc(); + await assertInitSuccess( + requiredKeypair3.publicKey, + dataPda3[0], + dataAccountKeypair3.publicKey, + initializeValue3 + ); + await program.methods + .initialize(initializeValue4, initializeKey) + .preInstructions([createRequiredIx4]) + .accounts({ + payer, + systemProgram, + required: requiredKeypair4.publicKey, + optionalPda: dataPda4[0], + optionalAccount: dataAccountKeypair4.publicKey, + }) + .signers([requiredKeypair4, dataAccountKeypair4]) + .rpc(); + await assertInitSuccess( + requiredKeypair4.publicKey, + dataPda4[0], + dataAccountKeypair4.publicKey, + initializeValue4 + ); + + await program.methods + .update(initializeValue3, dataAccountKeypair3.publicKey, dataPda3[1]) + .accounts({ + payer, + optionalPda: dataPda3[0], + optionalAccount: dataAccountKeypair3.publicKey, + }) + .signers([dataAccountKeypair3]) + .rpc(); + const optionalPda3 = await program.account.dataPda.fetch(dataPda3[0]); + expect(optionalPda3.dataAccount.toString()).to.equal( + dataAccountKeypair3.publicKey.toString() + ); + await program.methods + .update(initializeValue4, dataAccountKeypair4.publicKey, dataPda4[1]) + .accounts({ + payer, + optionalPda: dataPda4[0], + optionalAccount: dataAccountKeypair4.publicKey, + }) + .signers([dataAccountKeypair4]) + .rpc(); + const optionalPda4 = await program.account.dataPda.fetch(dataPda4[0]); + expect(optionalPda4.dataAccount.toString()).to.equal( + dataAccountKeypair4.publicKey.toString() + ); + }); + + it("Close with no close target fails", async () => { + try { + await program.methods + .close() + .accounts({ + payer: null, + optionalPda: null, + dataAccount: dataAccountKeypair3.publicKey, + systemProgram, + }) + .signers([dataAccountKeypair3]) + .rpc(); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `ConstraintRaw` error" + ); + } catch (e) { + // @ts-ignore + assert.isTrue(e instanceof AnchorError, e.toString()); + const err: AnchorError = e; + const errorCode = LangErrorCode.ConstraintAccountIsNone; + assert.strictEqual( + err.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(err.error.errorCode.number, errorCode); + } + }); + + it("Has one constraints are caught with optional accounts", async () => { + try { + await program.methods + .close() + .accounts({ + payer, + optionalPda: dataPda4[0], + dataAccount: dataAccountKeypair3.publicKey, + systemProgram, + }) + .signers([dataAccountKeypair3]) + .rpc(); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `ConstraintHasOne` error" + ); + } catch (e) { + // @ts-ignore + assert.isTrue(e instanceof AnchorError, e.toString()); + const err: AnchorError = e; + const errorCode = LangErrorCode.ConstraintHasOne; + assert.strictEqual( + err.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(err.error.errorCode.number, errorCode); + } + }); + + it("Can close an optional account", async () => { + await program.methods + .close() + .accounts({ + payer, + optionalPda: null, + dataAccount: dataAccountKeypair3.publicKey, + systemProgram, + }) + .signers([dataAccountKeypair3]) + .rpc(); + const dataAccount = await program.provider.connection.getAccountInfo( + dataAccountKeypair3.publicKey + ); + assert.isNull(dataAccount); + }); + + it("Can close multiple optional accounts", async () => { + await program.methods + .close() + .accounts({ + payer, + optionalPda: dataPda4[0], + dataAccount: dataAccountKeypair4.publicKey, + systemProgram, + }) + .signers([dataAccountKeypair4]) + .rpc(); + const dataAccount = await program.provider.connection.getAccountInfo( + dataAccountKeypair4.publicKey + ); + assert.isNull(dataAccount); + }); + }); }); From 72162e6a68f3a37e5efe0d5b1537cd97eeea8e21 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Wed, 12 Oct 2022 23:30:24 -0400 Subject: [PATCH 079/109] fix has one error message --- ts/packages/anchor/src/error.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/packages/anchor/src/error.ts b/ts/packages/anchor/src/error.ts index ca24f3146d..0844efb120 100644 --- a/ts/packages/anchor/src/error.ts +++ b/ts/packages/anchor/src/error.ts @@ -409,7 +409,7 @@ export const LangErrorMessage = new Map([ // Constraints. [LangErrorCode.ConstraintMut, "A mut constraint was violated"], - [LangErrorCode.ConstraintHasOne, "A has_one constraint was violated"], + [LangErrorCode.ConstraintHasOne, "A has one constraint was violated"], [LangErrorCode.ConstraintSigner, "A signer constraint was violated"], [LangErrorCode.ConstraintRaw, "A raw constraint was violated"], [LangErrorCode.ConstraintOwner, "An owner constraint was violated"], From 81743ac9be6d0b910e204ca7c340747e12ed5c9b Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Wed, 12 Oct 2022 23:47:52 -0400 Subject: [PATCH 080/109] fix client example tests --- client/example/run-test.sh | 3 +-- client/example/src/main.rs | 18 +++--------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/client/example/run-test.sh b/client/example/run-test.sh index c0cde1fb3e..7abfb21955 100755 --- a/client/example/run-test.sh +++ b/client/example/run-test.sh @@ -37,14 +37,13 @@ main() { --bpf-program $basic_4_pid ../../examples/tutorial/basic-4/target/deploy/basic_4.so \ --bpf-program $events_pid ../../tests/events/target/deploy/events.so \ --bpf-program $optional_pid ../../tests/optional/target/deploy/optional.so \ - > test-validator.log & sleep 5 # # Run Test. # - cargo run -- --composite-pid $composite_pid --basic-2-pid $basic_2_pid --basic-4-pid $basic_4_pid --events-pid $events_pid --optional_pid $optional_pid + cargo run -- --composite-pid $composite_pid --basic-2-pid $basic_2_pid --basic-4-pid $basic_4_pid --events-pid $events_pid --optional-pid $optional_pid } cleanup() { diff --git a/client/example/src/main.rs b/client/example/src/main.rs index d8bdc68a69..fec95462d6 100644 --- a/client/example/src/main.rs +++ b/client/example/src/main.rs @@ -54,8 +54,6 @@ fn main() -> Result<()> { // Wallet and cluster params. let payer = read_keypair_file(&*shellexpand::tilde("~/.config/solana/id.json")) .expect("Example requires a keypair file"); - let payer_clone = read_keypair_file(&*shellexpand::tilde("~/.config/solana/id.json")) - .expect("Example requires a keypair file"); let url = Cluster::Custom( "http://localhost:8899".to_string(), "ws://127.0.0.1:8900".to_string(), @@ -69,7 +67,7 @@ fn main() -> Result<()> { basic_2(&client, opts.basic_2_pid)?; basic_4(&client, opts.basic_4_pid)?; events(&client, opts.events_pid)?; - optional(&client, opts.optional_pid, payer_clone)?; + optional(&client, opts.optional_pid)?; // Success. Ok(()) @@ -242,7 +240,7 @@ pub fn basic_4(client: &Client, pid: Pubkey) -> Result<()> { // Runs a client for tests/optional. // // Make sure to run a localnet with the program deploy to run this example. -fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { +fn optional(client: &Client, pid: Pubkey) -> Result<()> { // Program client. let program = client.program(pid); @@ -269,18 +267,8 @@ fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { DataAccount::LEN as u64, &program.id(), )) - .instruction(system_instruction::create_account( - &program.payer(), - &data_account_keypair.pubkey(), - program - .rpc() - .get_minimum_balance_for_rent_exemption(DataAccount::LEN)?, - DataAccount::LEN as u64, - &program.id(), - )) .signer(&data_account_keypair) .signer(&required_keypair) - .signer(&signer) .accounts(OptionalInitialize { payer: Some(program.payer()), required: required_keypair.pubkey(), @@ -290,7 +278,7 @@ fn optional(client: &Client, pid: Pubkey, signer: Keypair) -> Result<()> { }) .args(optional_instruction::Initialize { value, key: pid }) .send() - .unwrap_err(); + .unwrap(); // Assert the transaction worked. let required: DataAccount = program.account(required_keypair.pubkey())?; From a2865c92a3e2617be7a1dad5825f7045e5ea1b96 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 13 Oct 2022 00:02:49 -0400 Subject: [PATCH 081/109] update lockfile --- tests/yarn.lock | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/yarn.lock b/tests/yarn.lock index add4d922d1..66f12da11b 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -76,7 +76,7 @@ version "0.25.0" dependencies: "@project-serum/borsh" "^0.2.5" - "@solana/web3.js" "^1.54.1" + "@solana/web3.js" "^1.36.0" base64-js "^1.5.1" bn.js "^5.1.2" bs58 "^4.0.1" @@ -173,10 +173,10 @@ superstruct "^0.14.2" tweetnacl "^1.0.0" -"@solana/web3.js@^1.54.1": - version "1.54.1" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.54.1.tgz#c39ffa598beaa6d761ab55c78263d874f4441e14" - integrity sha512-/PViPDGxF6oZZidcILndlm0MdbuzBouiQcqxrAfiBZ4lHMntLE4U75KhC+205EkVnkgCC4/prkjKVeSnbkfzrw== +"@solana/web3.js@^1.36.0": + version "1.64.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.64.0.tgz#b7f5a976976039a0161242e94d6e1224ab5d30f9" + integrity sha512-AcFaoy48GxSmzBryVwB88C/UPJd/UQa+nFrO/uPc8ww6RCjanZY2vEZxdfTZub+q1NMUckwXpPwF32jJLe7SPA== dependencies: "@babel/runtime" "^7.12.5" "@noble/ed25519" "^1.7.0" @@ -190,7 +190,6 @@ buffer "6.0.1" fast-stable-stringify "^1.0.0" jayson "^3.4.4" - js-sha3 "^0.8.0" node-fetch "2" rpc-websockets "^7.5.0" superstruct "^0.14.2" From f931d8e631696d36742308fe278881c034b5c31b Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 13 Oct 2022 13:48:27 -0400 Subject: [PATCH 082/109] update lockfile --- ts/yarn.lock | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/ts/yarn.lock b/ts/yarn.lock index bb6f548553..620ddabf45 100644 --- a/ts/yarn.lock +++ b/ts/yarn.lock @@ -904,15 +904,12 @@ dependencies: buffer "~6.0.3" - - -"@solana/web3.js@*", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0": +"@solana/web3.js@*", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0": version "1.64.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.64.0.tgz#b7f5a976976039a0161242e94d6e1224ab5d30f9" integrity sha512-AcFaoy48GxSmzBryVwB88C/UPJd/UQa+nFrO/uPc8ww6RCjanZY2vEZxdfTZub+q1NMUckwXpPwF32jJLe7SPA== dependencies: "@babel/runtime" "^7.12.5" - "@ethersproject/sha2" "^5.5.0" "@noble/ed25519" "^1.7.0" "@noble/hashes" "^1.1.2" "@noble/secp256k1" "^1.6.3" @@ -924,13 +921,9 @@ buffer "6.0.1" fast-stable-stringify "^1.0.0" jayson "^3.4.4" - js-sha3 "^0.8.0" node-fetch "2" - react-native-url-polyfill "^1.3.0" rpc-websockets "^7.5.0" - secp256k1 "^4.0.2" superstruct "^0.14.2" - tweetnacl "^1.0.3" "@tootallnate/once@1": version "1.1.2" From 8d0f9197328e7008178281452ef1cec39984db4b Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 13 Oct 2022 17:23:06 -0400 Subject: [PATCH 083/109] regenerate lockfile --- tests/yarn.lock | 543 +++++++++++++----------------------------------- 1 file changed, 145 insertions(+), 398 deletions(-) diff --git a/tests/yarn.lock b/tests/yarn.lock index 93857b7002..8115e56e71 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -2,41 +2,13 @@ # yarn lockfile v1 -"@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" - integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.17.2": +"@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78" integrity sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA== dependencies: regenerator-runtime "^0.13.4" -"@ethersproject/bytes@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" - integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== - dependencies: - "@ethersproject/logger" "^5.5.0" - -"@ethersproject/logger@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" - integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== - -"@ethersproject/sha2@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" - integrity sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - hash.js "1.1.7" - "@noble/ed25519@^1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.1.tgz#6899660f6fbb97798a6fbd227227c4589a454724" @@ -91,15 +63,7 @@ superstruct "^0.15.4" toml "^3.0.0" -"@project-serum/borsh@^0.2.2": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.2.tgz#63e558f2d6eb6ab79086bf499dea94da3182498f" - integrity sha512-Ms+aWmGVW6bWd3b0+MWwoaYig2QD0F90h0uhr7AzY3dpCb5e2S6RsRW02vFTfa085pY2VLB7nTZNbFECQ1liTg== - dependencies: - bn.js "^5.1.2" - buffer-layout "^1.2.0" - -"@project-serum/borsh@^0.2.5": +"@project-serum/borsh@^0.2.2", "@project-serum/borsh@^0.2.5": version "0.2.5" resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663" integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q== @@ -117,9 +81,9 @@ superstruct "0.8.3" "@project-serum/serum@^0.13.21", "@project-serum/serum@^0.13.60": - version "0.13.60" - resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.60.tgz#abeb3355ebc1895d685250df5965f688502ebcbb" - integrity sha512-fGsp9F0ZAS48YQ2HNy+6CNoifJESFXxVsOLPd9QK1XNV8CTuQoECOnVXxV6s5cKGre8pLNq5hrhi5J6aCGauEQ== + version "0.13.65" + resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.65.tgz#6d3cf07912f13985765237f053cca716fe84b0b0" + integrity sha512-BHRqsTqPSfFB5p+MgI2pjvMBAQtO8ibTK2fYY96boIFkCI3TTwXDt2gUmspeChKO2pqHr5aKevmexzAcXxrSRA== dependencies: "@project-serum/anchor" "^0.11.1" "@solana/spl-token" "^0.1.6" @@ -127,13 +91,6 @@ bn.js "^5.1.2" buffer-layout "^1.2.0" -"@solana/buffer-layout@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-3.0.0.tgz#b9353caeb9a1589cb77a1b145bcb1a9a93114326" - integrity sha512-MVdgAKKL39tEs0l8je0hKaXLQFb7Rdfb0Xg2LjFZd8Lfdazkg6xiS98uAZrEKvaoF3i4M95ei9RydkGIDMeo3w== - dependencies: - buffer "~6.0.3" - "@solana/buffer-layout@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.0.tgz#75b1b11adc487234821c81dfae3119b73a5fd734" @@ -153,47 +110,7 @@ buffer-layout "^1.2.0" dotenv "10.0.0" -"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0": - version "1.30.2" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.30.2.tgz#e85da75e0825dc64f53eb64a1ff0115b27bec135" - integrity sha512-hznCj+rkfvM5taRP3Z+l5lumB7IQnDrB4l55Wpsg4kDU9Zds8pE5YOH5Z9bbF/pUzZJKQjyBjnY/6kScBm3Ugg== - dependencies: - "@babel/runtime" "^7.12.5" - "@ethersproject/sha2" "^5.5.0" - "@solana/buffer-layout" "^3.0.0" - bn.js "^5.0.0" - borsh "^0.4.0" - bs58 "^4.0.1" - buffer "6.0.1" - cross-fetch "^3.1.4" - jayson "^3.4.4" - js-sha3 "^0.8.0" - rpc-websockets "^7.4.2" - secp256k1 "^4.0.2" - superstruct "^0.14.2" - tweetnacl "^1.0.0" - -"@solana/web3.js@^1.36.0": - version "1.36.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.36.0.tgz#79d7d5217b49b80139f4de68953adc5b9a9a264f" - integrity sha512-RNT1451iRR7TyW7EJKMCrH/0OXawIe4zVm0DWQASwXlR/u1jmW6FrmH0lujIh7cGTlfOVbH+2ZU9AVUPLBFzwA== - dependencies: - "@babel/runtime" "^7.12.5" - "@ethersproject/sha2" "^5.5.0" - "@solana/buffer-layout" "^3.0.0" - bn.js "^5.0.0" - borsh "^0.4.0" - bs58 "^4.0.1" - buffer "6.0.1" - cross-fetch "^3.1.4" - jayson "^3.4.4" - js-sha3 "^0.8.0" - rpc-websockets "^7.4.2" - secp256k1 "^4.0.2" - superstruct "^0.14.2" - tweetnacl "^1.0.0" - -"@solana/web3.js@^1.64.0": +"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.64.0": version "1.64.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.64.0.tgz#b7f5a976976039a0161242e94d6e1224ab5d30f9" integrity sha512-AcFaoy48GxSmzBryVwB88C/UPJd/UQa+nFrO/uPc8ww6RCjanZY2vEZxdfTZub+q1NMUckwXpPwF32jJLe7SPA== @@ -214,17 +131,10 @@ rpc-websockets "^7.5.0" superstruct "^0.14.2" -"@types/bn.js@^4.11.5": - version "4.11.6" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" - integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== - dependencies: - "@types/node" "*" - "@types/chai@^4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc" - integrity sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw== + version "4.3.3" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.3.tgz#3c90752792660c4b562ad73b3fbd68bf3bc7ae07" + integrity sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g== "@types/connect@^3.4.33": version "3.4.35" @@ -233,54 +143,30 @@ dependencies: "@types/node" "*" -"@types/express-serve-static-core@^4.17.9": - version "4.17.25" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.25.tgz#e42f7046adc65ece2eb6059b77aecfbe9e9f82e0" - integrity sha512-OUJIVfRMFijZukGGwTpKNFprqCCXk5WjNGvUgB/CxxBR40QWSjsNK86+yvGKlCOGc7sbwfHLaXhkG+NsytwBaQ== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= - -"@types/lodash@^4.14.159": - version "4.14.176" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.176.tgz#641150fc1cda36fbfa329de603bbb175d7ee20c0" - integrity sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ== + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/mocha@^9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.0.tgz#baf17ab2cca3fcce2d322ebc30454bff487efad5" - integrity sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg== + version "9.1.1" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" + integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== "@types/node@*": - version "16.11.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42" - integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw== + version "18.8.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.8.5.tgz#6a31f820c1077c3f8ce44f9e203e68a176e8f59e" + integrity sha512-Bq7G3AErwe5A/Zki5fdD3O6+0zDChhg671NfPjtIcbtzDNZTv4NPKMRFr7gtYPG7y+B8uTiNK4Ngd9T0FTar6Q== "@types/node@^12.12.54": - version "12.20.37" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.37.tgz#abb38afa9d6e8a2f627a8cb52290b3c80fbe61ed" - integrity sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA== + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== "@types/node@^14.14.37": - version "14.17.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.33.tgz#011ee28e38dc7aee1be032ceadf6332a0ab15b12" - integrity sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g== - -"@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== - -"@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + version "14.18.32" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.32.tgz#8074f7106731f1a12ba993fe8bad86ee73905014" + integrity sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow== "@types/ws@^7.4.4": version "7.4.7" @@ -335,7 +221,7 @@ argparse@^2.0.1: arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== assertion-error@^1.1.0: version "1.1.0" @@ -378,31 +264,11 @@ bindings@^1.3.0: dependencies: file-uri-to-path "1.0.0" -bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== - -bn.js@^5.2.0: +bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2, bn.js@^5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -borsh@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.4.0.tgz#9dd6defe741627f1315eac2a73df61421f6ddb9f" - integrity sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g== - dependencies: - "@types/bn.js" "^4.11.5" - bn.js "^5.0.0" - bs58 "^4.0.0" - text-encoding-utf-8 "^1.0.2" - borsh@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" @@ -434,11 +300,6 @@ braces@~3.0.2: dependencies: fill-range "^7.0.1" -brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -447,7 +308,7 @@ browser-stdout@1.3.1: bs58@^4.0.0, bs58@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" - integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== dependencies: base-x "^3.0.2" @@ -478,9 +339,9 @@ buffer@6.0.3, buffer@~6.0.3: ieee754 "^1.2.1" bufferutil@^4.0.1: - version "4.0.5" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" - integrity sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A== + version "4.0.6" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.6.tgz#ebd6c67c7922a0e902f053e5d8be5ec850e48433" + integrity sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw== dependencies: node-gyp-build "^4.3.0" @@ -490,19 +351,20 @@ camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== chai@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" - integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== + version "4.3.6" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c" + integrity sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q== dependencies: assertion-error "^1.1.0" check-error "^1.0.2" deep-eql "^3.0.1" get-func-name "^2.0.0" + loupe "^2.3.1" pathval "^1.1.1" type-detect "^4.0.5" @@ -517,22 +379,7 @@ chalk@^4.1.0: check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= - -chokidar@3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== chokidar@3.5.3: version "3.5.3" @@ -549,11 +396,6 @@ chokidar@3.5.3: optionalDependencies: fsevents "~2.3.2" -circular-json@^0.5.9: - version "0.5.9" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d" - integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== - cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -583,14 +425,7 @@ commander@^2.20.3: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -cross-fetch@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" - integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== - dependencies: - node-fetch "2.6.1" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== cross-fetch@^3.1.5: version "3.1.5" @@ -604,10 +439,10 @@ crypto-hash@^1.3.0: resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247" integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg== -debug@4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== +debug@4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== dependencies: ms "2.1.2" @@ -658,19 +493,6 @@ dotenv@10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -elliptic@^6.5.2: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -684,7 +506,7 @@ es6-promise@^4.0.3: es6-promisify@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== dependencies: es6-promise "^4.0.3" @@ -706,7 +528,7 @@ eventemitter3@^4.0.7: eyes@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" - integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= + integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== fast-stable-stringify@^1.0.0: version "1.0.0" @@ -748,7 +570,7 @@ flat@^5.0.2: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.3.2: version "2.3.2" @@ -763,7 +585,7 @@ get-caller-file@^2.0.5: get-func-name@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== glob-parent@~5.1.2: version "5.1.2" @@ -772,18 +594,6 @@ glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob@7.1.7: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -806,28 +616,11 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -836,12 +629,12 @@ ieee754@^1.2.1: inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@^2.0.4: +inherits@2: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -856,7 +649,7 @@ is-binary-path@~2.1.0: is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -888,7 +681,7 @@ is-unicode-supported@^0.1.0: isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isomorphic-ws@^4.0.1: version "4.0.1" @@ -896,13 +689,11 @@ isomorphic-ws@^4.0.1: integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== jayson@^3.4.4: - version "3.6.5" - resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.6.5.tgz#e560bcad4daf098c7391f46ba8efc9d6f34a4102" - integrity sha512-wmOjX+eQcnCDyPF4KORomaIj9wj3h0B5VEbeD0+2VHfTfErB+h1zpR7oBkgCZp36AFjp3+a4CLz6U72BYpFHAw== + version "3.7.0" + resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.7.0.tgz#b735b12d06d348639ae8230d7a1e2916cb078f25" + integrity sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ== dependencies: "@types/connect" "^3.4.33" - "@types/express-serve-static-core" "^4.17.9" - "@types/lodash" "^4.14.159" "@types/node" "^12.12.54" "@types/ws" "^7.4.4" JSONStream "^1.3.5" @@ -913,7 +704,7 @@ jayson@^3.4.4: isomorphic-ws "^4.0.1" json-stringify-safe "^5.0.1" lodash "^4.17.20" - uuid "^3.4.0" + uuid "^8.3.2" ws "^7.4.5" js-sha256@^0.9.0: @@ -921,11 +712,6 @@ js-sha256@^0.9.0: resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== -js-sha3@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" - integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== - js-yaml@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -936,7 +722,7 @@ js-yaml@4.1.0: json-stringify-safe@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== json5@^1.0.1: version "1.0.1" @@ -948,7 +734,7 @@ json5@^1.0.1: jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== kind-of@^6.0.2: version "6.0.3" @@ -975,6 +761,13 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +loupe@^2.3.1: + version "2.3.4" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" + integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== + dependencies: + get-func-name "^2.0.0" + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -987,20 +780,10 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -minimatch@3.0.4, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" + integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== dependencies: brace-expansion "^1.1.7" @@ -1011,17 +794,24 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== mkdirp@^0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: - minimist "^1.2.5" + minimist "^1.2.6" mocha@^10.0.0: version "10.0.0" @@ -1052,31 +842,31 @@ mocha@^10.0.0: yargs-unparser "2.0.0" mocha@^9.1.3: - version "9.1.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.3.tgz#8a623be6b323810493d8c8f6f7667440fa469fdb" - integrity sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw== + version "9.2.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" + integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== dependencies: "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.5.2" - debug "4.3.2" + chokidar "3.5.3" + debug "4.3.3" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.1.7" + glob "7.2.0" growl "1.10.5" he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" - minimatch "3.0.4" + minimatch "4.2.1" ms "2.1.3" - nanoid "3.1.25" + nanoid "3.3.1" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" which "2.0.2" - workerpool "6.1.5" + workerpool "6.2.0" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" @@ -1091,10 +881,10 @@ ms@2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -nanoid@3.1.25: - version "3.1.25" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" - integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q== +nanoid@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== nanoid@3.3.3: version "3.3.3" @@ -1109,11 +899,6 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-addon-api@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" - integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== - node-fetch@2, node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -1121,15 +906,10 @@ node-fetch@2, node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" - integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== +node-gyp-build@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" + integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" @@ -1139,7 +919,7 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" @@ -1170,7 +950,7 @@ path-exists@^4.0.0: path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== pathval@^1.1.1: version "1.1.1" @@ -1178,14 +958,14 @@ pathval@^1.1.1: integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== picomatch@^2.0.4, picomatch@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== prettier@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" - integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== randombytes@^2.1.0: version "2.1.0" @@ -1202,28 +982,14 @@ readdirp@~3.6.0: picomatch "^2.2.1" regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + version "0.13.10" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" + integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -rpc-websockets@^7.4.2: - version "7.4.16" - resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.4.16.tgz#eb701cdef577d4357ba5f526d50e25f370396fac" - integrity sha512-0b7OVhutzwRIaYAtJo5tqtaQTWKfwAsKnaThOSOy+VkhVdleNUgb8eZnWSdWITRZZEigV5uPEIDr5KZe4DBrdQ== - dependencies: - "@babel/runtime" "^7.11.2" - circular-json "^0.5.9" - eventemitter3 "^4.0.7" - uuid "^8.3.0" - ws "^7.4.5" - optionalDependencies: - bufferutil "^4.0.1" - utf-8-validate "^5.0.2" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== rpc-websockets@^7.5.0: version "7.5.0" @@ -1243,15 +1009,6 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -secp256k1@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" - integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== - dependencies: - elliptic "^6.5.2" - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -1268,9 +1025,9 @@ snake-case@^3.0.4: tslib "^2.0.3" source-map-support@^0.5.6: - version "0.5.20" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" - integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -1299,7 +1056,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-json-comments@3.1.1: version "3.1.1" @@ -1346,12 +1103,12 @@ text-encoding-utf-8@^1.0.2: "through@>=2.2.7 <3": version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== tiny-invariant@^1.0.6: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" - integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== + version "1.3.1" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" + integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== to-regex-range@^5.0.1: version "5.0.1" @@ -1368,12 +1125,12 @@ toml@^3.0.0: tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== traverse-chain@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" - integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE= + integrity sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg== ts-mocha@^10.0.0: version "10.0.0" @@ -1404,24 +1161,19 @@ tsc@^2.0.4: integrity sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q== tsconfig-paths@^3.5.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz#954c1fe973da6339c78e06b03ce2e48810b65f36" - integrity sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA== + version "3.14.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" - minimist "^1.2.0" + minimist "^1.2.6" strip-bom "^3.0.0" tslib@^2.0.3: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - -tweetnacl@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" @@ -1429,23 +1181,18 @@ type-detect@^4.0.0, type-detect@^4.0.5: integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== typescript@^4.4.4: - version "4.4.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" - integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== + version "4.8.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" + integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== utf-8-validate@^5.0.2: - version "5.0.7" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.7.tgz#c15a19a6af1f7ad9ec7ddc425747ca28c3644922" - integrity sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q== + version "5.0.9" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.9.tgz#ba16a822fbeedff1a58918f2a6a6b36387493ea3" + integrity sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q== dependencies: node-gyp-build "^4.3.0" -uuid@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.3.0, uuid@^8.3.2: +uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -1453,12 +1200,12 @@ uuid@^8.3.0, uuid@^8.3.2: webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" @@ -1470,10 +1217,10 @@ which@2.0.2: dependencies: isexe "^2.0.0" -workerpool@6.1.5: - version "6.1.5" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581" - integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw== +workerpool@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== workerpool@6.2.1: version "6.2.1" @@ -1492,12 +1239,12 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== ws@^7.4.5: - version "7.5.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" - integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== ws@^8.5.0: version "8.9.0" @@ -1545,7 +1292,7 @@ yargs@16.2.0: yn@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" - integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= + integrity sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ== yocto-queue@^0.1.0: version "0.1.0" From f57ef24ded0baa38b0da88521e0502247964978a Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 13 Oct 2022 17:50:37 -0400 Subject: [PATCH 084/109] reset lockfile --- tests/yarn.lock | 543 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 398 insertions(+), 145 deletions(-) diff --git a/tests/yarn.lock b/tests/yarn.lock index 8115e56e71..93857b7002 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -2,13 +2,41 @@ # yarn lockfile v1 -"@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2": +"@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5": + version "7.16.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" + integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.17.2": version "7.19.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78" integrity sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA== dependencies: regenerator-runtime "^0.13.4" +"@ethersproject/bytes@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" + integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/logger@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" + integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== + +"@ethersproject/sha2@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" + integrity sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + hash.js "1.1.7" + "@noble/ed25519@^1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.1.tgz#6899660f6fbb97798a6fbd227227c4589a454724" @@ -63,7 +91,15 @@ superstruct "^0.15.4" toml "^3.0.0" -"@project-serum/borsh@^0.2.2", "@project-serum/borsh@^0.2.5": +"@project-serum/borsh@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.2.tgz#63e558f2d6eb6ab79086bf499dea94da3182498f" + integrity sha512-Ms+aWmGVW6bWd3b0+MWwoaYig2QD0F90h0uhr7AzY3dpCb5e2S6RsRW02vFTfa085pY2VLB7nTZNbFECQ1liTg== + dependencies: + bn.js "^5.1.2" + buffer-layout "^1.2.0" + +"@project-serum/borsh@^0.2.5": version "0.2.5" resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663" integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q== @@ -81,9 +117,9 @@ superstruct "0.8.3" "@project-serum/serum@^0.13.21", "@project-serum/serum@^0.13.60": - version "0.13.65" - resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.65.tgz#6d3cf07912f13985765237f053cca716fe84b0b0" - integrity sha512-BHRqsTqPSfFB5p+MgI2pjvMBAQtO8ibTK2fYY96boIFkCI3TTwXDt2gUmspeChKO2pqHr5aKevmexzAcXxrSRA== + version "0.13.60" + resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.60.tgz#abeb3355ebc1895d685250df5965f688502ebcbb" + integrity sha512-fGsp9F0ZAS48YQ2HNy+6CNoifJESFXxVsOLPd9QK1XNV8CTuQoECOnVXxV6s5cKGre8pLNq5hrhi5J6aCGauEQ== dependencies: "@project-serum/anchor" "^0.11.1" "@solana/spl-token" "^0.1.6" @@ -91,6 +127,13 @@ bn.js "^5.1.2" buffer-layout "^1.2.0" +"@solana/buffer-layout@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-3.0.0.tgz#b9353caeb9a1589cb77a1b145bcb1a9a93114326" + integrity sha512-MVdgAKKL39tEs0l8je0hKaXLQFb7Rdfb0Xg2LjFZd8Lfdazkg6xiS98uAZrEKvaoF3i4M95ei9RydkGIDMeo3w== + dependencies: + buffer "~6.0.3" + "@solana/buffer-layout@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.0.tgz#75b1b11adc487234821c81dfae3119b73a5fd734" @@ -110,7 +153,47 @@ buffer-layout "^1.2.0" dotenv "10.0.0" -"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.64.0": +"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0": + version "1.30.2" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.30.2.tgz#e85da75e0825dc64f53eb64a1ff0115b27bec135" + integrity sha512-hznCj+rkfvM5taRP3Z+l5lumB7IQnDrB4l55Wpsg4kDU9Zds8pE5YOH5Z9bbF/pUzZJKQjyBjnY/6kScBm3Ugg== + dependencies: + "@babel/runtime" "^7.12.5" + "@ethersproject/sha2" "^5.5.0" + "@solana/buffer-layout" "^3.0.0" + bn.js "^5.0.0" + borsh "^0.4.0" + bs58 "^4.0.1" + buffer "6.0.1" + cross-fetch "^3.1.4" + jayson "^3.4.4" + js-sha3 "^0.8.0" + rpc-websockets "^7.4.2" + secp256k1 "^4.0.2" + superstruct "^0.14.2" + tweetnacl "^1.0.0" + +"@solana/web3.js@^1.36.0": + version "1.36.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.36.0.tgz#79d7d5217b49b80139f4de68953adc5b9a9a264f" + integrity sha512-RNT1451iRR7TyW7EJKMCrH/0OXawIe4zVm0DWQASwXlR/u1jmW6FrmH0lujIh7cGTlfOVbH+2ZU9AVUPLBFzwA== + dependencies: + "@babel/runtime" "^7.12.5" + "@ethersproject/sha2" "^5.5.0" + "@solana/buffer-layout" "^3.0.0" + bn.js "^5.0.0" + borsh "^0.4.0" + bs58 "^4.0.1" + buffer "6.0.1" + cross-fetch "^3.1.4" + jayson "^3.4.4" + js-sha3 "^0.8.0" + rpc-websockets "^7.4.2" + secp256k1 "^4.0.2" + superstruct "^0.14.2" + tweetnacl "^1.0.0" + +"@solana/web3.js@^1.64.0": version "1.64.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.64.0.tgz#b7f5a976976039a0161242e94d6e1224ab5d30f9" integrity sha512-AcFaoy48GxSmzBryVwB88C/UPJd/UQa+nFrO/uPc8ww6RCjanZY2vEZxdfTZub+q1NMUckwXpPwF32jJLe7SPA== @@ -131,10 +214,17 @@ rpc-websockets "^7.5.0" superstruct "^0.14.2" +"@types/bn.js@^4.11.5": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + "@types/chai@^4.3.0": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.3.tgz#3c90752792660c4b562ad73b3fbd68bf3bc7ae07" - integrity sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g== + version "4.3.0" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc" + integrity sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw== "@types/connect@^3.4.33": version "3.4.35" @@ -143,30 +233,54 @@ dependencies: "@types/node" "*" +"@types/express-serve-static-core@^4.17.9": + version "4.17.25" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.25.tgz#e42f7046adc65ece2eb6059b77aecfbe9e9f82e0" + integrity sha512-OUJIVfRMFijZukGGwTpKNFprqCCXk5WjNGvUgB/CxxBR40QWSjsNK86+yvGKlCOGc7sbwfHLaXhkG+NsytwBaQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + +"@types/lodash@^4.14.159": + version "4.14.176" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.176.tgz#641150fc1cda36fbfa329de603bbb175d7ee20c0" + integrity sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ== "@types/mocha@^9.1.0": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" - integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== + version "9.1.0" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.0.tgz#baf17ab2cca3fcce2d322ebc30454bff487efad5" + integrity sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg== "@types/node@*": - version "18.8.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.8.5.tgz#6a31f820c1077c3f8ce44f9e203e68a176e8f59e" - integrity sha512-Bq7G3AErwe5A/Zki5fdD3O6+0zDChhg671NfPjtIcbtzDNZTv4NPKMRFr7gtYPG7y+B8uTiNK4Ngd9T0FTar6Q== + version "16.11.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42" + integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw== "@types/node@^12.12.54": - version "12.20.55" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" - integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + version "12.20.37" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.37.tgz#abb38afa9d6e8a2f627a8cb52290b3c80fbe61ed" + integrity sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA== "@types/node@^14.14.37": - version "14.18.32" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.32.tgz#8074f7106731f1a12ba993fe8bad86ee73905014" - integrity sha512-Y6S38pFr04yb13qqHf8uk1nHE3lXgQ30WZbv1mLliV9pt0NjvqdWttLcrOYLnXbOafknVYRHZGoMSpR9UwfYow== + version "14.17.33" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.33.tgz#011ee28e38dc7aee1be032ceadf6332a0ab15b12" + integrity sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g== + +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/ws@^7.4.4": version "7.4.7" @@ -221,7 +335,7 @@ argparse@^2.0.1: arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= assertion-error@^1.1.0: version "1.1.0" @@ -264,11 +378,31 @@ bindings@^1.3.0: dependencies: file-uri-to-path "1.0.0" -bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2, bn.js@^5.2.0: +bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + +bn.js@^5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== +borsh@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.4.0.tgz#9dd6defe741627f1315eac2a73df61421f6ddb9f" + integrity sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g== + dependencies: + "@types/bn.js" "^4.11.5" + bn.js "^5.0.0" + bs58 "^4.0.0" + text-encoding-utf-8 "^1.0.2" + borsh@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" @@ -300,6 +434,11 @@ braces@~3.0.2: dependencies: fill-range "^7.0.1" +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -308,7 +447,7 @@ browser-stdout@1.3.1: bs58@^4.0.0, bs58@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" - integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= dependencies: base-x "^3.0.2" @@ -339,9 +478,9 @@ buffer@6.0.3, buffer@~6.0.3: ieee754 "^1.2.1" bufferutil@^4.0.1: - version "4.0.6" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.6.tgz#ebd6c67c7922a0e902f053e5d8be5ec850e48433" - integrity sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw== + version "4.0.5" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" + integrity sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A== dependencies: node-gyp-build "^4.3.0" @@ -351,20 +490,19 @@ camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== chai@^4.3.4: - version "4.3.6" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c" - integrity sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q== + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== dependencies: assertion-error "^1.1.0" check-error "^1.0.2" deep-eql "^3.0.1" get-func-name "^2.0.0" - loupe "^2.3.1" pathval "^1.1.1" type-detect "^4.0.5" @@ -379,7 +517,22 @@ chalk@^4.1.0: check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +chokidar@3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" chokidar@3.5.3: version "3.5.3" @@ -396,6 +549,11 @@ chokidar@3.5.3: optionalDependencies: fsevents "~2.3.2" +circular-json@^0.5.9: + version "0.5.9" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d" + integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -425,7 +583,14 @@ commander@^2.20.3: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +cross-fetch@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" + integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== + dependencies: + node-fetch "2.6.1" cross-fetch@^3.1.5: version "3.1.5" @@ -439,10 +604,10 @@ crypto-hash@^1.3.0: resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247" integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg== -debug@4.3.3: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== +debug@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" @@ -493,6 +658,19 @@ dotenv@10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== +elliptic@^6.5.2: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -506,7 +684,7 @@ es6-promise@^4.0.3: es6-promisify@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= dependencies: es6-promise "^4.0.3" @@ -528,7 +706,7 @@ eventemitter3@^4.0.7: eyes@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" - integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== + integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= fast-stable-stringify@^1.0.0: version "1.0.0" @@ -570,7 +748,7 @@ flat@^5.0.2: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@~2.3.2: version "2.3.2" @@ -585,7 +763,7 @@ get-caller-file@^2.0.5: get-func-name@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= glob-parent@~5.1.2: version "5.1.2" @@ -594,6 +772,18 @@ glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob@7.1.7: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -616,11 +806,28 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -629,12 +836,12 @@ ieee754@^1.2.1: inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -649,7 +856,7 @@ is-binary-path@~2.1.0: is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -681,7 +888,7 @@ is-unicode-supported@^0.1.0: isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isomorphic-ws@^4.0.1: version "4.0.1" @@ -689,11 +896,13 @@ isomorphic-ws@^4.0.1: integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== jayson@^3.4.4: - version "3.7.0" - resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.7.0.tgz#b735b12d06d348639ae8230d7a1e2916cb078f25" - integrity sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ== + version "3.6.5" + resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.6.5.tgz#e560bcad4daf098c7391f46ba8efc9d6f34a4102" + integrity sha512-wmOjX+eQcnCDyPF4KORomaIj9wj3h0B5VEbeD0+2VHfTfErB+h1zpR7oBkgCZp36AFjp3+a4CLz6U72BYpFHAw== dependencies: "@types/connect" "^3.4.33" + "@types/express-serve-static-core" "^4.17.9" + "@types/lodash" "^4.14.159" "@types/node" "^12.12.54" "@types/ws" "^7.4.4" JSONStream "^1.3.5" @@ -704,7 +913,7 @@ jayson@^3.4.4: isomorphic-ws "^4.0.1" json-stringify-safe "^5.0.1" lodash "^4.17.20" - uuid "^8.3.2" + uuid "^3.4.0" ws "^7.4.5" js-sha256@^0.9.0: @@ -712,6 +921,11 @@ js-sha256@^0.9.0: resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + js-yaml@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -722,7 +936,7 @@ js-yaml@4.1.0: json-stringify-safe@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@^1.0.1: version "1.0.1" @@ -734,7 +948,7 @@ json5@^1.0.1: jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= kind-of@^6.0.2: version "6.0.3" @@ -761,13 +975,6 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -loupe@^2.3.1: - version "2.3.4" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.4.tgz#7e0b9bffc76f148f9be769cb1321d3dcf3cb25f3" - integrity sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ== - dependencies: - get-func-name "^2.0.0" - lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -780,10 +987,20 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -minimatch@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" - integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" @@ -794,24 +1011,17 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^3.0.4: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mkdirp@^0.5.1: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: - minimist "^1.2.6" + minimist "^1.2.5" mocha@^10.0.0: version "10.0.0" @@ -842,31 +1052,31 @@ mocha@^10.0.0: yargs-unparser "2.0.0" mocha@^9.1.3: - version "9.2.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" - integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== + version "9.1.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.3.tgz#8a623be6b323810493d8c8f6f7667440fa469fdb" + integrity sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw== dependencies: "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.5.3" - debug "4.3.3" + chokidar "3.5.2" + debug "4.3.2" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.2.0" + glob "7.1.7" growl "1.10.5" he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" - minimatch "4.2.1" + minimatch "3.0.4" ms "2.1.3" - nanoid "3.3.1" + nanoid "3.1.25" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" which "2.0.2" - workerpool "6.2.0" + workerpool "6.1.5" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" @@ -881,10 +1091,10 @@ ms@2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -nanoid@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" - integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== +nanoid@3.1.25: + version "3.1.25" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" + integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q== nanoid@3.3.3: version "3.3.3" @@ -899,6 +1109,11 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + node-fetch@2, node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -906,10 +1121,15 @@ node-fetch@2, node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -node-gyp-build@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" + integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" @@ -919,7 +1139,7 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" @@ -950,7 +1170,7 @@ path-exists@^4.0.0: path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= pathval@^1.1.1: version "1.1.1" @@ -958,14 +1178,14 @@ pathval@^1.1.1: integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== picomatch@^2.0.4, picomatch@^2.2.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== prettier@^2.5.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== randombytes@^2.1.0: version "2.1.0" @@ -982,14 +1202,28 @@ readdirp@~3.6.0: picomatch "^2.2.1" regenerator-runtime@^0.13.4: - version "0.13.10" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" - integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +rpc-websockets@^7.4.2: + version "7.4.16" + resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.4.16.tgz#eb701cdef577d4357ba5f526d50e25f370396fac" + integrity sha512-0b7OVhutzwRIaYAtJo5tqtaQTWKfwAsKnaThOSOy+VkhVdleNUgb8eZnWSdWITRZZEigV5uPEIDr5KZe4DBrdQ== + dependencies: + "@babel/runtime" "^7.11.2" + circular-json "^0.5.9" + eventemitter3 "^4.0.7" + uuid "^8.3.0" + ws "^7.4.5" + optionalDependencies: + bufferutil "^4.0.1" + utf-8-validate "^5.0.2" rpc-websockets@^7.5.0: version "7.5.0" @@ -1009,6 +1243,15 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +secp256k1@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" + integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== + dependencies: + elliptic "^6.5.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -1025,9 +1268,9 @@ snake-case@^3.0.4: tslib "^2.0.3" source-map-support@^0.5.6: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + version "0.5.20" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" + integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -1056,7 +1299,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-json-comments@3.1.1: version "3.1.1" @@ -1103,12 +1346,12 @@ text-encoding-utf-8@^1.0.2: "through@>=2.2.7 <3": version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= tiny-invariant@^1.0.6: - version "1.3.1" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" - integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== + version "1.2.0" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" + integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== to-regex-range@^5.0.1: version "5.0.1" @@ -1125,12 +1368,12 @@ toml@^3.0.0: tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= traverse-chain@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" - integrity sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg== + integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE= ts-mocha@^10.0.0: version "10.0.0" @@ -1161,19 +1404,24 @@ tsc@^2.0.4: integrity sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q== tsconfig-paths@^3.5.0: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + version "3.11.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz#954c1fe973da6339c78e06b03ce2e48810b65f36" + integrity sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" - minimist "^1.2.6" + minimist "^1.2.0" strip-bom "^3.0.0" tslib@^2.0.3: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + +tweetnacl@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" @@ -1181,18 +1429,23 @@ type-detect@^4.0.0, type-detect@^4.0.5: integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== typescript@^4.4.4: - version "4.8.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" - integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== + version "4.4.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" + integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== utf-8-validate@^5.0.2: - version "5.0.9" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.9.tgz#ba16a822fbeedff1a58918f2a6a6b36387493ea3" - integrity sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q== + version "5.0.7" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.7.tgz#c15a19a6af1f7ad9ec7ddc425747ca28c3644922" + integrity sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q== dependencies: node-gyp-build "^4.3.0" -uuid@^8.3.2: +uuid@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -1200,12 +1453,12 @@ uuid@^8.3.2: webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" @@ -1217,10 +1470,10 @@ which@2.0.2: dependencies: isexe "^2.0.0" -workerpool@6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" - integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== +workerpool@6.1.5: + version "6.1.5" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581" + integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw== workerpool@6.2.1: version "6.2.1" @@ -1239,12 +1492,12 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= ws@^7.4.5: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + version "7.5.5" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" + integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== ws@^8.5.0: version "8.9.0" @@ -1292,7 +1545,7 @@ yargs@16.2.0: yn@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" - integrity sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ== + integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= yocto-queue@^0.1.0: version "0.1.0" From 3bcd4aacff27cc568b6d9c65452eac20e597ecc7 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 13 Oct 2022 17:52:54 -0400 Subject: [PATCH 085/109] reset ts yarn lockfile --- ts/yarn.lock | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ts/yarn.lock b/ts/yarn.lock index 620ddabf45..92844d02d3 100644 --- a/ts/yarn.lock +++ b/ts/yarn.lock @@ -904,12 +904,15 @@ dependencies: buffer "~6.0.3" + + "@solana/web3.js@*", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0": version "1.64.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.64.0.tgz#b7f5a976976039a0161242e94d6e1224ab5d30f9" integrity sha512-AcFaoy48GxSmzBryVwB88C/UPJd/UQa+nFrO/uPc8ww6RCjanZY2vEZxdfTZub+q1NMUckwXpPwF32jJLe7SPA== dependencies: "@babel/runtime" "^7.12.5" + "@ethersproject/sha2" "^5.5.0" "@noble/ed25519" "^1.7.0" "@noble/hashes" "^1.1.2" "@noble/secp256k1" "^1.6.3" @@ -921,9 +924,13 @@ buffer "6.0.1" fast-stable-stringify "^1.0.0" jayson "^3.4.4" + js-sha3 "^0.8.0" node-fetch "2" + react-native-url-polyfill "^1.3.0" rpc-websockets "^7.5.0" + secp256k1 "^4.0.2" superstruct "^0.14.2" + tweetnacl "^1.0.3" "@tootallnate/once@1": version "1.1.2" From f9b447eee9a6873d5e75aea586d7e092d9a14ae6 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 13 Oct 2022 18:15:45 -0400 Subject: [PATCH 086/109] update no caching tests --- .github/workflows/no-cashing-tests.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/no-cashing-tests.yaml b/.github/workflows/no-cashing-tests.yaml index 4943097cbc..bb30de6578 100644 --- a/.github/workflows/no-cashing-tests.yaml +++ b/.github/workflows/no-cashing-tests.yaml @@ -293,6 +293,8 @@ jobs: path: tests/cpi-returns - cmd: cd tests/multiple-suites && anchor test --skip-lint && npx tsc --noEmit path: tests/multiple-suites + - cmd: cd tests/optional && anchor test --skip-lint && npx tsc --noEmit + path: tests/optional - cmd: cd tests/pda-derivation && anchor test --skip-lint && npx tsc --noEmit path: tests/pda-derivation - cmd: cd tests/relations-derivation && anchor test --skip-lint && npx tsc --noEmit From acfd755f78a87b6a6595a28e59d5d7ddd888086b Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 18 Oct 2022 01:24:38 -0400 Subject: [PATCH 087/109] update exit codegen to use generate_optional_check --- lang/syn/src/codegen/accounts/constraints.rs | 4 +-- lang/syn/src/codegen/accounts/exit.rs | 28 ++++++++------------ 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index a2cf623b83..07bdae6468 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -1021,8 +1021,8 @@ pub fn generate_optional_check( let field_name = field_name.to_string(); if accs.is_field_optional(&field) { quote! { - let #field = if let Some(#field) = &#field { - #field + let #field = if let Some(account) = &#field { + account } else { return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAccountIsNone).with_account_name(#field_name)); }; diff --git a/lang/syn/src/codegen/accounts/exit.rs b/lang/syn/src/codegen/accounts/exit.rs index 6938ba7281..fa5716266e 100644 --- a/lang/syn/src/codegen/accounts/exit.rs +++ b/lang/syn/src/codegen/accounts/exit.rs @@ -1,6 +1,7 @@ +use crate::accounts_codegen::constraints::generate_optional_check; use crate::codegen::accounts::{generics, ParsedGenerics}; use crate::{AccountField, AccountsStruct}; -use quote::quote; +use quote::{quote, ToTokens}; // Generates the `Exit` trait implementation. pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { @@ -29,27 +30,20 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { let name_str = ident.to_string(); if f.constraints.is_close() { let close_target = &f.constraints.close.as_ref().unwrap().sol_dest; - let close_target_optional_info = if accs.is_field_optional(close_target) { - let close_target_name = close_target.to_string(); - quote! { - let close_target_info = if let Some(close_target) = &self.#close_target { - close_target.to_account_info() - } else { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAccountIsNone).with_account_name(#close_target_name)); - }; - } - } else { - quote! { - let close_target_info = self.#close_target.to_account_info(); - } - }; + let close_target_name = close_target.to_string(); + let close_target_optional_check = generate_optional_check( + accs, + close_target.to_token_stream(), + &close_target_name, + ); quote! { { - #close_target_optional_info + let #close_target = &self.#close_target; + #close_target_optional_check anchor_lang::AccountsClose::close( &self.#ident, - close_target_info, + #close_target.to_account_info(), ).map_err(|e| e.with_account_name(#name_str))?; } } From 010969661afffa6b341c4f2e045d4232143051c5 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 18 Oct 2022 02:48:59 -0400 Subject: [PATCH 088/109] remove `try_to_account_infos` --- lang/attribute/interface/src/lib.rs | 2 +- lang/src/accounts/option.rs | 13 ++----------- lang/src/context.rs | 12 ++---------- lang/src/lib.rs | 7 ------- lang/src/vec.rs | 6 ------ .../codegen/accounts/__cpi_client_accounts.rs | 17 ----------------- .../src/codegen/accounts/to_account_infos.rs | 16 ---------------- lang/syn/src/codegen/program/cpi.rs | 6 ++---- 8 files changed, 7 insertions(+), 72 deletions(-) diff --git a/lang/attribute/interface/src/lib.rs b/lang/attribute/interface/src/lib.rs index d87931816d..c2656eb554 100644 --- a/lang/attribute/interface/src/lib.rs +++ b/lang/attribute/interface/src/lib.rs @@ -218,7 +218,7 @@ pub fn interface( data, } }; - let mut acc_infos = ctx.try_to_account_infos(&ctx.program); + let mut acc_infos = ctx.to_account_infos(); acc_infos.push(ctx.program.clone()); anchor_lang::solana_program::program::invoke_signed( &ix, diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index 04e7e48998..c06cd97e27 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -50,17 +50,8 @@ impl<'info, T: Accounts<'info>> Accounts<'info> for Option { impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option { fn to_account_infos(&self) -> Vec> { - match self { - Some(account) => account.to_account_infos(), - None => panic!("Cannot run `to_account_infos` on None"), - } - } - - fn try_to_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { - match self { - Some(_) => self.to_account_infos(), - None => vec![program.clone()], - } + self.as_ref() + .map_or_else(Vec::new, |account| account.to_account_infos()) } } diff --git a/lang/src/context.rs b/lang/src/context.rs index 494d3cf9cb..f007bc5102 100644 --- a/lang/src/context.rs +++ b/lang/src/context.rs @@ -216,8 +216,7 @@ impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountInfos<'info> for CpiContext<'_, '_, '_, 'info, T> { fn to_account_infos(&self) -> Vec> { - // always uses try_account_infos in case there are optional accounts - let mut infos = self.accounts.try_to_account_infos(&self.program); + let mut infos = self.accounts.to_account_infos(); infos.extend_from_slice(&self.remaining_accounts); infos.push(self.program.clone()); infos @@ -318,14 +317,7 @@ 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.try_to_account_infos(self.program()); - infos.push(self.state.clone()); - infos.push(self.cpi_ctx.program.clone()); - infos - } - - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - let mut infos = self.cpi_ctx.accounts.try_to_account_infos(self.program()); + 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/lib.rs b/lang/src/lib.rs index fc458ca830..2d795a42c2 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -119,13 +119,6 @@ pub trait ToAccountMetas { /// structs. pub trait ToAccountInfos<'info> { fn to_account_infos(&self) -> Vec>; - - /// Intended for `Accounts` structs with optional accounts in order to - /// pass in the program `AccountInfo` as a sentinel value. When the account - /// is not present, it returns a copy of the program's `AccountInfo` instead. - fn try_to_account_infos(&self, _program: &AccountInfo<'info>) -> Vec> { - self.to_account_infos() - } } /// Transformation to an `AccountInfo` struct. diff --git a/lang/src/vec.rs b/lang/src/vec.rs index d934dfdb87..44c132be6b 100644 --- a/lang/src/vec.rs +++ b/lang/src/vec.rs @@ -10,12 +10,6 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Vec { .flat_map(|item| item.to_account_infos()) .collect() } - - fn try_to_account_infos(&self, program: &AccountInfo<'info>) -> Vec> { - self.iter() - .flat_map(|item| item.try_to_account_infos(program)) - .collect() - } } impl ToAccountMetas for Vec { diff --git a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs index e7195621ae..f1cdc36d21 100644 --- a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs +++ b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs @@ -129,17 +129,6 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { }) .collect(); - let account_struct_try_infos: Vec = accs - .fields - .iter() - .map(|f: &AccountField| { - let name = &f.ident(); - quote! { - try_account_infos.extend(anchor_lang::ToAccountInfos::try_to_account_infos(&self.#name, program)); - } - }) - .collect(); - // Re-export all composite account structs (i.e. other structs deriving // accounts embedded into this struct. Required because, these embedded // structs are *not* visible from the #[program] macro, which is responsible @@ -213,12 +202,6 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { #(#account_struct_infos)* account_infos } - - fn try_to_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { - let mut try_account_infos = vec![]; - #(#account_struct_try_infos)* - try_account_infos - } } } } diff --git a/lang/syn/src/codegen/accounts/to_account_infos.rs b/lang/syn/src/codegen/accounts/to_account_infos.rs index c50d76362a..938b22fc38 100644 --- a/lang/syn/src/codegen/accounts/to_account_infos.rs +++ b/lang/syn/src/codegen/accounts/to_account_infos.rs @@ -20,14 +20,6 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { quote! { account_infos.extend(self.#name.to_account_infos()); } }) .collect(); - let try_acc_infos: Vec = accs - .fields - .iter() - .map(|f: &AccountField| { - let name = &f.ident(); - quote! { account_infos.extend(self.#name.try_to_account_infos(program)); } - }) - .collect(); quote! { #[automatically_derived] impl<#combined_generics> anchor_lang::ToAccountInfos<#trait_generics> for #name <#struct_generics> #where_clause{ @@ -38,14 +30,6 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { account_infos } - - fn try_to_account_infos(&self, program: &anchor_lang::solana_program::account_info::AccountInfo<'info>) -> Vec> { - let mut account_infos = vec![]; - - #(#try_acc_infos)* - - account_infos - } } } } diff --git a/lang/syn/src/codegen/program/cpi.rs b/lang/syn/src/codegen/program/cpi.rs index 2b6be402c1..aa2aec5631 100644 --- a/lang/syn/src/codegen/program/cpi.rs +++ b/lang/syn/src/codegen/program/cpi.rs @@ -42,8 +42,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { data, } }; - // always use try_account_info in case there are optional accounts - let mut acc_infos = ctx.try_to_account_infos(ctx.program()); + let mut acc_infos = ctx.to_account_infos(); anchor_lang::solana_program::program::invoke_signed( &ix, &acc_infos, @@ -98,8 +97,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream { data, } }; - // always use try_account_info in case there are optional accounts - let mut acc_infos = ctx.try_to_account_infos(&ctx.program); + let mut acc_infos = ctx.to_account_infos(); anchor_lang::solana_program::program::invoke_signed( &ix, &acc_infos, From 32875eb979d2595ba019c488a76a4eae7c04cef2 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 20 Oct 2022 23:22:46 -0400 Subject: [PATCH 089/109] update parser to ignore method calls in constraints --- lang/syn/src/parser/accounts/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lang/syn/src/parser/accounts/mod.rs b/lang/syn/src/parser/accounts/mod.rs index bc72d4da30..5199a67d01 100644 --- a/lang/syn/src/parser/accounts/mod.rs +++ b/lang/syn/src/parser/accounts/mod.rs @@ -122,6 +122,8 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { let associated_payer_name = match field.constraints.init.clone().unwrap().payer { // composite payer, check not supported Expr::Field(_) => continue, + // method call, check not supported + Expr::MethodCall(_) => continue, field_name => field_name.to_token_stream().to_string(), }; @@ -204,6 +206,8 @@ fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { let associated_payer_name = match field.constraints.realloc.clone().unwrap().payer { // composite allocator, check not supported Expr::Field(_) => continue, + // method call, check not supported + Expr::MethodCall(_) => continue, field_name => field_name.to_token_stream().to_string(), }; From 3dc7566ccb5ea29520c9de7c24afd0328060f80e Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 20 Oct 2022 23:23:40 -0400 Subject: [PATCH 090/109] refactor and improve optional checks in constraints --- lang/syn/src/codegen/accounts/constraints.rs | 258 +++++++++++-------- lang/syn/src/codegen/accounts/exit.rs | 12 +- 2 files changed, 150 insertions(+), 120 deletions(-) diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 07bdae6468..231d88c3cc 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -1,5 +1,6 @@ use proc_macro2_diagnostics::SpanDiagnosticExt; use quote::quote; +use std::collections::HashSet; use syn::Expr; use crate::*; @@ -236,7 +237,7 @@ pub fn generate_constraint_close( let name_str = field.to_string(); let target = &c.sol_dest; let target_optional_check = - generate_optional_check(accs, quote! {#target}, &target.to_string()); + OptionalCheckScope::new_with_field(accs, field).generate_check(target); quote! { { #target_optional_check @@ -262,7 +263,7 @@ pub fn generate_constraint_has_one( c: &ConstraintHasOne, accs: &AccountsStruct, ) -> proc_macro2::TokenStream { - let target = c.join_target.clone(); + let target = &c.join_target; let ident = &f.ident; let field = match &f.ty { Ty::Loader(_) => quote! {#ident.load()?}, @@ -276,7 +277,8 @@ pub fn generate_constraint_has_one( &Some(&(quote! { my_key }, quote! { target_key })), ); let target_optional_check = - generate_optional_check(accs, quote! {#target}, tts_to_string(&target)); + OptionalCheckScope::new_with_field(accs, &field).generate_check(target); + quote! { { #target_optional_check @@ -390,10 +392,10 @@ fn generate_constraint_realloc( let payer = &c.payer; let zero = &c.zero; - let payer_optional_check = - generate_optional_check(accs, quote! {#payer}, &tts_to_string(payer)); + let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, field); + let payer_optional_check = optional_check_scope.generate_check(payer); let system_program_optional_check = - generate_optional_check(accs, quote! {system_program}, "system_program"); + optional_check_scope.generate_check(quote! {system_program}); quote! { // Blocks duplicate account reallocs in a single instruction to prevent accidental account overwrites @@ -458,15 +460,7 @@ fn generate_constraint_init_group( }; let space = &c.space; - // Payer for rent exemption. - let payer = { - let p = &c.payer; - let payer_optional_check = generate_optional_check(accs, quote! {#p}, tts_to_string(p)); - quote! { - #payer_optional_check - let payer = #p.to_account_info(); - } - }; + let payer = &c.payer; // Convert from account info to account context wrapper type. let from_account_info = f.from_account_info(Some(&c.kind), true); @@ -537,23 +531,22 @@ fn generate_constraint_init_group( } }; - let system_program_optional_check = - generate_optional_check(accs, quote! {system_program}, "system_program"); - let token_program_optional_check = - generate_optional_check(accs, quote! {token_program}, "token_program"); - let associated_token_program_optional_check = generate_optional_check( - accs, - quote! {associated_token_program}, - "associated_token_program", - ); - let rent_optional_check = generate_optional_check(accs, quote! {rent}, "rent"); + // Optional check idents + let system_program = "e! {system_program}; + let token_program = "e! {token_program}; + let associated_token_program = "e! {associated_token_program}; + let rent = "e! {rent}; + let mut check_scope = OptionalCheckScope::new_with_field(accs, field); match &c.kind { InitKind::Token { owner, mint } => { - let owner_optional_check = - generate_optional_check(accs, quote! {#owner}, &tts_to_string(owner)); - let mint_optional_check = - generate_optional_check(accs, quote! {#mint}, &tts_to_string(mint)); + let owner_optional_check = check_scope.generate_check(owner); + let mint_optional_check = check_scope.generate_check(mint); + + let system_program_optional_check = check_scope.generate_check(system_program); + let token_program_optional_check = check_scope.generate_check(token_program); + let rent_optional_check = check_scope.generate_check(rent); + let optional_checks = quote! { #system_program_optional_check #token_program_optional_check @@ -562,10 +555,13 @@ fn generate_constraint_init_group( #mint_optional_check }; + let payer_optional_check = check_scope.generate_check(payer); + let create_account = generate_create_account( field, quote! {anchor_spl::token::TokenAccount::LEN}, quote! {&token_program.key()}, + quote! {#payer}, seeds_with_bump, ); @@ -578,8 +574,7 @@ fn generate_constraint_init_group( #optional_checks if !#if_needed || AsRef::::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID { - // Define payer variable. - #payer + #payer_optional_check // Create the account with the system program. #create_account @@ -610,19 +605,26 @@ fn generate_constraint_init_group( } } InitKind::AssociatedToken { owner, mint } => { - let owner_optional_check = - generate_optional_check(accs, quote! {#owner}, &tts_to_string(owner)); - let mint_optional_check = - generate_optional_check(accs, quote! {#mint}, &tts_to_string(mint)); + let owner_optional_check = check_scope.generate_check(owner); + let mint_optional_check = check_scope.generate_check(mint); + + let system_program_optional_check = check_scope.generate_check(system_program); + let token_program_optional_check = check_scope.generate_check(token_program); + let associated_token_program_optional_check = + check_scope.generate_check(associated_token_program); + let rent_optional_check = check_scope.generate_check(rent); + let optional_checks = quote! { #system_program_optional_check - #associated_token_program_optional_check #token_program_optional_check + #associated_token_program_optional_check #rent_optional_check #owner_optional_check #mint_optional_check }; + let payer_optional_check = check_scope.generate_check(payer); + quote! { // Define the bump and pda variable. #find_pda @@ -632,11 +634,11 @@ fn generate_constraint_init_group( #optional_checks if !#if_needed || AsRef::::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID { - #payer + #payer_optional_check let cpi_program = associated_token_program.to_account_info(); let cpi_accounts = anchor_spl::associated_token::Create { - payer: payer.to_account_info(), + payer: #payer.to_account_info(), associated_token: #field.to_account_info(), authority: #owner.to_account_info(), mint: #mint.to_account_info(), @@ -669,32 +671,39 @@ fn generate_constraint_init_group( decimals, freeze_authority, } => { - let owner_optional_check = - generate_optional_check(accs, quote! {#owner}, &tts_to_string(owner)); + let owner_optional_check = check_scope.generate_check(owner); + let freeze_authority_optional_check = match freeze_authority { + Some(fa) => check_scope.generate_check(fa), + None => quote! {}, + }; + + let system_program_optional_check = check_scope.generate_check(system_program); + let token_program_optional_check = check_scope.generate_check(token_program); + let rent_optional_check = check_scope.generate_check(rent); + let optional_checks = quote! { #system_program_optional_check #token_program_optional_check #rent_optional_check #owner_optional_check + #freeze_authority_optional_check }; + let payer_optional_check = check_scope.generate_check(payer); + let create_account = generate_create_account( field, quote! {anchor_spl::token::Mint::LEN}, quote! {&token_program.key()}, + quote! {#payer}, seeds_with_bump, ); + let freeze_authority = match freeze_authority { - Some(fa) => { - let freeze_authority_optional_check = - generate_optional_check(accs, quote! {#fa}, &tts_to_string(fa)); - quote! { - #freeze_authority_optional_check - Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) - } - } + Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, }; + quote! { // Define the bump and pda variable. #find_pda @@ -705,7 +714,7 @@ fn generate_constraint_init_group( if !#if_needed || AsRef::::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID { // Define payer variable. - #payer + #payer_optional_check // Create the account with the system program. #create_account @@ -742,29 +751,45 @@ fn generate_constraint_init_group( // Define the space variable. let space = quote! {let space = #space;}; + let system_program_optional_check = check_scope.generate_check(system_program); + // Define the owner of the account being created. If not specified, // default to the currently executing program. - let owner = match owner { - None => quote! { - program_id - }, - Some(o) => { - let owner_optional_check = - generate_optional_check(accs, quote! {#o}, &tts_to_string(o)); + let (owner, owner_optional_check) = match owner { + None => ( quote! { - #owner_optional_check - &#o - } + program_id + }, + quote! {}, + ), + + Some(o) => { + // We clone the `check_scope` here to avoid collisions with the + // `payer_optional_check`, which is in a separate scope + let owner_optional_check = check_scope.clone().generate_check(o); + ( + quote! { + &#o + }, + owner_optional_check, + ) } }; + let payer_optional_check = check_scope.generate_check(payer); + let optional_checks = quote! { #system_program_optional_check }; // CPI to the system program to create the account. - let create_account = - generate_create_account(field, quote! {space}, owner.clone(), seeds_with_bump); + let create_account = generate_create_account( + field, + quote! {space}, + owner.clone(), + quote! {#payer}, + seeds_with_bump, + ); // Put it all together. quote! { @@ -784,8 +809,7 @@ fn generate_constraint_init_group( // Create the account. Always do this in the event // if needed is not specified or the system program is the owner. let pa: #ty_decl = if !#if_needed || actual_owner == &anchor_lang::solana_program::system_program::ID { - // Define the payer variable. - #payer + #payer_optional_check // CPI to the system program to create. #create_account @@ -799,6 +823,7 @@ fn generate_constraint_init_group( // Assert the account was created correctly. if #if_needed { + #owner_optional_check if space != actual_field.data_len() { return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSpace).with_account_name(#name_str).with_values((space, actual_field.data_len()))); } @@ -892,20 +917,18 @@ fn generate_constraint_associated_token( let name_str = name.to_string(); let wallet_address = &c.wallet; let spl_token_mint_address = &c.mint; - let wallet_address_optional_check = generate_optional_check( - accs, - quote! {#wallet_address}, - tts_to_string(wallet_address), - ); - let spl_token_mint_address_optional_check = generate_optional_check( - accs, - quote! {#spl_token_mint_address}, - tts_to_string(spl_token_mint_address), - ); + let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name); + let wallet_address_optional_check = optional_check_scope.generate_check(wallet_address); + let spl_token_mint_address_optional_check = + optional_check_scope.generate_check(spl_token_mint_address); + let optional_checks = quote! { + #wallet_address_optional_check + #spl_token_mint_address_optional_check + }; + quote! { { - #wallet_address_optional_check - #spl_token_mint_address_optional_check + #optional_checks let my_owner = #name.owner; let wallet_address = #wallet_address.key(); @@ -927,10 +950,10 @@ fn generate_constraint_token_account( accs: &AccountsStruct, ) -> proc_macro2::TokenStream { let name = &f.ident; + let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name); let authority_check = match &c.authority { Some(authority) => { - let authority_optional_check = - generate_optional_check(accs, quote! {#authority}, tts_to_string(authority)); + let authority_optional_check = optional_check_scope.generate_check(authority); quote! { #authority_optional_check if #name.owner != #authority.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); } @@ -940,8 +963,7 @@ fn generate_constraint_token_account( }; let mint_check = match &c.mint { Some(mint) => { - let mint_optional_check = - generate_optional_check(accs, quote! {#mint}, tts_to_string(mint)); + let mint_optional_check = optional_check_scope.generate_check(mint); quote! { #mint_optional_check if #name.mint != #mint.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); } @@ -972,16 +994,13 @@ fn generate_constraint_mint( }, None => quote! {}, }; + let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name); let mint_authority_check = match &c.mint_authority { Some(mint_authority) => { - let mint_authority_optional_check = generate_optional_check( - accs, - quote! {#mint_authority}, - tts_to_string(mint_authority), - ); + let mint_authority_optional_check = optional_check_scope.generate_check(mint_authority); quote! { #mint_authority_optional_check - if #name.mint_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#mint_authority)) { + if #name.mint_authority != anchor_lang::solana_program::program_option::COption::Some(#mint_authority.key()) { return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into()); } } @@ -990,14 +1009,11 @@ fn generate_constraint_mint( }; let freeze_authority_check = match &c.freeze_authority { Some(freeze_authority) => { - let freeze_authority_optional_check = generate_optional_check( - accs, - quote! {#freeze_authority}, - tts_to_string(freeze_authority), - ); + let freeze_authority_optional_check = + optional_check_scope.generate_check(freeze_authority); quote! { #freeze_authority_optional_check - if #name.freeze_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#freeze_authority)) { + if #name.freeze_authority != anchor_lang::solana_program::program_option::COption::Some(#freeze_authority.key()) { return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into()); } } @@ -1013,22 +1029,42 @@ fn generate_constraint_mint( } } -pub fn generate_optional_check( - accs: &AccountsStruct, - field: TokenStream, - field_name: impl ToString, -) -> TokenStream { - let field_name = field_name.to_string(); - if accs.is_field_optional(&field) { - quote! { - let #field = if let Some(account) = &#field { - account +#[derive(Clone, Debug)] +pub struct OptionalCheckScope<'a> { + seen: HashSet, + accounts: &'a AccountsStruct, +} + +impl<'a> OptionalCheckScope<'a> { + pub fn new(accounts: &'a AccountsStruct) -> Self { + Self { + seen: HashSet::new(), + accounts, + } + } + pub fn new_with_field(accounts: &'a AccountsStruct, field: impl ToString) -> Self { + let mut check_scope = Self::new(accounts); + check_scope.seen.insert(field.to_string()); + check_scope + } + pub fn generate_check(&mut self, field: impl ToTokens) -> TokenStream { + let field_name = tts_to_string(&field); + if self.seen.contains(&field_name) { + quote! {} + } else { + self.seen.insert(field_name.clone()); + if self.accounts.is_field_optional(&field) { + quote! { + let #field = if let Some(ref account) = #field { + account + } else { + return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAccountIsNone).with_account_name(#field_name)); + }; + } } else { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAccountIsNone).with_account_name(#field_name)); - }; + quote! {} + } } - } else { - quote! {} } } @@ -1043,6 +1079,7 @@ fn generate_create_account( field: &Ident, space: proc_macro2::TokenStream, owner: proc_macro2::TokenStream, + payer: proc_macro2::TokenStream, seeds_with_nonce: proc_macro2::TokenStream, ) -> proc_macro2::TokenStream { // Field, payer, and system program are already validated to not be an Option at this point @@ -1056,7 +1093,7 @@ fn generate_create_account( // Create the token account with right amount of lamports and space, and the correct owner. let lamports = __anchor_rent.minimum_balance(#space); let cpi_accounts = anchor_lang::system_program::CreateAccount { - from: payer.to_account_info(), + from: #payer.to_account_info(), to: #field.to_account_info() }; let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); @@ -1069,7 +1106,7 @@ fn generate_create_account( .saturating_sub(__current_lamports); if required_lamports > 0 { let cpi_accounts = anchor_lang::system_program::Transfer { - from: payer.to_account_info(), + from: #payer.to_account_info(), to: #field.to_account_info(), }; let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); @@ -1119,11 +1156,8 @@ pub fn generate_constraint_state( Ty::CpiState(ty) => &ty.account_type_path, _ => panic!("Invalid state constraint"), }; - let program_target_optional_check = generate_optional_check( - accs, - quote! {#program_target}, - tts_to_string(&program_target), - ); + let program_target_optional_check = + OptionalCheckScope::new_with_field(accs, ident).generate_check(quote! {#program_target}); quote! { { #program_target_optional_check diff --git a/lang/syn/src/codegen/accounts/exit.rs b/lang/syn/src/codegen/accounts/exit.rs index fa5716266e..de732bd71b 100644 --- a/lang/syn/src/codegen/accounts/exit.rs +++ b/lang/syn/src/codegen/accounts/exit.rs @@ -1,7 +1,7 @@ -use crate::accounts_codegen::constraints::generate_optional_check; +use crate::accounts_codegen::constraints::OptionalCheckScope; use crate::codegen::accounts::{generics, ParsedGenerics}; use crate::{AccountField, AccountsStruct}; -use quote::{quote, ToTokens}; +use quote::quote; // Generates the `Exit` trait implementation. pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { @@ -30,12 +30,8 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { let name_str = ident.to_string(); if f.constraints.is_close() { let close_target = &f.constraints.close.as_ref().unwrap().sol_dest; - let close_target_name = close_target.to_string(); - let close_target_optional_check = generate_optional_check( - accs, - close_target.to_token_stream(), - &close_target_name, - ); + let close_target_optional_check = + OptionalCheckScope::new(accs).generate_check(close_target); quote! { { From bedfabcc5d4e20f03c6b07ccaff8b0e86bc1d56d Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 20 Oct 2022 23:25:52 -0400 Subject: [PATCH 091/109] add misc-optional program and tests --- tests/misc/Anchor.toml | 1 + tests/misc/programs/misc-optional/Cargo.toml | 22 + tests/misc/programs/misc-optional/Xargo.toml | 2 + .../programs/misc-optional/src/account.rs | 75 + .../programs/misc-optional/src/context.rs | 578 +++++ .../misc/programs/misc-optional/src/event.rs | 55 + tests/misc/programs/misc-optional/src/lib.rs | 388 +++ tests/misc/tests/misc-optional/Test.toml | 2 + .../misc/tests/misc-optional/misc-optional.ts | 2190 +++++++++++++++++ 9 files changed, 3313 insertions(+) create mode 100644 tests/misc/programs/misc-optional/Cargo.toml create mode 100644 tests/misc/programs/misc-optional/Xargo.toml create mode 100644 tests/misc/programs/misc-optional/src/account.rs create mode 100644 tests/misc/programs/misc-optional/src/context.rs create mode 100644 tests/misc/programs/misc-optional/src/event.rs create mode 100644 tests/misc/programs/misc-optional/src/lib.rs create mode 100644 tests/misc/tests/misc-optional/Test.toml create mode 100644 tests/misc/tests/misc-optional/misc-optional.ts diff --git a/tests/misc/Anchor.toml b/tests/misc/Anchor.toml index 7f9e40c60a..d7dd74bc8e 100644 --- a/tests/misc/Anchor.toml +++ b/tests/misc/Anchor.toml @@ -5,6 +5,7 @@ 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 new file mode 100644 index 0000000000..3e217525e2 --- /dev/null +++ b/tests/misc/programs/misc-optional/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "misc-optional" +version = "0.1.0" +description = "Created with Anchor" +rust-version = "1.56" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "misc_optional" + +[features] +no-entrypoint = [] +no-idl = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } +anchor-spl = { path = "../../../../spl" } +misc2 = { path = "../misc2", features = ["cpi"] } +spl-associated-token-account = "~1.0.3" diff --git a/tests/misc/programs/misc-optional/Xargo.toml b/tests/misc/programs/misc-optional/Xargo.toml new file mode 100644 index 0000000000..1744f098ae --- /dev/null +++ b/tests/misc/programs/misc-optional/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] \ No newline at end of file diff --git a/tests/misc/programs/misc-optional/src/account.rs b/tests/misc/programs/misc-optional/src/account.rs new file mode 100644 index 0000000000..893dac256b --- /dev/null +++ b/tests/misc/programs/misc-optional/src/account.rs @@ -0,0 +1,75 @@ +use anchor_lang::prelude::*; + +macro_rules! size { + ($name: ident, $size:expr) => { + impl $name { + pub const LEN: usize = $size; + } + }; +} + +pub const MAX_SIZE: usize = 10; +pub const MAX_SIZE_U8: u8 = 11; + +#[account] +pub struct Data { + pub udata: u128, // 16 + pub idata: i128, // 16 +} +size!(Data, 32); + +#[account] +pub struct DataU16 { + pub data: u16, // 2 +} +size!(DataU16, 32); + +#[account] +pub struct DataI8 { + pub data: i8, // 1 +} +size!(DataI8, 1); + +#[account] +pub struct DataI16 { + pub data: i16, // 2 +} +size!(DataI16, 2); + +#[account(zero_copy)] +pub struct DataZeroCopy { + pub data: u16, // 2 + pub _padding: u8, // 1 + pub bump: u8, // 1 +} +size!(DataZeroCopy, 4); + +#[account] +pub struct DataWithFilter { + pub authority: Pubkey, // 32 + pub filterable: Pubkey, // 32 +} +size!(DataWithFilter, 64); + +#[account] +pub struct DataMultidimensionalArray { + pub data: [[u8; 10]; 10], // 100 +} +size!(DataMultidimensionalArray, 100); + +#[account] +pub struct DataConstArraySize { + pub data: [u8; MAX_SIZE], // 10 +} +size!(DataConstArraySize, MAX_SIZE); + +#[account] +pub struct DataConstCastArraySize { + pub data_one: [u8; MAX_SIZE as usize], + pub data_two: [u8; MAX_SIZE_U8 as usize], +} + +#[account] +pub struct DataMultidimensionalArrayConstSizes { + pub data: [[u8; MAX_SIZE_U8 as usize]; MAX_SIZE], +} diff --git a/tests/misc/programs/misc-optional/src/context.rs b/tests/misc/programs/misc-optional/src/context.rs new file mode 100644 index 0000000000..d23892ea33 --- /dev/null +++ b/tests/misc/programs/misc-optional/src/context.rs @@ -0,0 +1,578 @@ +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> { + #[account( + init, + seeds = [b"my-mint-seed".as_ref()], + bump, + payer = authority, + mint::decimals = 6, + mint::authority = authority, + )] + pub mint: Option>, + #[account( + init, + seeds = [b"my-token-seed".as_ref()], + bump, + payer = authority, + token::mint = mint, + token::authority = authority, + )] + pub my_pda: Option>, + #[account(mut)] + /// CHECK: + pub authority: Option>, + pub system_program: Option>, + pub rent: Option>, + pub token_program: Option>, +} + +#[derive(Accounts)] +pub struct TestInitAssociatedToken<'info> { + #[account( + init, + associated_token::mint = mint, + payer = payer, + associated_token::authority = payer, + )] + pub token: Option>, + pub mint: Option>, + #[account(mut)] + pub payer: Option>, + pub rent: Option>, + pub system_program: Option>, + pub token_program: Option>, + pub associated_token_program: Option>, +} + +#[derive(Accounts)] +pub struct TestValidateAssociatedToken<'info> { + #[account( + associated_token::mint = mint, + associated_token::authority = wallet, + )] + pub token: Option>, + pub mint: Option>, + /// CHECK: + pub wallet: Option>, +} + +#[derive(Accounts)] +#[instruction(nonce: u8)] +pub struct TestInstructionConstraint<'info> { + #[account( + seeds = [b"my-seed", my_account.as_ref().unwrap().key.as_ref()], + bump = nonce, + )] + /// CHECK: + pub my_pda: Option>, + /// CHECK: + pub my_account: Option>, +} + +#[derive(Accounts)] +#[instruction(domain: String, seed: Vec, bump: u8)] +pub struct TestPdaInit<'info> { + #[account( + init, + seeds = [b"my-seed", domain.as_bytes(), foo.as_ref().unwrap().key.as_ref(), &seed], + bump, + payer = my_payer, + space = DataU16::LEN + 8 + )] + pub my_pda: Option>, + #[account(mut)] + pub my_payer: Option>, + /// CHECK: + pub foo: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct TestPdaInitZeroCopy<'info> { + #[account( + init, + seeds = [b"my-seed".as_ref()], + bump, + payer = my_payer, + space = DataZeroCopy::LEN + 8 + )] + pub my_pda: Option>, + #[account(mut)] + pub my_payer: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct TestPdaMutZeroCopy<'info> { + #[account( + mut, + seeds = [b"my-seed".as_ref()], + bump = my_pda.load()?.bump, + )] + pub my_pda: Option>, + /// CHECK: + pub my_payer: Option>, +} + +#[derive(Accounts)] +pub struct Ctor {} + +#[derive(Accounts)] +pub struct RemainingAccounts {} + +#[derive(Accounts)] +pub struct Initialize<'info> { + #[account(zero)] + pub data: Option>, +} + +#[derive(Accounts)] +pub struct InitializeSkipRentExempt<'info> { + #[account(zero, rent_exempt = skip)] + pub data: Option>, +} + +#[derive(Accounts)] +pub struct InitializeNoRentExempt<'info> { + /// CHECK: + pub data: Option>, +} + +#[derive(Accounts)] +pub struct TestOwner<'info> { + #[account(owner = *misc.key)] + /// CHECK: + pub data: Option>, + /// CHECK: + pub misc: AccountInfo<'info>, +} + +#[derive(Accounts)] +pub struct TestExecutable<'info> { + #[account(executable)] + /// CHECK: + 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)] + pub data: Option>, + /// CHECK: + sol_dest: Option>, +} + +#[derive(Accounts)] +pub struct TestU16<'info> { + #[account(zero)] + pub my_account: Option>, +} + +#[derive(Accounts)] +pub struct TestI16<'info> { + #[account(zero)] + pub data: Option>, +} + +#[derive(Accounts)] +pub struct TestSimulate {} + +#[derive(Accounts)] +pub struct TestI8<'info> { + #[account(zero)] + pub data: Option>, +} + +#[derive(Accounts)] +pub struct TestCompositePayer<'info> { + pub composite: Option>, + #[account(init, payer = payer.as_ref().unwrap(), space = Data::LEN + 8)] + pub data: Option>, + pub payer: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct TestInit<'info> { + #[account(init, payer = payer, space = DataI8::LEN + 8)] + pub data: Option>, + #[account(mut)] + pub payer: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct TestInitZeroCopy<'info> { + #[account(init, payer = payer, space = DataZeroCopy::LEN + 8)] + pub data: Option>, + #[account(mut)] + pub payer: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct TestInitMint<'info> { + #[account(init, mint::decimals = 6, mint::authority = payer, mint::freeze_authority = payer, payer = payer, )] + pub mint: Option>, + #[account(mut)] + pub payer: Option>, + pub rent: Option>, + pub system_program: Option>, + pub token_program: Option>, +} + +#[derive(Accounts)] +pub struct TestInitToken<'info> { + #[account(init, token::mint = mint, token::authority = payer, payer = payer, )] + pub token: Option>, + pub mint: Option>, + #[account(mut)] + pub payer: Option>, + pub rent: Option>, + pub system_program: Option>, + pub token_program: Option>, +} + +#[derive(Accounts)] +pub struct TestFetchAll<'info> { + #[account(init, payer = authority, space = DataWithFilter::LEN + 8)] + pub data: Option>, + #[account(mut)] + pub authority: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct TestInitWithEmptySeeds<'info> { + #[account(init, seeds = [], bump, payer = authority, space = Data::LEN + 8)] + pub pda: Option>, + #[account(mut)] + pub authority: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct TestEmptySeedsConstraint<'info> { + #[account(seeds = [], bump)] + /// CHECK: + pub pda: Option>, +} + +#[derive(Accounts)] +pub struct InitWithSpace<'info> { + #[account(init, payer = payer, space = DataU16::LEN + 8)] + pub data: Option>, + #[account(mut)] + pub payer: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct TestInitIfNeeded<'info> { + // intentionally using more space (+500) to check whether space is checked when using init_if_needed + #[account(init_if_needed, payer = payer, space = DataU16::LEN + 8 + 500)] + pub data: Option>, + #[account(mut)] + pub payer: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct TestInitIfNeededChecksOwner<'info> { + #[account(init_if_needed, payer = payer, space = 100, owner = *owner.key, seeds = [b"hello"], bump)] + /// CHECK: + pub data: Option>, + #[account(mut)] + pub payer: Option>, + pub system_program: Option>, + /// CHECK: + pub owner: AccountInfo<'info>, +} + +#[derive(Accounts)] +#[instruction(seed_data: String)] +pub struct TestInitIfNeededChecksSeeds<'info> { + #[account(init_if_needed, payer = payer, space = 100, seeds = [seed_data.as_bytes()], bump)] + /// CHECK: + pub data: Option>, + #[account(mut)] + pub payer: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +#[instruction(decimals: u8)] +pub struct TestInitMintIfNeeded<'info> { + #[account(init_if_needed, mint::decimals = decimals, mint::authority = mint_authority, mint::freeze_authority = freeze_authority, payer = payer)] + pub mint: Option>, + #[account(mut)] + pub payer: Option>, + pub rent: Option>, + pub system_program: Option>, + pub token_program: Option>, + /// CHECK: + pub mint_authority: Option>, + /// CHECK: + pub freeze_authority: Option>, +} + +#[derive(Accounts)] +pub struct TestInitTokenIfNeeded<'info> { + #[account(init_if_needed, token::mint = mint, token::authority = authority, payer = payer, )] + pub token: Option>, + pub mint: Option>, + #[account(mut)] + pub payer: Option>, + pub rent: Option>, + pub system_program: Option>, + pub token_program: Option>, + /// CHECK: + pub authority: Option>, +} + +#[derive(Accounts)] +pub struct TestInitAssociatedTokenIfNeeded<'info> { + #[account( + init_if_needed, + payer = payer, + associated_token::mint = mint, + associated_token::authority = authority + )] + pub token: Option>, + pub mint: Option>, + #[account(mut)] + pub payer: Option>, + pub rent: Option>, + pub system_program: Option>, + pub token_program: Option>, + pub associated_token_program: Option>, + /// CHECK: + pub authority: Option>, +} + +#[derive(Accounts)] +pub struct TestMultidimensionalArray<'info> { + #[account(zero)] + pub data: Option>, +} + +#[derive(Accounts)] +pub struct TestConstArraySize<'info> { + #[account(zero)] + pub data: Option>, +} + +#[derive(Accounts)] +pub struct TestConstIxDataSize<'info> { + #[account(zero)] + pub data: Option>, +} + +#[derive(Accounts)] +pub struct TestMultidimensionalArrayConstSizes<'info> { + #[account(zero)] + pub data: Option>, +} + +#[derive(Accounts)] +pub struct NoRentExempt<'info> { + /// CHECK: + pub data: Option>, +} + +#[derive(Accounts)] +pub struct EnforceRentExempt<'info> { + #[account(rent_exempt = enforce)] + /// CHECK: + pub data: Option>, +} + +#[derive(Accounts)] +pub struct InitDecreaseLamports<'info> { + #[account(init, payer = user, space = 1000)] + /// CHECK: + pub data: Option>, + #[account(mut)] + pub user: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct InitIfNeededChecksRentExemption<'info> { + #[account(init_if_needed, payer = user, space = 1000)] + /// CHECK: + pub data: Option>, + #[account(mut)] + pub user: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +#[instruction(bump: u8, second_bump: u8)] +pub struct TestProgramIdConstraint<'info> { + // not a real associated token account + // just deriving like this for testing purposes + #[account(seeds = [b"seed"], bump = bump, seeds::program = anchor_spl::associated_token::ID)] + /// CHECK: + first: Option>, + + #[account(seeds = [b"seed"], bump = second_bump, seeds::program = crate::ID)] + /// CHECK: + second: Option>, +} + +#[derive(Accounts)] +pub struct TestProgramIdConstraintUsingFindPda<'info> { + // not a real associated token account + // just deriving like this for testing purposes + #[account(seeds = [b"seed"], bump, seeds::program = anchor_spl::associated_token::ID)] + /// CHECK: + first: Option>, + + #[account(seeds = [b"seed"], bump, seeds::program = crate::ID)] + /// CHECK: + second: Option>, +} + +#[derive(Accounts)] +pub struct TestUnsafeFieldSafetyErrors<'info> { + #[doc = "test"] + /// CHECK: + pub data: Option>, + #[account(mut)] + /// CHECK: + pub data_two: Option>, + #[account( + seeds = [b"my-seed", signer.as_ref().unwrap().key.as_ref()], + bump + )] + /// CHECK: + pub data_three: Option>, + /// CHECK: + pub data_four: Option>, + pub signer: Option>, + pub system_program: Option>, +} + +#[derive(Accounts)] +pub struct TestConstraintToken<'info> { + #[account( + token::mint = mint, + token::authority = payer + )] + pub token: Option>, + pub mint: Option>, + pub payer: Option>, +} + +#[derive(Accounts)] +pub struct TestAuthorityConstraint<'info> { + #[account( + token::mint = mint, + token::authority = fake_authority + )] + pub token: Option>, + pub mint: Option>, + pub fake_authority: Option>, +} +#[derive(Accounts)] +pub struct TestOnlyAuthorityConstraint<'info> { + #[account( + token::authority = payer + )] + pub token: Option>, + pub mint: Option>, + pub payer: Option>, +} +#[derive(Accounts)] +pub struct TestOnlyMintConstraint<'info> { + #[account( + token::mint = mint, + )] + pub token: Option>, + pub mint: Option>, +} + +#[derive(Accounts)] +#[instruction(decimals: u8)] +pub struct TestMintConstraint<'info> { + #[account( + mint::decimals = decimals, + mint::authority = mint_authority, + mint::freeze_authority = freeze_authority + )] + pub mint: Option>, + pub mint_authority: Option>, + pub freeze_authority: Option>, +} + +#[derive(Accounts)] +#[instruction(decimals: u8)] +pub struct TestMintOnlyDecimalsConstraint<'info> { + #[account( + mint::decimals = decimals, + )] + pub mint: Option>, +} + +#[derive(Accounts)] +pub struct TestMintAuthorityConstraint<'info> { + #[account( + mint::authority = mint_authority, + mint::freeze_authority = freeze_authority + )] + pub mint: Option>, + pub mint_authority: Option>, + pub freeze_authority: Option>, +} + +#[derive(Accounts)] +pub struct TestMintOneAuthorityConstraint<'info> { + #[account( + mint::authority = mint_authority, + )] + pub mint: Option>, + pub mint_authority: Option>, +} + +#[derive(Accounts)] +#[instruction(decimals: u8)] +pub struct TestMintMissMintAuthConstraint<'info> { + #[account( + mint::decimals = decimals, + mint::freeze_authority = freeze_authority, + )] + pub mint: Option>, + pub freeze_authority: Option>, +} + +#[derive(Accounts)] +pub struct TestAssociatedToken<'info> { + #[account( + associated_token::mint = mint, + associated_token::authority = authority, + )] + pub token: Option>, + pub mint: Option>, + pub authority: Option>, +} diff --git a/tests/misc/programs/misc-optional/src/event.rs b/tests/misc/programs/misc-optional/src/event.rs new file mode 100644 index 0000000000..7a41c33786 --- /dev/null +++ b/tests/misc/programs/misc-optional/src/event.rs @@ -0,0 +1,55 @@ +use anchor_lang::prelude::*; + +pub const MAX_EVENT_SIZE: usize = 10; +pub const MAX_EVENT_SIZE_U8: u8 = 11; + +#[event] +pub struct E1 { + pub data: u32, +} + +#[event] +pub struct E2 { + pub data: u32, +} + +#[event] +pub struct E3 { + pub data: u32, +} + +#[event] +pub struct E4 { + pub data: Pubkey, +} + +#[event] +pub struct E5 { + pub data: [u8; MAX_EVENT_SIZE], +} + +#[event] +pub struct E6 { + pub data: [u8; MAX_EVENT_SIZE_U8 as usize], +} + +#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)] +pub struct TestStruct { + pub data1: u8, + pub data2: u16, + pub data3: u32, + pub data4: u64, +} + +#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)] +pub enum TestEnum { + First, + Second { x: u64, y: u64 }, + TupleTest(u8, u8, u16, u16), + TupleStructTest(TestStruct), +} + +#[event] +pub struct E7 { + pub data: TestEnum, +} diff --git a/tests/misc/programs/misc-optional/src/lib.rs b/tests/misc/programs/misc-optional/src/lib.rs new file mode 100644 index 0000000000..d915a97eee --- /dev/null +++ b/tests/misc/programs/misc-optional/src/lib.rs @@ -0,0 +1,388 @@ +//! Misc example is a catchall program for testing unrelated features. +//! It's not too instructive/coherent by itself, so please see other examples. + +use account::MAX_SIZE; +use anchor_lang::prelude::*; +use context::*; +use event::*; +use misc2::Auth; + +mod account; +mod context; +mod event; + +declare_id!("FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG"); + +#[constant] +pub const BASE: u128 = 1_000_000; +#[constant] +pub const DECIMALS: u8 = 6; +pub const NO_IDL: u16 = 55; + +#[program] +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; + Ok(()) + } + + pub fn initialize_no_rent_exempt(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn initialize_skip_rent_exempt(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_owner(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_executable(_ctx: Context) -> Result<()> { + 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(()) + } + + pub fn test_simulate(_ctx: Context, data: u32) -> Result<()> { + emit!(E1 { data }); + emit!(E2 { data: 1234 }); + emit!(E3 { data: 9 }); + emit!(E5 { + data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + }); + emit!(E6 { + data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + }); + Ok(()) + } + + pub fn test_input_enum(ctx: Context, data: TestEnum) -> Result<()> { + emit!(E7 { data: data }); + Ok(()) + } + + pub fn test_i8(ctx: Context, data: i8) -> Result<()> { + ctx.accounts.data.as_mut().unwrap().data = data; + Ok(()) + } + + pub fn test_i16(ctx: Context, data: i16) -> Result<()> { + ctx.accounts.data.as_mut().unwrap().data = data; + Ok(()) + } + + pub fn test_const_array_size(ctx: Context, data: u8) -> Result<()> { + ctx.accounts.data.as_mut().unwrap().data[0] = data; + Ok(()) + } + + pub fn test_const_ix_data_size( + ctx: Context, + data: [u8; MAX_SIZE], + ) -> Result<()> { + ctx.accounts.data.as_mut().unwrap().data = data; + Ok(()) + } + + pub fn test_close(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_instruction_constraint( + _ctx: Context, + _nonce: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_pda_init( + ctx: Context, + _domain: String, + _seed: Vec, + _bump: u8, + ) -> Result<()> { + ctx.accounts.my_pda.as_mut().unwrap().data = 6; + Ok(()) + } + + pub fn test_pda_init_zero_copy(ctx: Context) -> Result<()> { + let mut acc = ctx.accounts.my_pda.as_ref().unwrap().load_init()?; + acc.data = 9; + acc.bump = *ctx.bumps.get("my_pda").unwrap(); + Ok(()) + } + + pub fn test_pda_mut_zero_copy(ctx: Context) -> Result<()> { + let mut acc = ctx.accounts.my_pda.as_mut().unwrap().load_mut()?; + acc.data = 1234; + Ok(()) + } + + pub fn test_token_seeds_init(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn default<'info>( + _program_id: &Pubkey, + _accounts: &[AccountInfo<'info>], + _data: &[u8], + ) -> Result<()> { + Err(ProgramError::Custom(1234).into()) + } + + pub fn test_init(ctx: Context) -> Result<()> { + ctx.accounts.data.as_mut().unwrap().data = 3; + Ok(()) + } + + pub fn test_init_zero_copy(ctx: Context) -> Result<()> { + let mut data = ctx.accounts.data.as_ref().unwrap().load_init()?; + data.data = 10; + data.bump = 2; + Ok(()) + } + + pub fn test_init_mint(ctx: Context) -> Result<()> { + assert!(ctx.accounts.mint.as_ref().unwrap().decimals == 6); + Ok(()) + } + + pub fn test_init_token(ctx: Context) -> Result<()> { + assert!( + ctx.accounts.token.as_ref().unwrap().mint == ctx.accounts.mint.as_ref().unwrap().key() + ); + Ok(()) + } + + pub fn test_composite_payer(ctx: Context) -> Result<()> { + ctx.accounts + .composite + .as_mut() + .unwrap() + .data + .as_mut() + .unwrap() + .data = 1; + ctx.accounts.data.as_mut().unwrap().udata = 2; + ctx.accounts.data.as_mut().unwrap().idata = 3; + Ok(()) + } + + pub fn test_init_associated_token(ctx: Context) -> Result<()> { + assert!( + ctx.accounts.token.as_ref().unwrap().mint == ctx.accounts.mint.as_ref().unwrap().key() + ); + Ok(()) + } + + pub fn test_validate_associated_token( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_fetch_all(ctx: Context, filterable: Pubkey) -> Result<()> { + ctx.accounts.data.as_mut().unwrap().authority = + ctx.accounts.authority.as_ref().unwrap().key(); + ctx.accounts.data.as_mut().unwrap().filterable = filterable; + Ok(()) + } + + pub fn test_init_with_empty_seeds(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_empty_seeds_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_init_if_needed(ctx: Context, data: u16) -> Result<()> { + ctx.accounts.data.as_mut().unwrap().data = data; + Ok(()) + } + + pub fn test_init_if_needed_checks_owner( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_init_if_needed_checks_seeds( + _ctx: Context, + _seed_data: String, + ) -> Result<()> { + Ok(()) + } + + pub fn test_init_mint_if_needed( + _ctx: Context, + _decimals: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_init_token_if_needed(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_init_associated_token_if_needed( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn init_with_space(_ctx: Context, data: u16) -> Result<()> { + Ok(()) + } + + pub fn test_multidimensional_array( + ctx: Context, + data: [[u8; 10]; 10], + ) -> Result<()> { + ctx.accounts.data.as_mut().unwrap().data = data; + Ok(()) + } + + pub fn test_multidimensional_array_const_sizes( + ctx: Context, + data: [[u8; 11]; 10], + ) -> Result<()> { + ctx.accounts.data.as_mut().unwrap().data = data; + Ok(()) + } + + pub fn test_no_rent_exempt(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_enforce_rent_exempt(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn init_decrease_lamports(ctx: Context) -> Result<()> { + **ctx + .accounts + .data + .as_mut() + .unwrap() + .try_borrow_mut_lamports()? -= 1; + **ctx + .accounts + .user + .as_mut() + .unwrap() + .try_borrow_mut_lamports()? += 1; + Ok(()) + } + + pub fn init_if_needed_checks_rent_exemption( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_program_id_constraint( + _ctx: Context, + _bump: u8, + _second_bump: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_program_id_constraint_find_pda( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_token_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_token_auth_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_only_auth_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_only_mint_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } + + pub fn test_mint_constraint(_ctx: Context, _decimals: u8) -> Result<()> { + Ok(()) + } + + pub fn test_mint_only_decimals_constraint( + _ctx: Context, + _decimals: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_mint_only_auth_constraint( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_mint_only_one_auth_constraint( + _ctx: Context, + ) -> Result<()> { + Ok(()) + } + + pub fn test_mint_miss_mint_auth_constraint( + _ctx: Context, + _decimals: u8, + ) -> Result<()> { + Ok(()) + } + + pub fn test_associated_constraint(_ctx: Context) -> Result<()> { + Ok(()) + } +} diff --git a/tests/misc/tests/misc-optional/Test.toml b/tests/misc/tests/misc-optional/Test.toml new file mode 100644 index 0000000000..60785cbf93 --- /dev/null +++ b/tests/misc/tests/misc-optional/Test.toml @@ -0,0 +1,2 @@ +[scripts] +test = "yarn run ts-mocha -t 1000000 ./tests/misc-optional/*.ts" diff --git a/tests/misc/tests/misc-optional/misc-optional.ts b/tests/misc/tests/misc-optional/misc-optional.ts new file mode 100644 index 0000000000..697a089c63 --- /dev/null +++ b/tests/misc/tests/misc-optional/misc-optional.ts @@ -0,0 +1,2190 @@ +import * as anchor from "@project-serum/anchor"; +import { + Program, + BN, + IdlAccounts, + AnchorError, + Wallet, + IdlTypes, + IdlEvents, +} from "@project-serum/anchor"; +import { + PublicKey, + Keypair, + SystemProgram, + SYSVAR_RENT_PUBKEY, + Message, + VersionedTransaction, +} from "@solana/web3.js"; +import { + TOKEN_PROGRAM_ID, + Token, + ASSOCIATED_TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { MiscOptional } from "../../target/types/misc_optional"; +import { Misc2 } from "../../target/types/misc2"; + +const utf8 = anchor.utils.bytes.utf8; +const { assert, expect } = require("chai"); +const nativeAssert = require("assert"); +const miscIdl = require("../../target/idl/misc.json"); + +describe("misc-optional", () => { + // Configure the client to use the local cluster. + const provider = anchor.AnchorProvider.env(); + const wallet = provider.wallet as Wallet; + anchor.setProvider(provider); + const program = anchor.workspace.MiscOptional as Program; + 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(); + + it("Can use u128 and i128", async () => { + const tx = await program.rpc.initialize( + new anchor.BN(1234), + new anchor.BN(22), + { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [await program.account.data.createInstruction(data)], + } + ); + const dataAccount = await program.account.data.fetch(data.publicKey); + assert.isTrue(dataAccount.udata.eq(new anchor.BN(1234))); + assert.isTrue(dataAccount.idata.eq(new anchor.BN(22))); + }); + + it("Can use u16", async () => { + const data = anchor.web3.Keypair.generate(); + const tx = await program.rpc.testU16(99, { + accounts: { + myAccount: data.publicKey, + }, + signers: [data], + instructions: [await program.account.dataU16.createInstruction(data)], + }); + const dataAccount = await program.account.dataU16.fetch(data.publicKey); + assert.strictEqual(dataAccount.data, 99); + }); + + it("Can use the owner constraint", async () => { + await program.rpc.testOwner({ + accounts: { + data: data.publicKey, + misc: program.programId, + }, + options: { skipPreflight: true }, + }); + + await nativeAssert.rejects( + async () => { + await program.rpc.testOwner({ + accounts: { + data: provider.wallet.publicKey, + misc: program.programId, + }, + }); + }, + (err) => { + return true; + } + ); + }); + + it("Can use the executable attribute", async () => { + await program.rpc.testExecutable({ + accounts: { + program: program.programId, + }, + }); + + await nativeAssert.rejects( + async () => { + await program.rpc.testExecutable({ + accounts: { + program: provider.wallet.publicKey, + }, + }); + }, + (err) => { + return true; + } + ); + }); + + it("Can CPI to state instructions", async () => { + const oldData = new anchor.BN(0); + 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 = [ + "Program FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG invoke [1]", + "Program log: Instruction: TestSimulate", + "Program data: NgyCA9omwbMsAAAA", + "Program data: fPhuIELK/k7SBAAA", + "Program data: jvbowsvlmkcJAAAA", + "Program data: zxM5neEnS1kBAgMEBQYHCAkK", + "Program data: g06Ei2GL1gIBAgMEBQYHCAkKCw==", + ]; + + assert.deepStrictEqual(expectedRaw, resp.raw.slice(0, -2)); + assert.strictEqual(resp.events[0].name, "E1"); + assert.strictEqual(resp.events[0].data.data, 44); + assert.strictEqual(resp.events[1].name, "E2"); + assert.strictEqual(resp.events[1].data.data, 1234); + assert.strictEqual(resp.events[2].name, "E3"); + assert.strictEqual(resp.events[2].data.data, 9); + assert.strictEqual(resp.events[3].name, "E5"); + assert.deepStrictEqual( + resp.events[3].data.data, + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ); + assert.strictEqual(resp.events[4].name, "E6"); + assert.deepStrictEqual( + resp.events[4].data.data, + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + ); + }); + + it("Can use enum in idl", async () => { + const resp1 = await program.methods.testInputEnum({ first: {} }).simulate(); + const event1 = resp1.events[0].data as IdlEvents["E7"]; + assert.deepEqual(event1.data.first, {}); + + const resp2 = await program.methods + .testInputEnum({ second: { x: new BN(1), y: new BN(2) } }) + .simulate(); + const event2 = resp2.events[0].data as IdlEvents["E7"]; + assert.isTrue(new BN(1).eq(event2.data.second.x)); + assert.isTrue(new BN(2).eq(event2.data.second.y)); + + const resp3 = await program.methods + .testInputEnum({ + tupleStructTest: [ + { data1: 1, data2: 11, data3: 111, data4: new BN(1111) }, + ], + }) + .simulate(); + const event3 = resp3.events[0].data as IdlEvents["E7"]; + assert.strictEqual(event3.data.tupleStructTest[0].data1, 1); + assert.strictEqual(event3.data.tupleStructTest[0].data2, 11); + assert.strictEqual(event3.data.tupleStructTest[0].data3, 111); + assert.isTrue(event3.data.tupleStructTest[0].data4.eq(new BN(1111))); + + const resp4 = await program.methods + .testInputEnum({ tupleTest: [1, 2, 3, 4] }) + .simulate(); + const event4 = resp4.events[0].data as IdlEvents["E7"]; + assert.strictEqual(event4.data.tupleTest[0], 1); + assert.strictEqual(event4.data.tupleTest[1], 2); + assert.strictEqual(event4.data.tupleTest[2], 3); + assert.strictEqual(event4.data.tupleTest[3], 4); + }); + + let dataI8; + + it("Can use i8 in the idl", async () => { + dataI8 = anchor.web3.Keypair.generate(); + await program.rpc.testI8(-3, { + accounts: { + data: dataI8.publicKey, + }, + instructions: [await program.account.dataI8.createInstruction(dataI8)], + signers: [dataI8], + }); + const dataAccount = await program.account.dataI8.fetch(dataI8.publicKey); + assert.strictEqual(dataAccount.data, -3); + }); + + let dataPubkey; + + it("Can use i16 in the idl", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testI16(-2048, { + accounts: { + data: data.publicKey, + }, + instructions: [await program.account.dataI16.createInstruction(data)], + signers: [data], + }); + const dataAccount = await program.account.dataI16.fetch(data.publicKey); + assert.strictEqual(dataAccount.data, -2048); + + dataPubkey = data.publicKey; + }); + + it("Can use base58 strings to fetch an account", async () => { + const dataAccount = await program.account.dataI16.fetch( + dataPubkey.toString() + ); + assert.strictEqual(dataAccount.data, -2048); + }); + + it("Should fail to close an account when sending lamports to itself", async () => { + try { + await program.rpc.testClose({ + accounts: { + data: data.publicKey, + solDest: data.publicKey, + }, + }); + expect(false).to.be.true; + } catch (err) { + const errMsg = "A close constraint was violated"; + assert.strictEqual(err.error.errorMessage, errMsg); + assert.strictEqual(err.error.errorCode.number, 2011); + } + }); + + it("Can close an account", async () => { + const openAccount = await program.provider.connection.getAccountInfo( + data.publicKey + ); + assert.isNotNull(openAccount); + + let beforeBalance = ( + await program.provider.connection.getAccountInfo( + provider.wallet.publicKey + ) + ).lamports; + + await program.rpc.testClose({ + accounts: { + data: data.publicKey, + solDest: provider.wallet.publicKey, + }, + }); + + let afterBalance = ( + await program.provider.connection.getAccountInfo( + provider.wallet.publicKey + ) + ).lamports; + + // Retrieved rent exemption sol. + expect(afterBalance > beforeBalance).to.be.true; + + const closedAccount = await program.provider.connection.getAccountInfo( + data.publicKey + ); + assert.isNull(closedAccount); + }); + + it("Can use instruction data in accounts constraints", async () => { + // b"my-seed" + const seed = Buffer.from([109, 121, 45, 115, 101, 101, 100]); + const [myPda, nonce] = await PublicKey.findProgramAddress( + [seed, anchor.web3.SYSVAR_RENT_PUBKEY.toBuffer()], + program.programId + ); + + await program.rpc.testInstructionConstraint(nonce, { + accounts: { + myPda, + myAccount: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + }); + }); + + it("Can create a PDA account with instruction data", async () => { + const seed = Buffer.from([1, 2, 3, 4]); + const domain = "my-domain"; + const foo = anchor.web3.SYSVAR_RENT_PUBKEY; + const [myPda, nonce] = await PublicKey.findProgramAddress( + [ + Buffer.from(anchor.utils.bytes.utf8.encode("my-seed")), + Buffer.from(anchor.utils.bytes.utf8.encode(domain)), + foo.toBuffer(), + seed, + ], + program.programId + ); + + await program.rpc.testPdaInit(domain, seed, nonce, { + accounts: { + myPda, + myPayer: provider.wallet.publicKey, + foo, + systemProgram: anchor.web3.SystemProgram.programId, + }, + }); + + const myPdaAccount = await program.account.dataU16.fetch(myPda); + assert.strictEqual(myPdaAccount.data, 6); + }); + + it("Can create a zero copy PDA account", async () => { + const [myPda, nonce] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], + program.programId + ); + await program.rpc.testPdaInitZeroCopy({ + accounts: { + myPda, + myPayer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + }); + + const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); + assert.strictEqual(myPdaAccount.data, 9); + assert.strictEqual(myPdaAccount.bump, nonce); + }); + + it("Can write to a zero copy PDA account", async () => { + const [myPda, bump] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], + program.programId + ); + await program.rpc.testPdaMutZeroCopy({ + accounts: { + myPda, + myPayer: provider.wallet.publicKey, + }, + }); + + const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); + assert.strictEqual(myPdaAccount.data, 1234); + assert.strictEqual(myPdaAccount.bump, bump); + }); + + it("Can create a token account from seeds pda", async () => { + const [mint, mint_bump] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-mint-seed"))], + program.programId + ); + const [myPda, token_bump] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-token-seed"))], + program.programId + ); + await program.rpc.testTokenSeedsInit({ + accounts: { + myPda, + mint, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + tokenProgram: TOKEN_PROGRAM_ID, + }, + }); + + const mintAccount = new Token( + program.provider.connection, + mint, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await mintAccount.getAccountInfo(myPda); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint)); + }); + + it("Can execute a fallback function", async () => { + await nativeAssert.rejects( + async () => { + await anchor.utils.rpc.invoke(program.programId); + }, + (err) => { + assert.isTrue(err.toString().includes("custom program error: 0x4d2")); + return true; + } + ); + }); + + it("Can init a random account", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testInit({ + accounts: { + data: data.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data], + }); + + const account = await program.account.dataI8.fetch(data.publicKey); + assert.strictEqual(account.data, 3); + }); + + it("Can init a random account prefunded", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testInit({ + accounts: { + data: data.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: data.publicKey, + lamports: 4039280, + }), + ], + }); + + const account = await program.account.dataI8.fetch(data.publicKey); + assert.strictEqual(account.data, 3); + }); + + it("Can init a random zero copy account", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testInitZeroCopy({ + accounts: { + data: data.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data], + }); + const account = await program.account.dataZeroCopy.fetch(data.publicKey); + assert.strictEqual(account.data, 10); + assert.strictEqual(account.bump, 2); + }); + + let mint = undefined; + + it("Can create a random mint account", async () => { + mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue(mintAccount.mintAuthority.equals(provider.wallet.publicKey)); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + ); + }); + + it("Can create a random mint account prefunded", async () => { + mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: mint.publicKey, + lamports: 4039280, + }), + ], + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue(mintAccount.mintAuthority.equals(provider.wallet.publicKey)); + }); + + it("Can create a random token account", async () => { + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await client.getAccountInfo(token.publicKey); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); + }); + + it("Can create a random token with prefunding", async () => { + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: token.publicKey, + lamports: 4039280, + }), + ], + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await client.getAccountInfo(token.publicKey); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); + }); + + it("Can create a random token with prefunding under the rent exemption", async () => { + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: token.publicKey, + lamports: 1, + }), + ], + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await client.getAccountInfo(token.publicKey); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); + }); + + it("Can initialize multiple accounts via a composite payer", async () => { + const data1 = anchor.web3.Keypair.generate(); + const data2 = anchor.web3.Keypair.generate(); + + const tx = await program.methods + .testCompositePayer() + .accounts({ + composite: { + data: data1.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + data: data2.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }) + .signers([data1, data2]) + .rpc(); + + const account1 = await program.account.dataI8.fetch(data1.publicKey); + assert.strictEqual(account1.data, 1); + + const account2 = await program.account.data.fetch(data2.publicKey); + assert.strictEqual(account2.udata.toNumber(), 2); + assert.strictEqual(account2.idata.toNumber(), 3); + }); + + describe("associated_token constraints", () => { + let associatedToken = null; + // apparently cannot await here so doing it in the 'it' statements + let client = Token.createMint( + program.provider.connection, + wallet.payer, + provider.wallet.publicKey, + provider.wallet.publicKey, + 9, + TOKEN_PROGRAM_ID + ); + + it("Can create an associated token account", async () => { + const localClient = await client; + associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + localClient.publicKey, + provider.wallet.publicKey + ); + + await program.rpc.testInitAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + }, + }); + + const account = await localClient.getAccountInfo(associatedToken); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(localClient.publicKey)); + }); + + it("Can validate associated_token constraints", async () => { + const localClient = await client; + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + + let otherMint = await Token.createMint( + program.provider.connection, + wallet.payer, + provider.wallet.publicKey, + provider.wallet.publicKey, + 9, + TOKEN_PROGRAM_ID + ); + + await nativeAssert.rejects( + async () => { + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: otherMint.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + }, + (err) => { + assert.strictEqual(err.error.errorCode.number, 2009); + return true; + } + ); + }); + + it("associated_token constraints check do not allow authority change", async () => { + const localClient = await client; + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + + await localClient.setAuthority( + associatedToken, + anchor.web3.Keypair.generate().publicKey, + "AccountOwner", + wallet.payer, + [] + ); + + await nativeAssert.rejects( + async () => { + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + }, + (err) => { + assert.strictEqual(err.error.errorCode.number, 2015); + return true; + } + ); + }); + }); + + it("Can fetch all accounts of a given type", async () => { + // Initialize the accounts. + const data1 = anchor.web3.Keypair.generate(); + const data2 = anchor.web3.Keypair.generate(); + const data3 = anchor.web3.Keypair.generate(); + const data4 = anchor.web3.Keypair.generate(); + // Initialize filterable data. + const filterable1 = anchor.web3.Keypair.generate().publicKey; + const filterable2 = anchor.web3.Keypair.generate().publicKey; + // Set up a secondary wallet and program. + const anotherProvider = new anchor.AnchorProvider( + program.provider.connection, + new anchor.Wallet(anchor.web3.Keypair.generate()), + { commitment: program.provider.connection.commitment } + ); + const anotherProgram = new anchor.Program( + miscIdl, + program.programId, + anotherProvider + ); + // Request airdrop for secondary wallet. + const signature = await program.provider.connection.requestAirdrop( + anotherProvider.wallet.publicKey, + anchor.web3.LAMPORTS_PER_SOL + ); + await program.provider.connection.confirmTransaction(signature); + // Create all the accounts. + await Promise.all([ + program.rpc.testFetchAll(filterable1, { + accounts: { + data: data1.publicKey, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data1], + }), + program.rpc.testFetchAll(filterable1, { + accounts: { + data: data2.publicKey, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data2], + }), + program.rpc.testFetchAll(filterable2, { + accounts: { + data: data3.publicKey, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data3], + }), + anotherProgram.rpc.testFetchAll(filterable1, { + accounts: { + data: data4.publicKey, + authority: anotherProvider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data4], + }), + ]); + // Call for multiple kinds of .all. + const allAccounts = await program.account.dataWithFilter.all(); + const allAccountsFilteredByBuffer = + await program.account.dataWithFilter.all( + provider.wallet.publicKey.toBuffer() + ); + const allAccountsFilteredByProgramFilters1 = + await program.account.dataWithFilter.all([ + { + memcmp: { + offset: 8, + bytes: provider.wallet.publicKey.toBase58(), + }, + }, + { memcmp: { offset: 40, bytes: filterable1.toBase58() } }, + ]); + const allAccountsFilteredByProgramFilters2 = + await program.account.dataWithFilter.all([ + { + memcmp: { + offset: 8, + bytes: provider.wallet.publicKey.toBase58(), + }, + }, + { memcmp: { offset: 40, bytes: filterable2.toBase58() } }, + ]); + // Without filters there should be 4 accounts. + assert.lengthOf(allAccounts, 4); + // Filtering by main wallet there should be 3 accounts. + assert.lengthOf(allAccountsFilteredByBuffer, 3); + // Filtering all the main wallet accounts and matching the filterable1 value + // results in a 2 accounts. + assert.lengthOf(allAccountsFilteredByProgramFilters1, 2); + // Filtering all the main wallet accounts and matching the filterable2 value + // results in 1 account. + assert.lengthOf(allAccountsFilteredByProgramFilters2, 1); + }); + + it("Can use pdas with empty seeds", async () => { + const [pda, bump] = await PublicKey.findProgramAddress( + [], + program.programId + ); + + await program.rpc.testInitWithEmptySeeds({ + accounts: { + pda: pda, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + }); + await program.rpc.testEmptySeedsConstraint({ + accounts: { + pda: pda, + }, + }); + + const [pda2] = await PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("non-empty")], + program.programId + ); + await nativeAssert.rejects( + program.rpc.testEmptySeedsConstraint({ + accounts: { + pda: pda2, + }, + }), + (err) => { + assert.equal(err.error.errorCode.number, 2006); + return true; + } + ); + }); + + const ifNeededAcc = anchor.web3.Keypair.generate(); + + it("Can init if needed a new account", async () => { + await program.rpc.testInitIfNeeded(1, { + accounts: { + data: ifNeededAcc.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + }, + signers: [ifNeededAcc], + }); + const account = await program.account.dataU16.fetch(ifNeededAcc.publicKey); + assert.strictEqual(account.data, 1); + }); + + it("Can init if needed a previously created account", async () => { + await program.rpc.testInitIfNeeded(3, { + accounts: { + data: ifNeededAcc.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + }, + signers: [ifNeededAcc], + }); + const account = await program.account.dataU16.fetch(ifNeededAcc.publicKey); + assert.strictEqual(account.data, 3); + }); + + it("Can use const for array size", async () => { + const data = anchor.web3.Keypair.generate(); + const tx = await program.rpc.testConstArraySize(99, { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [ + await program.account.dataConstArraySize.createInstruction(data), + ], + }); + const dataAccount = await program.account.dataConstArraySize.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, [99, ...new Array(9).fill(0)]); + }); + + it("Can use const for instruction data size", async () => { + const data = anchor.web3.Keypair.generate(); + const dataArray = [99, ...new Array(9).fill(0)]; + const tx = await program.rpc.testConstIxDataSize(dataArray, { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [ + await program.account.dataConstArraySize.createInstruction(data), + ], + }); + const dataAccount = await program.account.dataConstArraySize.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, dataArray); + }); + + it("Should include BASE const in IDL", async () => { + assert.isDefined( + miscIdl.constants.find( + (c) => c.name === "BASE" && c.type === "u128" && c.value === "1_000_000" + ) + ); + }); + + it("Should include DECIMALS const in IDL", async () => { + assert.isDefined( + miscIdl.constants.find( + (c) => c.name === "DECIMALS" && c.type === "u8" && c.value === "6" + ) + ); + }); + + it("Should not include NO_IDL const in IDL", async () => { + assert.isUndefined(miscIdl.constants.find((c) => c.name === "NO_IDL")); + }); + + it("init_if_needed throws if account exists but is not owned by the expected program", async () => { + const newAcc = await anchor.web3.PublicKey.findProgramAddress( + [utf8.encode("hello")], + program.programId + ); + await program.rpc.testInitIfNeededChecksOwner({ + accounts: { + data: newAcc[0], + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + owner: program.programId, + }, + }); + + try { + await program.rpc.testInitIfNeededChecksOwner({ + accounts: { + data: newAcc[0], + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + owner: anchor.web3.Keypair.generate().publicKey, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2004); + } + }); + + it("init_if_needed throws if pda account exists but does not have the expected seeds", async () => { + const newAcc = await anchor.web3.PublicKey.findProgramAddress( + [utf8.encode("nothello")], + program.programId + ); + await program.rpc.testInitIfNeededChecksSeeds("nothello", { + accounts: { + data: newAcc[0], + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + }, + }); + + // this will throw if it is not a proper PDA + // we need this so we know that the following tx failed + // not because it couldn't create this pda + // but because the two pdas were different + anchor.web3.PublicKey.createProgramAddress( + [utf8.encode("hello")], + program.programId + ); + + try { + await program.rpc.testInitIfNeededChecksSeeds("hello", { + accounts: { + data: newAcc[0], + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + }); + + it("init_if_needed throws if account exists but is not the expected space", async () => { + const newAcc = anchor.web3.Keypair.generate(); + const _irrelevantForTest = 3; + await program.rpc.initWithSpace(_irrelevantForTest, { + accounts: { + data: newAcc.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + }, + signers: [newAcc], + }); + + try { + await program.rpc.testInitIfNeeded(_irrelevantForTest, { + accounts: { + data: newAcc.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + }, + signers: [newAcc], + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2019); + } + }); + + it("init_if_needed throws if mint exists but has the wrong mint authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + try { + await program.rpc.testInitMintIfNeeded(6, { + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + mintAuthority: anchor.web3.Keypair.generate().publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + signers: [mint], + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2016); + } + }); + + it("init_if_needed throws if mint exists but has the wrong freeze authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + try { + await program.rpc.testInitMintIfNeeded(6, { + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: anchor.web3.Keypair.generate().publicKey, + }, + signers: [mint], + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2017); + } + }); + + it("init_if_needed throws if mint exists but has the wrong decimals", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + try { + await program.rpc.testInitMintIfNeeded(9, { + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + signers: [mint], + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2018); + } + }); + + it("init_if_needed throws if token exists but has the wrong owner", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + + try { + await program.rpc.testInitTokenIfNeeded({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + authority: anchor.web3.Keypair.generate().publicKey, + }, + signers: [token], + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); + } + }); + + it("init_if_needed throws if token exists but has the wrong mint", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const mint2 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint2.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint2], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + + try { + await program.rpc.testInitTokenIfNeeded({ + accounts: { + token: token.publicKey, + mint: mint2.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + authority: provider.wallet.publicKey, + }, + signers: [token], + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2014); + } + }); + + it("init_if_needed throws if associated token exists but has the wrong owner", async () => { + const mint = Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + mint.publicKey, + provider.wallet.publicKey + ); + + await program.rpc.testInitAssociatedToken({ + accounts: { + token: associatedToken, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + }, + }); + + try { + await program.rpc.testInitAssociatedTokenIfNeeded({ + accounts: { + token: associatedToken, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + authority: anchor.web3.Keypair.generate().publicKey, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); + } + }); + + it("init_if_needed throws if associated token exists but has the wrong mint", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const mint2 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint2.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint2], + }); + + const associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + mint.publicKey, + provider.wallet.publicKey + ); + + await program.rpc.testInitAssociatedToken({ + accounts: { + token: associatedToken, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + }, + }); + + try { + await program.rpc.testInitAssociatedTokenIfNeeded({ + accounts: { + token: associatedToken, + mint: mint2.publicKey, + payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + authority: provider.wallet.publicKey, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2014); + } + }); + + it("init_if_needed throws if token exists with correct owner and mint but is not the ATA", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + mint.publicKey, + provider.wallet.publicKey + ); + + await program.rpc.testInitAssociatedToken({ + accounts: { + token: associatedToken, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + }, + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + + try { + await program.rpc.testInitAssociatedTokenIfNeeded({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + authority: provider.wallet.publicKey, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 3014); + } + }); + + it("Can use multidimensional array", async () => { + const array2d = new Array(10).fill(new Array(10).fill(99)); + const data = anchor.web3.Keypair.generate(); + await program.rpc.testMultidimensionalArray(array2d, { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [ + await program.account.dataMultidimensionalArray.createInstruction(data), + ], + }); + const dataAccount = await program.account.dataMultidimensionalArray.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, array2d); + }); + + it("Can use multidimensional array with const sizes", async () => { + const array2d = new Array(10).fill(new Array(11).fill(22)); + const data = anchor.web3.Keypair.generate(); + await program.rpc.testMultidimensionalArrayConstSizes(array2d, { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [ + await program.account.dataMultidimensionalArrayConstSizes.createInstruction( + data + ), + ], + }); + const dataAccount = + await program.account.dataMultidimensionalArrayConstSizes.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, array2d); + }); + + describe("Can validate PDAs derived from other program ids", () => { + it("With bumps using create_program_address", async () => { + const [firstPDA, firstBump] = + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + ASSOCIATED_TOKEN_PROGRAM_ID + ); + const [secondPDA, secondBump] = + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + program.programId + ); + + // correct bump but wrong address + const wrongAddress = anchor.web3.Keypair.generate().publicKey; + try { + await program.rpc.testProgramIdConstraint(firstBump, secondBump, { + accounts: { + first: wrongAddress, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // matching bump seed for wrong address but derived from wrong program + try { + await program.rpc.testProgramIdConstraint(secondBump, secondBump, { + accounts: { + first: secondPDA, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // correct inputs should lead to successful tx + await program.rpc.testProgramIdConstraint(firstBump, secondBump, { + accounts: { + first: firstPDA, + second: secondPDA, + }, + }); + }); + + it("With bumps using find_program_address", async () => { + const firstPDA = ( + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + ASSOCIATED_TOKEN_PROGRAM_ID + ) + )[0]; + const secondPDA = ( + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + program.programId + ) + )[0]; + + // random wrong address + const wrongAddress = anchor.web3.Keypair.generate().publicKey; + try { + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: wrongAddress, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // same seeds but derived from wrong program + try { + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: secondPDA, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // correct inputs should lead to successful tx + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: firstPDA, + second: secondPDA, + }, + }); + }); + }); + describe("Token Constraint Test", () => { + it("Token Constraint Test(no init) - Can make token::mint and token::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + await program.rpc.testTokenConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); + }); + + it("Token Constraint Test(no init) - Can make only token::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + await program.rpc.testOnlyAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + }); + + it("Token Constraint Test(no init) - Can make only token::mint", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + await program.rpc.testOnlyMintConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.isTrue(account.mint.equals(mint.publicKey)); + }); + + it("Token Constraint Test(no init) - throws if token::mint mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const mint1 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint1.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint1], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + try { + await program.rpc.testTokenConstraint({ + accounts: { + token: token.publicKey, + mint: mint1.publicKey, + payer: provider.wallet.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2014); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenMint"); + } + }); + + it("Token Constraint Test(no init) - throws if token::authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testTokenAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + fakeAuthority: fakeAuthority.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); + } + }); + + it("Token Constraint Test(no init) - throws if both token::authority, token::mint mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + const mint1 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint1.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint1], + }); + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testTokenAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint1.publicKey, + fakeAuthority: fakeAuthority.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); + } + }); + + it("Mint Constraint Test(no init) - mint::decimals, mint::authority, mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + ); + }); + + it("Mint Constraint Test(no init) - throws if mint::decimals mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + const fakeDecimal = 5; + try { + await program.rpc.testMintConstraint(fakeDecimal, { + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2018); + assert.strictEqual(err.error.errorCode.code, "ConstraintMintDecimals"); + } + }); + + it("Mint Constraint Test(no init) - throws if mint::mint_authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: fakeAuthority.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2016); + assert.strictEqual( + err.error.errorCode.code, + "ConstraintMintMintAuthority" + ); + } + }); + + it("Mint Constraint Test(no init) - throws if mint::freeze_authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: fakeAuthority.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2017); + assert.strictEqual( + err.error.errorCode.code, + "ConstraintMintFreezeAuthority" + ); + } + }); + + it("Mint Constraint Test(no init) - can write only mint::decimals", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + await program.rpc.testMintOnlyDecimalsConstraint(6, { + accounts: { + mint: mint.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + }); + + it("Mint Constraint Test(no init) - can write only mint::authority and mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + await program.rpc.testMintOnlyAuthConstraint({ + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + ); + }); + + it("Mint Constraint Test(no init) - can write only mint::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + await program.rpc.testMintOnlyOneAuthConstraint({ + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + }); + + it("Mint Constraint Test(no init) - can write only mint::decimals and mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + await program.rpc.testMintMissMintAuthConstraint(6, { + accounts: { + mint: mint.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + ); + }); + it("check versioned transaction is now available", async () => { + let thisTx = new VersionedTransaction( + new Message({ + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 0, + numRequiredSignatures: 0, + }, + accountKeys: [new PublicKey([0]).toString()], + instructions: [{ accounts: [0], data: "", programIdIndex: 0 }], + recentBlockhash: "", + }) + ); + assert.isDefined(thisTx); + }); + }); +}); From c864cd5d4f019e6bd5f93641e01bd82fc74041d4 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 20 Oct 2022 23:26:08 -0400 Subject: [PATCH 092/109] enable cpi for optional tests --- tests/optional/programs/optional/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/optional/programs/optional/Cargo.toml b/tests/optional/programs/optional/Cargo.toml index 090174508c..1fd65989fc 100644 --- a/tests/optional/programs/optional/Cargo.toml +++ b/tests/optional/programs/optional/Cargo.toml @@ -11,6 +11,7 @@ name = "optional" [features] no-entrypoint = [] cpi = ["no-entrypoint"] +default = ["cpi"] [dependencies] anchor-lang = { path = "../../../../lang" } From 18d3b8190baad2ed923b04fd4a9ea6b9ba421560 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Fri, 21 Oct 2022 00:12:46 -0400 Subject: [PATCH 093/109] Revert "enable cpi for optional tests" This reverts commit c864cd5d4f019e6bd5f93641e01bd82fc74041d4. --- tests/optional/programs/optional/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/optional/programs/optional/Cargo.toml b/tests/optional/programs/optional/Cargo.toml index 1fd65989fc..090174508c 100644 --- a/tests/optional/programs/optional/Cargo.toml +++ b/tests/optional/programs/optional/Cargo.toml @@ -11,7 +11,6 @@ name = "optional" [features] no-entrypoint = [] cpi = ["no-entrypoint"] -default = ["cpi"] [dependencies] anchor-lang = { path = "../../../../lang" } From bccae2fad13bb05d2e2c0cd02dc2705f6d324d67 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sun, 23 Oct 2022 15:57:44 -0500 Subject: [PATCH 094/109] simplify misc tests --- .../programs/misc-optional/src/context.rs | 16 + tests/misc/programs/misc-optional/src/lib.rs | 20 +- tests/misc/tests/misc-optional/Test.toml | 2 - .../misc/tests/misc-optional/misc-optional.ts | 2190 ---------- tests/misc/tests/misc/misc.ts | 3691 +++++++++-------- 5 files changed, 1910 insertions(+), 4009 deletions(-) delete mode 100644 tests/misc/tests/misc-optional/Test.toml delete mode 100644 tests/misc/tests/misc-optional/misc-optional.ts diff --git a/tests/misc/programs/misc-optional/src/context.rs b/tests/misc/programs/misc-optional/src/context.rs index d23892ea33..46ed539648 100644 --- a/tests/misc/programs/misc-optional/src/context.rs +++ b/tests/misc/programs/misc-optional/src/context.rs @@ -182,6 +182,22 @@ pub struct TestClose<'info> { sol_dest: Option>, } +#[derive(Accounts)] +pub struct TestCloseTwice<'info> { + #[account(mut, close = sol_dest)] + pub data: Option>, + /// CHECK: + pub sol_dest: Option>, +} + +#[derive(Accounts)] +pub struct TestCloseMut<'info> { + #[account(mut)] + pub data: Option>, + /// CHECK: + pub sol_dest: Option>, +} + #[derive(Accounts)] pub struct TestU16<'info> { #[account(zero)] diff --git a/tests/misc/programs/misc-optional/src/lib.rs b/tests/misc/programs/misc-optional/src/lib.rs index d915a97eee..9b93f6cb75 100644 --- a/tests/misc/programs/misc-optional/src/lib.rs +++ b/tests/misc/programs/misc-optional/src/lib.rs @@ -1,4 +1,4 @@ -//! Misc example is a catchall program for testing unrelated features. +//! Misc optional example is a catchall program for testing unrelated features. //! It's not too instructive/coherent by itself, so please see other examples. use account::MAX_SIZE; @@ -129,6 +129,24 @@ pub mod misc_optional { Ok(()) } + pub fn test_close_twice(ctx: Context) -> Result<()> { + let data_account = &ctx.accounts.data.as_ref().unwrap(); + let sol_dest_info = ctx.accounts.sol_dest.as_ref().unwrap().to_account_info(); + data_account.close(sol_dest_info)?; + let data_account_info: &AccountInfo = data_account.as_ref(); + require_keys_eq!(*data_account_info.owner, System::id()); + Ok(()) + } + + pub fn test_close_mut(ctx: Context) -> Result<()> { + let data_account = &ctx.accounts.data.as_ref().unwrap(); + let sol_dest_info = ctx.accounts.sol_dest.as_ref().unwrap().to_account_info(); + data_account.close(sol_dest_info)?; + let data_account_info: &AccountInfo = data_account.as_ref(); + require_keys_eq!(*data_account_info.owner, System::id()); + Ok(()) + } + pub fn test_instruction_constraint( _ctx: Context, _nonce: u8, diff --git a/tests/misc/tests/misc-optional/Test.toml b/tests/misc/tests/misc-optional/Test.toml deleted file mode 100644 index 60785cbf93..0000000000 --- a/tests/misc/tests/misc-optional/Test.toml +++ /dev/null @@ -1,2 +0,0 @@ -[scripts] -test = "yarn run ts-mocha -t 1000000 ./tests/misc-optional/*.ts" diff --git a/tests/misc/tests/misc-optional/misc-optional.ts b/tests/misc/tests/misc-optional/misc-optional.ts deleted file mode 100644 index 697a089c63..0000000000 --- a/tests/misc/tests/misc-optional/misc-optional.ts +++ /dev/null @@ -1,2190 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { - Program, - BN, - IdlAccounts, - AnchorError, - Wallet, - IdlTypes, - IdlEvents, -} from "@project-serum/anchor"; -import { - PublicKey, - Keypair, - SystemProgram, - SYSVAR_RENT_PUBKEY, - Message, - VersionedTransaction, -} from "@solana/web3.js"; -import { - TOKEN_PROGRAM_ID, - Token, - ASSOCIATED_TOKEN_PROGRAM_ID, -} from "@solana/spl-token"; -import { MiscOptional } from "../../target/types/misc_optional"; -import { Misc2 } from "../../target/types/misc2"; - -const utf8 = anchor.utils.bytes.utf8; -const { assert, expect } = require("chai"); -const nativeAssert = require("assert"); -const miscIdl = require("../../target/idl/misc.json"); - -describe("misc-optional", () => { - // Configure the client to use the local cluster. - const provider = anchor.AnchorProvider.env(); - const wallet = provider.wallet as Wallet; - anchor.setProvider(provider); - const program = anchor.workspace.MiscOptional as Program; - 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(); - - it("Can use u128 and i128", async () => { - const tx = await program.rpc.initialize( - new anchor.BN(1234), - new anchor.BN(22), - { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [await program.account.data.createInstruction(data)], - } - ); - const dataAccount = await program.account.data.fetch(data.publicKey); - assert.isTrue(dataAccount.udata.eq(new anchor.BN(1234))); - assert.isTrue(dataAccount.idata.eq(new anchor.BN(22))); - }); - - it("Can use u16", async () => { - const data = anchor.web3.Keypair.generate(); - const tx = await program.rpc.testU16(99, { - accounts: { - myAccount: data.publicKey, - }, - signers: [data], - instructions: [await program.account.dataU16.createInstruction(data)], - }); - const dataAccount = await program.account.dataU16.fetch(data.publicKey); - assert.strictEqual(dataAccount.data, 99); - }); - - it("Can use the owner constraint", async () => { - await program.rpc.testOwner({ - accounts: { - data: data.publicKey, - misc: program.programId, - }, - options: { skipPreflight: true }, - }); - - await nativeAssert.rejects( - async () => { - await program.rpc.testOwner({ - accounts: { - data: provider.wallet.publicKey, - misc: program.programId, - }, - }); - }, - (err) => { - return true; - } - ); - }); - - it("Can use the executable attribute", async () => { - await program.rpc.testExecutable({ - accounts: { - program: program.programId, - }, - }); - - await nativeAssert.rejects( - async () => { - await program.rpc.testExecutable({ - accounts: { - program: provider.wallet.publicKey, - }, - }); - }, - (err) => { - return true; - } - ); - }); - - it("Can CPI to state instructions", async () => { - const oldData = new anchor.BN(0); - 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 = [ - "Program FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG invoke [1]", - "Program log: Instruction: TestSimulate", - "Program data: NgyCA9omwbMsAAAA", - "Program data: fPhuIELK/k7SBAAA", - "Program data: jvbowsvlmkcJAAAA", - "Program data: zxM5neEnS1kBAgMEBQYHCAkK", - "Program data: g06Ei2GL1gIBAgMEBQYHCAkKCw==", - ]; - - assert.deepStrictEqual(expectedRaw, resp.raw.slice(0, -2)); - assert.strictEqual(resp.events[0].name, "E1"); - assert.strictEqual(resp.events[0].data.data, 44); - assert.strictEqual(resp.events[1].name, "E2"); - assert.strictEqual(resp.events[1].data.data, 1234); - assert.strictEqual(resp.events[2].name, "E3"); - assert.strictEqual(resp.events[2].data.data, 9); - assert.strictEqual(resp.events[3].name, "E5"); - assert.deepStrictEqual( - resp.events[3].data.data, - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - ); - assert.strictEqual(resp.events[4].name, "E6"); - assert.deepStrictEqual( - resp.events[4].data.data, - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - ); - }); - - it("Can use enum in idl", async () => { - const resp1 = await program.methods.testInputEnum({ first: {} }).simulate(); - const event1 = resp1.events[0].data as IdlEvents["E7"]; - assert.deepEqual(event1.data.first, {}); - - const resp2 = await program.methods - .testInputEnum({ second: { x: new BN(1), y: new BN(2) } }) - .simulate(); - const event2 = resp2.events[0].data as IdlEvents["E7"]; - assert.isTrue(new BN(1).eq(event2.data.second.x)); - assert.isTrue(new BN(2).eq(event2.data.second.y)); - - const resp3 = await program.methods - .testInputEnum({ - tupleStructTest: [ - { data1: 1, data2: 11, data3: 111, data4: new BN(1111) }, - ], - }) - .simulate(); - const event3 = resp3.events[0].data as IdlEvents["E7"]; - assert.strictEqual(event3.data.tupleStructTest[0].data1, 1); - assert.strictEqual(event3.data.tupleStructTest[0].data2, 11); - assert.strictEqual(event3.data.tupleStructTest[0].data3, 111); - assert.isTrue(event3.data.tupleStructTest[0].data4.eq(new BN(1111))); - - const resp4 = await program.methods - .testInputEnum({ tupleTest: [1, 2, 3, 4] }) - .simulate(); - const event4 = resp4.events[0].data as IdlEvents["E7"]; - assert.strictEqual(event4.data.tupleTest[0], 1); - assert.strictEqual(event4.data.tupleTest[1], 2); - assert.strictEqual(event4.data.tupleTest[2], 3); - assert.strictEqual(event4.data.tupleTest[3], 4); - }); - - let dataI8; - - it("Can use i8 in the idl", async () => { - dataI8 = anchor.web3.Keypair.generate(); - await program.rpc.testI8(-3, { - accounts: { - data: dataI8.publicKey, - }, - instructions: [await program.account.dataI8.createInstruction(dataI8)], - signers: [dataI8], - }); - const dataAccount = await program.account.dataI8.fetch(dataI8.publicKey); - assert.strictEqual(dataAccount.data, -3); - }); - - let dataPubkey; - - it("Can use i16 in the idl", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testI16(-2048, { - accounts: { - data: data.publicKey, - }, - instructions: [await program.account.dataI16.createInstruction(data)], - signers: [data], - }); - const dataAccount = await program.account.dataI16.fetch(data.publicKey); - assert.strictEqual(dataAccount.data, -2048); - - dataPubkey = data.publicKey; - }); - - it("Can use base58 strings to fetch an account", async () => { - const dataAccount = await program.account.dataI16.fetch( - dataPubkey.toString() - ); - assert.strictEqual(dataAccount.data, -2048); - }); - - it("Should fail to close an account when sending lamports to itself", async () => { - try { - await program.rpc.testClose({ - accounts: { - data: data.publicKey, - solDest: data.publicKey, - }, - }); - expect(false).to.be.true; - } catch (err) { - const errMsg = "A close constraint was violated"; - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number, 2011); - } - }); - - it("Can close an account", async () => { - const openAccount = await program.provider.connection.getAccountInfo( - data.publicKey - ); - assert.isNotNull(openAccount); - - let beforeBalance = ( - await program.provider.connection.getAccountInfo( - provider.wallet.publicKey - ) - ).lamports; - - await program.rpc.testClose({ - accounts: { - data: data.publicKey, - solDest: provider.wallet.publicKey, - }, - }); - - let afterBalance = ( - await program.provider.connection.getAccountInfo( - provider.wallet.publicKey - ) - ).lamports; - - // Retrieved rent exemption sol. - expect(afterBalance > beforeBalance).to.be.true; - - const closedAccount = await program.provider.connection.getAccountInfo( - data.publicKey - ); - assert.isNull(closedAccount); - }); - - it("Can use instruction data in accounts constraints", async () => { - // b"my-seed" - const seed = Buffer.from([109, 121, 45, 115, 101, 101, 100]); - const [myPda, nonce] = await PublicKey.findProgramAddress( - [seed, anchor.web3.SYSVAR_RENT_PUBKEY.toBuffer()], - program.programId - ); - - await program.rpc.testInstructionConstraint(nonce, { - accounts: { - myPda, - myAccount: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - }); - }); - - it("Can create a PDA account with instruction data", async () => { - const seed = Buffer.from([1, 2, 3, 4]); - const domain = "my-domain"; - const foo = anchor.web3.SYSVAR_RENT_PUBKEY; - const [myPda, nonce] = await PublicKey.findProgramAddress( - [ - Buffer.from(anchor.utils.bytes.utf8.encode("my-seed")), - Buffer.from(anchor.utils.bytes.utf8.encode(domain)), - foo.toBuffer(), - seed, - ], - program.programId - ); - - await program.rpc.testPdaInit(domain, seed, nonce, { - accounts: { - myPda, - myPayer: provider.wallet.publicKey, - foo, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); - - const myPdaAccount = await program.account.dataU16.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 6); - }); - - it("Can create a zero copy PDA account", async () => { - const [myPda, nonce] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], - program.programId - ); - await program.rpc.testPdaInitZeroCopy({ - accounts: { - myPda, - myPayer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); - - const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 9); - assert.strictEqual(myPdaAccount.bump, nonce); - }); - - it("Can write to a zero copy PDA account", async () => { - const [myPda, bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], - program.programId - ); - await program.rpc.testPdaMutZeroCopy({ - accounts: { - myPda, - myPayer: provider.wallet.publicKey, - }, - }); - - const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 1234); - assert.strictEqual(myPdaAccount.bump, bump); - }); - - it("Can create a token account from seeds pda", async () => { - const [mint, mint_bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-mint-seed"))], - program.programId - ); - const [myPda, token_bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-token-seed"))], - program.programId - ); - await program.rpc.testTokenSeedsInit({ - accounts: { - myPda, - mint, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - - const mintAccount = new Token( - program.provider.connection, - mint, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await mintAccount.getAccountInfo(myPda); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint)); - }); - - it("Can execute a fallback function", async () => { - await nativeAssert.rejects( - async () => { - await anchor.utils.rpc.invoke(program.programId); - }, - (err) => { - assert.isTrue(err.toString().includes("custom program error: 0x4d2")); - return true; - } - ); - }); - - it("Can init a random account", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testInit({ - accounts: { - data: data.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - }); - - const account = await program.account.dataI8.fetch(data.publicKey); - assert.strictEqual(account.data, 3); - }); - - it("Can init a random account prefunded", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testInit({ - accounts: { - data: data.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: data.publicKey, - lamports: 4039280, - }), - ], - }); - - const account = await program.account.dataI8.fetch(data.publicKey); - assert.strictEqual(account.data, 3); - }); - - it("Can init a random zero copy account", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testInitZeroCopy({ - accounts: { - data: data.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - }); - const account = await program.account.dataZeroCopy.fetch(data.publicKey); - assert.strictEqual(account.data, 10); - assert.strictEqual(account.bump, 2); - }); - - let mint = undefined; - - it("Can create a random mint account", async () => { - mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue(mintAccount.mintAuthority.equals(provider.wallet.publicKey)); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) - ); - }); - - it("Can create a random mint account prefunded", async () => { - mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: mint.publicKey, - lamports: 4039280, - }), - ], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue(mintAccount.mintAuthority.equals(provider.wallet.publicKey)); - }); - - it("Can create a random token account", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can create a random token with prefunding", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: token.publicKey, - lamports: 4039280, - }), - ], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can create a random token with prefunding under the rent exemption", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: token.publicKey, - lamports: 1, - }), - ], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can initialize multiple accounts via a composite payer", async () => { - const data1 = anchor.web3.Keypair.generate(); - const data2 = anchor.web3.Keypair.generate(); - - const tx = await program.methods - .testCompositePayer() - .accounts({ - composite: { - data: data1.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - data: data2.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }) - .signers([data1, data2]) - .rpc(); - - const account1 = await program.account.dataI8.fetch(data1.publicKey); - assert.strictEqual(account1.data, 1); - - const account2 = await program.account.data.fetch(data2.publicKey); - assert.strictEqual(account2.udata.toNumber(), 2); - assert.strictEqual(account2.idata.toNumber(), 3); - }); - - describe("associated_token constraints", () => { - let associatedToken = null; - // apparently cannot await here so doing it in the 'it' statements - let client = Token.createMint( - program.provider.connection, - wallet.payer, - provider.wallet.publicKey, - provider.wallet.publicKey, - 9, - TOKEN_PROGRAM_ID - ); - - it("Can create an associated token account", async () => { - const localClient = await client; - associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - localClient.publicKey, - provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: localClient.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, - }); - - const account = await localClient.getAccountInfo(associatedToken); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(localClient.publicKey)); - }); - - it("Can validate associated_token constraints", async () => { - const localClient = await client; - await program.rpc.testValidateAssociatedToken({ - accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: provider.wallet.publicKey, - }, - }); - - let otherMint = await Token.createMint( - program.provider.connection, - wallet.payer, - provider.wallet.publicKey, - provider.wallet.publicKey, - 9, - TOKEN_PROGRAM_ID - ); - - await nativeAssert.rejects( - async () => { - await program.rpc.testValidateAssociatedToken({ - accounts: { - token: associatedToken, - mint: otherMint.publicKey, - wallet: provider.wallet.publicKey, - }, - }); - }, - (err) => { - assert.strictEqual(err.error.errorCode.number, 2009); - return true; - } - ); - }); - - it("associated_token constraints check do not allow authority change", async () => { - const localClient = await client; - await program.rpc.testValidateAssociatedToken({ - accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: provider.wallet.publicKey, - }, - }); - - await localClient.setAuthority( - associatedToken, - anchor.web3.Keypair.generate().publicKey, - "AccountOwner", - wallet.payer, - [] - ); - - await nativeAssert.rejects( - async () => { - await program.rpc.testValidateAssociatedToken({ - accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: provider.wallet.publicKey, - }, - }); - }, - (err) => { - assert.strictEqual(err.error.errorCode.number, 2015); - return true; - } - ); - }); - }); - - it("Can fetch all accounts of a given type", async () => { - // Initialize the accounts. - const data1 = anchor.web3.Keypair.generate(); - const data2 = anchor.web3.Keypair.generate(); - const data3 = anchor.web3.Keypair.generate(); - const data4 = anchor.web3.Keypair.generate(); - // Initialize filterable data. - const filterable1 = anchor.web3.Keypair.generate().publicKey; - const filterable2 = anchor.web3.Keypair.generate().publicKey; - // Set up a secondary wallet and program. - const anotherProvider = new anchor.AnchorProvider( - program.provider.connection, - new anchor.Wallet(anchor.web3.Keypair.generate()), - { commitment: program.provider.connection.commitment } - ); - const anotherProgram = new anchor.Program( - miscIdl, - program.programId, - anotherProvider - ); - // Request airdrop for secondary wallet. - const signature = await program.provider.connection.requestAirdrop( - anotherProvider.wallet.publicKey, - anchor.web3.LAMPORTS_PER_SOL - ); - await program.provider.connection.confirmTransaction(signature); - // Create all the accounts. - await Promise.all([ - program.rpc.testFetchAll(filterable1, { - accounts: { - data: data1.publicKey, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data1], - }), - program.rpc.testFetchAll(filterable1, { - accounts: { - data: data2.publicKey, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data2], - }), - program.rpc.testFetchAll(filterable2, { - accounts: { - data: data3.publicKey, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data3], - }), - anotherProgram.rpc.testFetchAll(filterable1, { - accounts: { - data: data4.publicKey, - authority: anotherProvider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data4], - }), - ]); - // Call for multiple kinds of .all. - const allAccounts = await program.account.dataWithFilter.all(); - const allAccountsFilteredByBuffer = - await program.account.dataWithFilter.all( - provider.wallet.publicKey.toBuffer() - ); - const allAccountsFilteredByProgramFilters1 = - await program.account.dataWithFilter.all([ - { - memcmp: { - offset: 8, - bytes: provider.wallet.publicKey.toBase58(), - }, - }, - { memcmp: { offset: 40, bytes: filterable1.toBase58() } }, - ]); - const allAccountsFilteredByProgramFilters2 = - await program.account.dataWithFilter.all([ - { - memcmp: { - offset: 8, - bytes: provider.wallet.publicKey.toBase58(), - }, - }, - { memcmp: { offset: 40, bytes: filterable2.toBase58() } }, - ]); - // Without filters there should be 4 accounts. - assert.lengthOf(allAccounts, 4); - // Filtering by main wallet there should be 3 accounts. - assert.lengthOf(allAccountsFilteredByBuffer, 3); - // Filtering all the main wallet accounts and matching the filterable1 value - // results in a 2 accounts. - assert.lengthOf(allAccountsFilteredByProgramFilters1, 2); - // Filtering all the main wallet accounts and matching the filterable2 value - // results in 1 account. - assert.lengthOf(allAccountsFilteredByProgramFilters2, 1); - }); - - it("Can use pdas with empty seeds", async () => { - const [pda, bump] = await PublicKey.findProgramAddress( - [], - program.programId - ); - - await program.rpc.testInitWithEmptySeeds({ - accounts: { - pda: pda, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); - await program.rpc.testEmptySeedsConstraint({ - accounts: { - pda: pda, - }, - }); - - const [pda2] = await PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("non-empty")], - program.programId - ); - await nativeAssert.rejects( - program.rpc.testEmptySeedsConstraint({ - accounts: { - pda: pda2, - }, - }), - (err) => { - assert.equal(err.error.errorCode.number, 2006); - return true; - } - ); - }); - - const ifNeededAcc = anchor.web3.Keypair.generate(); - - it("Can init if needed a new account", async () => { - await program.rpc.testInitIfNeeded(1, { - accounts: { - data: ifNeededAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - signers: [ifNeededAcc], - }); - const account = await program.account.dataU16.fetch(ifNeededAcc.publicKey); - assert.strictEqual(account.data, 1); - }); - - it("Can init if needed a previously created account", async () => { - await program.rpc.testInitIfNeeded(3, { - accounts: { - data: ifNeededAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - signers: [ifNeededAcc], - }); - const account = await program.account.dataU16.fetch(ifNeededAcc.publicKey); - assert.strictEqual(account.data, 3); - }); - - it("Can use const for array size", async () => { - const data = anchor.web3.Keypair.generate(); - const tx = await program.rpc.testConstArraySize(99, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataConstArraySize.createInstruction(data), - ], - }); - const dataAccount = await program.account.dataConstArraySize.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, [99, ...new Array(9).fill(0)]); - }); - - it("Can use const for instruction data size", async () => { - const data = anchor.web3.Keypair.generate(); - const dataArray = [99, ...new Array(9).fill(0)]; - const tx = await program.rpc.testConstIxDataSize(dataArray, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataConstArraySize.createInstruction(data), - ], - }); - const dataAccount = await program.account.dataConstArraySize.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, dataArray); - }); - - it("Should include BASE const in IDL", async () => { - assert.isDefined( - miscIdl.constants.find( - (c) => c.name === "BASE" && c.type === "u128" && c.value === "1_000_000" - ) - ); - }); - - it("Should include DECIMALS const in IDL", async () => { - assert.isDefined( - miscIdl.constants.find( - (c) => c.name === "DECIMALS" && c.type === "u8" && c.value === "6" - ) - ); - }); - - it("Should not include NO_IDL const in IDL", async () => { - assert.isUndefined(miscIdl.constants.find((c) => c.name === "NO_IDL")); - }); - - it("init_if_needed throws if account exists but is not owned by the expected program", async () => { - const newAcc = await anchor.web3.PublicKey.findProgramAddress( - [utf8.encode("hello")], - program.programId - ); - await program.rpc.testInitIfNeededChecksOwner({ - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - owner: program.programId, - }, - }); - - try { - await program.rpc.testInitIfNeededChecksOwner({ - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - owner: anchor.web3.Keypair.generate().publicKey, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2004); - } - }); - - it("init_if_needed throws if pda account exists but does not have the expected seeds", async () => { - const newAcc = await anchor.web3.PublicKey.findProgramAddress( - [utf8.encode("nothello")], - program.programId - ); - await program.rpc.testInitIfNeededChecksSeeds("nothello", { - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - }); - - // this will throw if it is not a proper PDA - // we need this so we know that the following tx failed - // not because it couldn't create this pda - // but because the two pdas were different - anchor.web3.PublicKey.createProgramAddress( - [utf8.encode("hello")], - program.programId - ); - - try { - await program.rpc.testInitIfNeededChecksSeeds("hello", { - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - }); - - it("init_if_needed throws if account exists but is not the expected space", async () => { - const newAcc = anchor.web3.Keypair.generate(); - const _irrelevantForTest = 3; - await program.rpc.initWithSpace(_irrelevantForTest, { - accounts: { - data: newAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - signers: [newAcc], - }); - - try { - await program.rpc.testInitIfNeeded(_irrelevantForTest, { - accounts: { - data: newAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - signers: [newAcc], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2019); - } - }); - - it("init_if_needed throws if mint exists but has the wrong mint authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - try { - await program.rpc.testInitMintIfNeeded(6, { - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - mintAuthority: anchor.web3.Keypair.generate().publicKey, - freezeAuthority: provider.wallet.publicKey, - }, - signers: [mint], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2016); - } - }); - - it("init_if_needed throws if mint exists but has the wrong freeze authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - try { - await program.rpc.testInitMintIfNeeded(6, { - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: anchor.web3.Keypair.generate().publicKey, - }, - signers: [mint], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2017); - } - }); - - it("init_if_needed throws if mint exists but has the wrong decimals", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - try { - await program.rpc.testInitMintIfNeeded(9, { - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, - }, - signers: [mint], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2018); - } - }); - - it("init_if_needed throws if token exists but has the wrong owner", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - - try { - await program.rpc.testInitTokenIfNeeded({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - authority: anchor.web3.Keypair.generate().publicKey, - }, - signers: [token], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - } - }); - - it("init_if_needed throws if token exists but has the wrong mint", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const mint2 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint2.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint2], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - - try { - await program.rpc.testInitTokenIfNeeded({ - accounts: { - token: token.publicKey, - mint: mint2.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - authority: provider.wallet.publicKey, - }, - signers: [token], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2014); - } - }); - - it("init_if_needed throws if associated token exists but has the wrong owner", async () => { - const mint = Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, - }); - - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: anchor.web3.Keypair.generate().publicKey, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - } - }); - - it("init_if_needed throws if associated token exists but has the wrong mint", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const mint2 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint2.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint2], - }); - - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, - }); - - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ - accounts: { - token: associatedToken, - mint: mint2.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: provider.wallet.publicKey, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2014); - } - }); - - it("init_if_needed throws if token exists with correct owner and mint but is not the ATA", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: provider.wallet.publicKey, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 3014); - } - }); - - it("Can use multidimensional array", async () => { - const array2d = new Array(10).fill(new Array(10).fill(99)); - const data = anchor.web3.Keypair.generate(); - await program.rpc.testMultidimensionalArray(array2d, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataMultidimensionalArray.createInstruction(data), - ], - }); - const dataAccount = await program.account.dataMultidimensionalArray.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, array2d); - }); - - it("Can use multidimensional array with const sizes", async () => { - const array2d = new Array(10).fill(new Array(11).fill(22)); - const data = anchor.web3.Keypair.generate(); - await program.rpc.testMultidimensionalArrayConstSizes(array2d, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataMultidimensionalArrayConstSizes.createInstruction( - data - ), - ], - }); - const dataAccount = - await program.account.dataMultidimensionalArrayConstSizes.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, array2d); - }); - - describe("Can validate PDAs derived from other program ids", () => { - it("With bumps using create_program_address", async () => { - const [firstPDA, firstBump] = - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - ASSOCIATED_TOKEN_PROGRAM_ID - ); - const [secondPDA, secondBump] = - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - program.programId - ); - - // correct bump but wrong address - const wrongAddress = anchor.web3.Keypair.generate().publicKey; - try { - await program.rpc.testProgramIdConstraint(firstBump, secondBump, { - accounts: { - first: wrongAddress, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // matching bump seed for wrong address but derived from wrong program - try { - await program.rpc.testProgramIdConstraint(secondBump, secondBump, { - accounts: { - first: secondPDA, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // correct inputs should lead to successful tx - await program.rpc.testProgramIdConstraint(firstBump, secondBump, { - accounts: { - first: firstPDA, - second: secondPDA, - }, - }); - }); - - it("With bumps using find_program_address", async () => { - const firstPDA = ( - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - ASSOCIATED_TOKEN_PROGRAM_ID - ) - )[0]; - const secondPDA = ( - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - program.programId - ) - )[0]; - - // random wrong address - const wrongAddress = anchor.web3.Keypair.generate().publicKey; - try { - await program.rpc.testProgramIdConstraintFindPda({ - accounts: { - first: wrongAddress, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // same seeds but derived from wrong program - try { - await program.rpc.testProgramIdConstraintFindPda({ - accounts: { - first: secondPDA, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // correct inputs should lead to successful tx - await program.rpc.testProgramIdConstraintFindPda({ - accounts: { - first: firstPDA, - second: secondPDA, - }, - }); - }); - }); - describe("Token Constraint Test", () => { - it("Token Constraint Test(no init) - Can make token::mint and token::authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - await program.rpc.testTokenConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - }, - }); - const mintAccount = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await mintAccount.getAccountInfo(token.publicKey); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Token Constraint Test(no init) - Can make only token::authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - await program.rpc.testOnlyAuthConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - }, - }); - const mintAccount = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await mintAccount.getAccountInfo(token.publicKey); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - }); - - it("Token Constraint Test(no init) - Can make only token::mint", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - await program.rpc.testOnlyMintConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - }, - }); - const mintAccount = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await mintAccount.getAccountInfo(token.publicKey); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Token Constraint Test(no init) - throws if token::mint mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const mint1 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint1.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint1], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - try { - await program.rpc.testTokenConstraint({ - accounts: { - token: token.publicKey, - mint: mint1.publicKey, - payer: provider.wallet.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2014); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenMint"); - } - }); - - it("Token Constraint Test(no init) - throws if token::authority mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - const fakeAuthority = Keypair.generate(); - try { - await program.rpc.testTokenAuthConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - fakeAuthority: fakeAuthority.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); - } - }); - - it("Token Constraint Test(no init) - throws if both token::authority, token::mint mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - const mint1 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint1.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint1], - }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - const fakeAuthority = Keypair.generate(); - try { - await program.rpc.testTokenAuthConstraint({ - accounts: { - token: token.publicKey, - mint: mint1.publicKey, - fakeAuthority: fakeAuthority.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); - } - }); - - it("Mint Constraint Test(no init) - mint::decimals, mint::authority, mint::freeze_authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - await program.rpc.testMintConstraint(6, { - accounts: { - mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue( - mintAccount.mintAuthority.equals(provider.wallet.publicKey) - ); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) - ); - }); - - it("Mint Constraint Test(no init) - throws if mint::decimals mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - const fakeDecimal = 5; - try { - await program.rpc.testMintConstraint(fakeDecimal, { - accounts: { - mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2018); - assert.strictEqual(err.error.errorCode.code, "ConstraintMintDecimals"); - } - }); - - it("Mint Constraint Test(no init) - throws if mint::mint_authority mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const fakeAuthority = Keypair.generate(); - try { - await program.rpc.testMintConstraint(6, { - accounts: { - mint: mint.publicKey, - mintAuthority: fakeAuthority.publicKey, - freezeAuthority: provider.wallet.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2016); - assert.strictEqual( - err.error.errorCode.code, - "ConstraintMintMintAuthority" - ); - } - }); - - it("Mint Constraint Test(no init) - throws if mint::freeze_authority mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const fakeAuthority = Keypair.generate(); - try { - await program.rpc.testMintConstraint(6, { - accounts: { - mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: fakeAuthority.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2017); - assert.strictEqual( - err.error.errorCode.code, - "ConstraintMintFreezeAuthority" - ); - } - }); - - it("Mint Constraint Test(no init) - can write only mint::decimals", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - await program.rpc.testMintOnlyDecimalsConstraint(6, { - accounts: { - mint: mint.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - }); - - it("Mint Constraint Test(no init) - can write only mint::authority and mint::freeze_authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - await program.rpc.testMintOnlyAuthConstraint({ - accounts: { - mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.isTrue( - mintAccount.mintAuthority.equals(provider.wallet.publicKey) - ); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) - ); - }); - - it("Mint Constraint Test(no init) - can write only mint::authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - await program.rpc.testMintOnlyOneAuthConstraint({ - accounts: { - mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.isTrue( - mintAccount.mintAuthority.equals(provider.wallet.publicKey) - ); - }); - - it("Mint Constraint Test(no init) - can write only mint::decimals and mint::freeze_authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - await program.rpc.testMintMissMintAuthConstraint(6, { - accounts: { - mint: mint.publicKey, - freezeAuthority: provider.wallet.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) - ); - }); - it("check versioned transaction is now available", async () => { - let thisTx = new VersionedTransaction( - new Message({ - header: { - numReadonlySignedAccounts: 0, - numReadonlyUnsignedAccounts: 0, - numRequiredSignatures: 0, - }, - accountKeys: [new PublicKey([0]).toString()], - instructions: [{ accounts: [0], data: "", programIdIndex: 0 }], - recentBlockhash: "", - }) - ); - assert.isDefined(thisTx); - }); - }); -}); diff --git a/tests/misc/tests/misc/misc.ts b/tests/misc/tests/misc/misc.ts index 1d95feaf56..fc4626601c 100644 --- a/tests/misc/tests/misc/misc.ts +++ b/tests/misc/tests/misc/misc.ts @@ -22,1321 +22,706 @@ import { ASSOCIATED_TOKEN_PROGRAM_ID, } 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"); const nativeAssert = require("assert"); const miscIdl = require("../../target/idl/misc.json"); -describe("misc", () => { - // Configure the client to use the local cluster. - const provider = anchor.AnchorProvider.env(); - const wallet = provider.wallet as Wallet; - anchor.setProvider(provider); - const program = anchor.workspace.Misc as Program; - 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 miscTest = ( + program: anchor.Program | anchor.Program +) => { + return () => { + // Configure the client to use the local cluster. + 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(); + + it("Can use u128 and i128", async () => { + const tx = await program.rpc.initialize( + new anchor.BN(1234), + new anchor.BN(22), + { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [await program.account.data.createInstruction(data)], + } + ); + const dataAccount = await program.account.data.fetch(data.publicKey); + assert.isTrue(dataAccount.udata.eq(new anchor.BN(1234))); + assert.isTrue(dataAccount.idata.eq(new anchor.BN(22))); + }); + + it("Can use u16", async () => { + const data = anchor.web3.Keypair.generate(); + const tx = await program.rpc.testU16(99, { + accounts: { + myAccount: data.publicKey, + }, + signers: [data], + instructions: [await program.account.dataU16.createInstruction(data)], + }); + const dataAccount = await program.account.dataU16.fetch(data.publicKey); + assert.strictEqual(dataAccount.data, 99); }); - }); - const data = anchor.web3.Keypair.generate(); + it("Can embed programs into genesis from the Anchor.toml", async () => { + const pid = new anchor.web3.PublicKey( + "FtMNMKp9DZHKWUyVAsj3Q5QV8ow4P3fUPP7ZrWEQJzKr" + ); + let accInfo = await anchor.getProvider().connection.getAccountInfo(pid); + assert.isTrue(accInfo.executable); + }); - it("Can use u128 and i128", async () => { - const tx = await program.rpc.initialize( - new anchor.BN(1234), - new anchor.BN(22), - { + it("Can use the owner constraint", async () => { + await program.rpc.testOwner({ accounts: { data: data.publicKey, + misc: program.programId, }, - signers: [data], - instructions: [await program.account.data.createInstruction(data)], - } - ); - const dataAccount = await program.account.data.fetch(data.publicKey); - assert.isTrue(dataAccount.udata.eq(new anchor.BN(1234))); - assert.isTrue(dataAccount.idata.eq(new anchor.BN(22))); - }); + }); - it("Can use u16", async () => { - const data = anchor.web3.Keypair.generate(); - const tx = await program.rpc.testU16(99, { - accounts: { - myAccount: data.publicKey, - }, - signers: [data], - instructions: [await program.account.dataU16.createInstruction(data)], + await nativeAssert.rejects( + async () => { + await program.rpc.testOwner({ + accounts: { + data: provider.wallet.publicKey, + misc: program.programId, + }, + }); + }, + (err) => { + return true; + } + ); }); - const dataAccount = await program.account.dataU16.fetch(data.publicKey); - assert.strictEqual(dataAccount.data, 99); - }); - - it("Can embed programs into genesis from the Anchor.toml", async () => { - const pid = new anchor.web3.PublicKey( - "FtMNMKp9DZHKWUyVAsj3Q5QV8ow4P3fUPP7ZrWEQJzKr" - ); - let accInfo = await anchor.getProvider().connection.getAccountInfo(pid); - assert.isTrue(accInfo.executable); - }); - - it("Can use the owner constraint", async () => { - await program.rpc.testOwner({ - accounts: { - data: data.publicKey, - misc: program.programId, - }, + + it("Can use the executable attribute", async () => { + await program.rpc.testExecutable({ + accounts: { + program: program.programId, + }, + }); + + await nativeAssert.rejects( + async () => { + await program.rpc.testExecutable({ + accounts: { + program: provider.wallet.publicKey, + }, + }); + }, + (err) => { + return true; + } + ); }); - await nativeAssert.rejects( - async () => { - await program.rpc.testOwner({ + 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: { - data: provider.wallet.publicKey, - misc: program.programId, + authority: provider.wallet.publicKey, + cpiState: await misc2Program.state.address(), + misc2Program: misc2Program.programId, }, }); - }, - (err) => { - return true; - } - ); - }); - - it("Can use the executable attribute", async () => { - await program.rpc.testExecutable({ - accounts: { - program: program.programId, - }, - }); - - await nativeAssert.rejects( - async () => { - await program.rpc.testExecutable({ + } catch (e) { + // initialize if it doesn't exist + await misc2Program.state.rpc.new({ accounts: { - program: provider.wallet.publicKey, + authority: provider.wallet.publicKey, }, }); - }, - (err) => { - return true; } - ); - }); - - it("Can CPI to state instructions", async () => { - const oldData = new anchor.BN(0); - 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, - }, + 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 = [ + `Program ${program.programId.toString()} invoke [1]`, + "Program log: Instruction: TestSimulate", + "Program data: NgyCA9omwbMsAAAA", + "Program data: fPhuIELK/k7SBAAA", + "Program data: jvbowsvlmkcJAAAA", + "Program data: zxM5neEnS1kBAgMEBQYHCAkK", + "Program data: g06Ei2GL1gIBAgMEBQYHCAkKCw==", + ]; + + assert.deepStrictEqual(expectedRaw, resp.raw.slice(0, -2)); + assert.strictEqual(resp.events[0].name, "E1"); + assert.strictEqual(resp.events[0].data.data, 44); + assert.strictEqual(resp.events[1].name, "E2"); + assert.strictEqual(resp.events[1].data.data, 1234); + assert.strictEqual(resp.events[2].name, "E3"); + assert.strictEqual(resp.events[2].data.data, 9); + assert.strictEqual(resp.events[3].name, "E5"); + assert.deepStrictEqual( + resp.events[3].data.data, + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ); + assert.strictEqual(resp.events[4].name, "E6"); + assert.deepStrictEqual( + resp.events[4].data.data, + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + ); }); - 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 = [ - "Program 3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh invoke [1]", - "Program log: Instruction: TestSimulate", - "Program data: NgyCA9omwbMsAAAA", - "Program data: fPhuIELK/k7SBAAA", - "Program data: jvbowsvlmkcJAAAA", - "Program data: zxM5neEnS1kBAgMEBQYHCAkK", - "Program data: g06Ei2GL1gIBAgMEBQYHCAkKCw==", - ]; - - assert.deepStrictEqual(expectedRaw, resp.raw.slice(0, -2)); - assert.strictEqual(resp.events[0].name, "E1"); - assert.strictEqual(resp.events[0].data.data, 44); - assert.strictEqual(resp.events[1].name, "E2"); - assert.strictEqual(resp.events[1].data.data, 1234); - assert.strictEqual(resp.events[2].name, "E3"); - assert.strictEqual(resp.events[2].data.data, 9); - assert.strictEqual(resp.events[3].name, "E5"); - assert.deepStrictEqual( - resp.events[3].data.data, - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - ); - assert.strictEqual(resp.events[4].name, "E6"); - assert.deepStrictEqual( - resp.events[4].data.data, - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - ); - }); - - it("Can use enum in idl", async () => { - const resp1 = await program.methods.testInputEnum({ first: {} }).simulate(); - const event1 = resp1.events[0].data as IdlEvents["E7"]; - assert.deepEqual(event1.data.first, {}); - - const resp2 = await program.methods - .testInputEnum({ second: { x: new BN(1), y: new BN(2) } }) - .simulate(); - const event2 = resp2.events[0].data as IdlEvents["E7"]; - assert.isTrue(new BN(1).eq(event2.data.second.x)); - assert.isTrue(new BN(2).eq(event2.data.second.y)); - - const resp3 = await program.methods - .testInputEnum({ - tupleStructTest: [ - { data1: 1, data2: 11, data3: 111, data4: new BN(1111) }, - ], - }) - .simulate(); - const event3 = resp3.events[0].data as IdlEvents["E7"]; - assert.strictEqual(event3.data.tupleStructTest[0].data1, 1); - assert.strictEqual(event3.data.tupleStructTest[0].data2, 11); - assert.strictEqual(event3.data.tupleStructTest[0].data3, 111); - assert.isTrue(event3.data.tupleStructTest[0].data4.eq(new BN(1111))); - - const resp4 = await program.methods - .testInputEnum({ tupleTest: [1, 2, 3, 4] }) - .simulate(); - const event4 = resp4.events[0].data as IdlEvents["E7"]; - assert.strictEqual(event4.data.tupleTest[0], 1); - assert.strictEqual(event4.data.tupleTest[1], 2); - assert.strictEqual(event4.data.tupleTest[2], 3); - assert.strictEqual(event4.data.tupleTest[3], 4); - }); - - let dataI8; - - it("Can use i8 in the idl", async () => { - dataI8 = anchor.web3.Keypair.generate(); - await program.rpc.testI8(-3, { - accounts: { - data: dataI8.publicKey, - }, - instructions: [await program.account.dataI8.createInstruction(dataI8)], - signers: [dataI8], + + it("Can use enum in idl", async () => { + const resp1 = await program.methods + .testInputEnum({ first: {} }) + .simulate(); + const event1 = resp1.events[0].data as IdlEvents< + typeof program.idl + >["E7"]; + assert.deepEqual(event1.data.first, {}); + + const resp2 = await program.methods + .testInputEnum({ second: { x: new BN(1), y: new BN(2) } }) + .simulate(); + const event2 = resp2.events[0].data as IdlEvents< + typeof program.idl + >["E7"]; + assert.isTrue(new BN(1).eq(event2.data.second.x)); + assert.isTrue(new BN(2).eq(event2.data.second.y)); + + const resp3 = await program.methods + .testInputEnum({ + tupleStructTest: [ + { data1: 1, data2: 11, data3: 111, data4: new BN(1111) }, + ], + }) + .simulate(); + const event3 = resp3.events[0].data as IdlEvents< + typeof program.idl + >["E7"]; + assert.strictEqual(event3.data.tupleStructTest[0].data1, 1); + assert.strictEqual(event3.data.tupleStructTest[0].data2, 11); + assert.strictEqual(event3.data.tupleStructTest[0].data3, 111); + assert.isTrue(event3.data.tupleStructTest[0].data4.eq(new BN(1111))); + + const resp4 = await program.methods + .testInputEnum({ tupleTest: [1, 2, 3, 4] }) + .simulate(); + const event4 = resp4.events[0].data as IdlEvents< + typeof program.idl + >["E7"]; + assert.strictEqual(event4.data.tupleTest[0], 1); + assert.strictEqual(event4.data.tupleTest[1], 2); + assert.strictEqual(event4.data.tupleTest[2], 3); + assert.strictEqual(event4.data.tupleTest[3], 4); + }); + + let dataI8; + + it("Can use i8 in the idl", async () => { + dataI8 = anchor.web3.Keypair.generate(); + await program.rpc.testI8(-3, { + accounts: { + data: dataI8.publicKey, + }, + instructions: [await program.account.dataI8.createInstruction(dataI8)], + signers: [dataI8], + }); + const dataAccount = await program.account.dataI8.fetch(dataI8.publicKey); + assert.strictEqual(dataAccount.data, -3); }); - const dataAccount = await program.account.dataI8.fetch(dataI8.publicKey); - assert.strictEqual(dataAccount.data, -3); - }); - let dataPubkey; + let dataPubkey; - it("Can use i16 in the idl", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testI16(-2048, { - accounts: { - data: data.publicKey, - }, - instructions: [await program.account.dataI16.createInstruction(data)], - signers: [data], - }); - const dataAccount = await program.account.dataI16.fetch(data.publicKey); - assert.strictEqual(dataAccount.data, -2048); - - dataPubkey = data.publicKey; - }); - - it("Can use base58 strings to fetch an account", async () => { - const dataAccount = await program.account.dataI16.fetch( - dataPubkey.toString() - ); - assert.strictEqual(dataAccount.data, -2048); - }); - - it("Should fail to close an account when sending lamports to itself", async () => { - try { - await program.rpc.testClose({ + it("Can use i16 in the idl", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testI16(-2048, { accounts: { data: data.publicKey, - solDest: data.publicKey, }, + instructions: [await program.account.dataI16.createInstruction(data)], + signers: [data], }); - expect(false).to.be.true; - } catch (err) { - const errMsg = "A close constraint was violated"; - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number, 2011); - } - }); - - it("Can close an account", async () => { - const connection = program.provider.connection; - const openAccount = await connection.getAccountInfo(data.publicKey); - - assert.isNotNull(openAccount); - const openAccountBalance = openAccount.lamports; - // double balance to calculate closed balance correctly - const transferIx = anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: data.publicKey, - lamports: openAccountBalance, + const dataAccount = await program.account.dataI16.fetch(data.publicKey); + assert.strictEqual(dataAccount.data, -2048); + + dataPubkey = data.publicKey; }); - const transferTransaction = new anchor.web3.Transaction().add(transferIx); - await provider.sendAndConfirm(transferTransaction); - let beforeBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; + it("Can use base58 strings to fetch an account", async () => { + const dataAccount = await program.account.dataI16.fetch( + dataPubkey.toString() + ); + assert.strictEqual(dataAccount.data, -2048); + }); - await program.methods - .testClose() - .accounts({ - data: data.publicKey, - solDest: provider.wallet.publicKey, - }) - .postInstructions([transferIx]) - .rpc(); + it("Should fail to close an account when sending lamports to itself", async () => { + try { + await program.rpc.testClose({ + accounts: { + data: data.publicKey, + solDest: data.publicKey, + }, + }); + expect(false).to.be.true; + } catch (err) { + const errMsg = "A close constraint was violated"; + assert.strictEqual(err.error.errorMessage, errMsg); + assert.strictEqual(err.error.errorCode.number, 2011); + } + }); - let afterBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; + it("Can close an account", async () => { + const connection = program.provider.connection; + const openAccount = await connection.getAccountInfo(data.publicKey); - // Retrieved rent exemption sol. - expect(afterBalance > beforeBalance).to.be.true; + assert.isNotNull(openAccount); + const openAccountBalance = openAccount.lamports; + // double balance to calculate closed balance correctly + const transferIx = anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: data.publicKey, + lamports: openAccountBalance, + }); + const transferTransaction = new anchor.web3.Transaction().add(transferIx); + await provider.sendAndConfirm(transferTransaction); - const closedAccount = await connection.getAccountInfo(data.publicKey); + let beforeBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; - assert.isTrue(closedAccount.data.length === 0); - assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); - }); + await program.methods + .testClose() + .accounts({ + data: data.publicKey, + solDest: provider.wallet.publicKey, + }) + .postInstructions([transferIx]) + .rpc(); - it("Can close an account twice", async () => { - const data = anchor.web3.Keypair.generate(); - await program.methods - .initialize(new anchor.BN(10), new anchor.BN(10)) - .accounts({ data: data.publicKey }) - .preInstructions([await program.account.data.createInstruction(data)]) - .signers([data]) - .rpc(); - - const connection = program.provider.connection; - const openAccount = await connection.getAccountInfo(data.publicKey); - assert.isNotNull(openAccount); - - const openAccountBalance = openAccount.lamports; - // double balance to calculate closed balance correctly - const transferIx = anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: data.publicKey, - lamports: openAccountBalance, - }); - const transferTransaction = new anchor.web3.Transaction().add(transferIx); - await provider.sendAndConfirm(transferTransaction); - - let beforeBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; - - await program.methods - .testCloseTwice() - .accounts({ - data: data.publicKey, - solDest: provider.wallet.publicKey, - }) - .postInstructions([transferIx]) - .rpc(); - - let afterBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; - - // Retrieved rent exemption sol. - expect(afterBalance > beforeBalance).to.be.true; - - const closedAccount = await connection.getAccountInfo(data.publicKey); - assert.isTrue(closedAccount.data.length === 0); - assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); - }); - - it("Can close a mut account manually", async () => { - const data = anchor.web3.Keypair.generate(); - await program.methods - .initialize(new anchor.BN(10), new anchor.BN(10)) - .accounts({ data: data.publicKey }) - .preInstructions([await program.account.data.createInstruction(data)]) - .signers([data]) - .rpc(); - - const connection = program.provider.connection; - const openAccount = await connection.getAccountInfo(data.publicKey); - - assert.isNotNull(openAccount); - const openAccountBalance = openAccount.lamports; - // double balance to calculate closed balance correctly - const transferIx = anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: data.publicKey, - lamports: openAccountBalance, - }); - const transferTransaction = new anchor.web3.Transaction().add(transferIx); - await provider.sendAndConfirm(transferTransaction); - - let beforeBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; - - await program.methods - .testCloseMut() - .accounts({ - data: data.publicKey, - solDest: provider.wallet.publicKey, - }) - .postInstructions([transferIx]) - .rpc(); - - let afterBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; - - // Retrieved rent exemption sol. - expect(afterBalance > beforeBalance).to.be.true; - - const closedAccount = await connection.getAccountInfo(data.publicKey); - assert.isTrue(closedAccount.data.length === 0); - assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); - }); - - it("Can use instruction data in accounts constraints", async () => { - // b"my-seed" - const seed = Buffer.from([109, 121, 45, 115, 101, 101, 100]); - const [myPda, nonce] = await PublicKey.findProgramAddress( - [seed, anchor.web3.SYSVAR_RENT_PUBKEY.toBuffer()], - program.programId - ); - - await program.rpc.testInstructionConstraint(nonce, { - accounts: { - myPda, - myAccount: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - }); - }); - - it("Can create a PDA account with instruction data", async () => { - const seed = Buffer.from([1, 2, 3, 4]); - const domain = "my-domain"; - const foo = anchor.web3.SYSVAR_RENT_PUBKEY; - const [myPda, nonce] = await PublicKey.findProgramAddress( - [ - Buffer.from(anchor.utils.bytes.utf8.encode("my-seed")), - Buffer.from(anchor.utils.bytes.utf8.encode(domain)), - foo.toBuffer(), - seed, - ], - program.programId - ); - - await program.rpc.testPdaInit(domain, seed, nonce, { - accounts: { - myPda, - myPayer: provider.wallet.publicKey, - foo, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); + let afterBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; - const myPdaAccount = await program.account.dataU16.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 6); - }); - - it("Can create a zero copy PDA account", async () => { - const [myPda, nonce] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], - program.programId - ); - await program.rpc.testPdaInitZeroCopy({ - accounts: { - myPda, - myPayer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); + // Retrieved rent exemption sol. + expect(afterBalance > beforeBalance).to.be.true; - const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 9); - assert.strictEqual(myPdaAccount.bump, nonce); - }); - - it("Can write to a zero copy PDA account", async () => { - const [myPda, bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], - program.programId - ); - await program.rpc.testPdaMutZeroCopy({ - accounts: { - myPda, - myPayer: provider.wallet.publicKey, - }, - }); + const closedAccount = await connection.getAccountInfo(data.publicKey); - const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 1234); - assert.strictEqual(myPdaAccount.bump, bump); - }); - - it("Can create a token account from seeds pda", async () => { - const [mint, mint_bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-mint-seed"))], - program.programId - ); - const [myPda, token_bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-token-seed"))], - program.programId - ); - await program.rpc.testTokenSeedsInit({ - accounts: { - myPda, - mint, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - tokenProgram: TOKEN_PROGRAM_ID, - }, + assert.isTrue(closedAccount.data.length === 0); + assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); }); - const mintAccount = new Token( - program.provider.connection, - mint, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await mintAccount.getAccountInfo(myPda); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint)); - }); - - it("Can execute a fallback function", async () => { - await nativeAssert.rejects( - async () => { - await anchor.utils.rpc.invoke(program.programId); - }, - (err) => { - assert.isTrue(err.toString().includes("custom program error: 0x4d2")); - return true; - } - ); - }); + it("Can close an account twice", async () => { + const data = anchor.web3.Keypair.generate(); + await program.methods + .initialize(new anchor.BN(10), new anchor.BN(10)) + .accounts({ data: data.publicKey }) + .preInstructions([await program.account.data.createInstruction(data)]) + .signers([data]) + .rpc(); - it("Can init a random account", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testInit({ - accounts: { - data: data.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - }); + const connection = program.provider.connection; + const openAccount = await connection.getAccountInfo(data.publicKey); + assert.isNotNull(openAccount); - const account = await program.account.dataI8.fetch(data.publicKey); - assert.strictEqual(account.data, 3); - }); + const openAccountBalance = openAccount.lamports; + // double balance to calculate closed balance correctly + const transferIx = anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: data.publicKey, + lamports: openAccountBalance, + }); + const transferTransaction = new anchor.web3.Transaction().add(transferIx); + await provider.sendAndConfirm(transferTransaction); - it("Can init a random account prefunded", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testInit({ - accounts: { - data: data.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: data.publicKey, - lamports: 4039280, - }), - ], - }); + let beforeBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; - const account = await program.account.dataI8.fetch(data.publicKey); - assert.strictEqual(account.data, 3); - }); + await program.methods + .testCloseTwice() + .accounts({ + data: data.publicKey, + solDest: provider.wallet.publicKey, + }) + .postInstructions([transferIx]) + .rpc(); + + let afterBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; + + // Retrieved rent exemption sol. + expect(afterBalance > beforeBalance).to.be.true; + + const closedAccount = await connection.getAccountInfo(data.publicKey); + assert.isTrue(closedAccount.data.length === 0); + assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); + }); + + it("Can close a mut account manually", async () => { + const data = anchor.web3.Keypair.generate(); + await program.methods + .initialize(new anchor.BN(10), new anchor.BN(10)) + .accounts({ data: data.publicKey }) + .preInstructions([await program.account.data.createInstruction(data)]) + .signers([data]) + .rpc(); + + const connection = program.provider.connection; + const openAccount = await connection.getAccountInfo(data.publicKey); + + assert.isNotNull(openAccount); + const openAccountBalance = openAccount.lamports; + // double balance to calculate closed balance correctly + const transferIx = anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: data.publicKey, + lamports: openAccountBalance, + }); + const transferTransaction = new anchor.web3.Transaction().add(transferIx); + await provider.sendAndConfirm(transferTransaction); - it("Can init a random zero copy account", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testInitZeroCopy({ - accounts: { - data: data.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - }); - const account = await program.account.dataZeroCopy.fetch(data.publicKey); - assert.strictEqual(account.data, 10); - assert.strictEqual(account.bump, 2); - }); - - let mint = undefined; - - it("Can create a random mint account", async () => { - mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue(mintAccount.mintAuthority.equals(provider.wallet.publicKey)); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) - ); - }); - - it("Can create a random mint account prefunded", async () => { - mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: mint.publicKey, - lamports: 4039280, - }), - ], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue(mintAccount.mintAuthority.equals(provider.wallet.publicKey)); - }); - - it("Can create a random token account", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can create a random token with prefunding", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: token.publicKey, - lamports: 4039280, - }), - ], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can create a random token with prefunding under the rent exemption", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: token.publicKey, - lamports: 1, - }), - ], + let beforeBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; + + await program.methods + .testCloseMut() + .accounts({ + data: data.publicKey, + solDest: provider.wallet.publicKey, + }) + .postInstructions([transferIx]) + .rpc(); + + let afterBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; + + // Retrieved rent exemption sol. + expect(afterBalance > beforeBalance).to.be.true; + + const closedAccount = await connection.getAccountInfo(data.publicKey); + assert.isTrue(closedAccount.data.length === 0); + assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can initialize multiple accounts via a composite payer", async () => { - const data1 = anchor.web3.Keypair.generate(); - const data2 = anchor.web3.Keypair.generate(); - - const tx = await program.rpc.testCompositePayer({ - accounts: { - composite: { - data: data1.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, + + it("Can use instruction data in accounts constraints", async () => { + // b"my-seed" + const seed = Buffer.from([109, 121, 45, 115, 101, 101, 100]); + const [myPda, nonce] = await PublicKey.findProgramAddress( + [seed, anchor.web3.SYSVAR_RENT_PUBKEY.toBuffer()], + program.programId + ); + + await program.rpc.testInstructionConstraint(nonce, { + accounts: { + myPda, + myAccount: anchor.web3.SYSVAR_RENT_PUBKEY, }, - data: data2.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data1, data2], + }); }); - const account1 = await program.account.dataI8.fetch(data1.publicKey); - assert.strictEqual(account1.data, 1); - - const account2 = await program.account.data.fetch(data2.publicKey); - assert.strictEqual(account2.udata.toNumber(), 2); - assert.strictEqual(account2.idata.toNumber(), 3); - }); - - describe("associated_token constraints", () => { - let associatedToken = null; - // apparently cannot await here so doing it in the 'it' statements - let client = Token.createMint( - program.provider.connection, - wallet.payer, - provider.wallet.publicKey, - provider.wallet.publicKey, - 9, - TOKEN_PROGRAM_ID - ); - - it("Can create an associated token account", async () => { - const localClient = await client; - associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - localClient.publicKey, - provider.wallet.publicKey + it("Can create a PDA account with instruction data", async () => { + const seed = Buffer.from([1, 2, 3, 4]); + const domain = "my-domain"; + const foo = anchor.web3.SYSVAR_RENT_PUBKEY; + const [myPda, nonce] = await PublicKey.findProgramAddress( + [ + Buffer.from(anchor.utils.bytes.utf8.encode("my-seed")), + Buffer.from(anchor.utils.bytes.utf8.encode(domain)), + foo.toBuffer(), + seed, + ], + program.programId ); - await program.rpc.testInitAssociatedToken({ + await program.rpc.testPdaInit(domain, seed, nonce, { accounts: { - token: associatedToken, - mint: localClient.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, + myPda, + myPayer: provider.wallet.publicKey, + foo, systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, }, }); - const account = await localClient.getAccountInfo(associatedToken); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(localClient.publicKey)); + const myPdaAccount = await program.account.dataU16.fetch(myPda); + assert.strictEqual(myPdaAccount.data, 6); }); - it("Can validate associated_token constraints", async () => { - const localClient = await client; - await program.rpc.testValidateAssociatedToken({ + it("Can create a zero copy PDA account", async () => { + const [myPda, nonce] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], + program.programId + ); + await program.rpc.testPdaInitZeroCopy({ accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: provider.wallet.publicKey, + myPda, + myPayer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, }, }); - let otherMint = await Token.createMint( - program.provider.connection, - wallet.payer, - provider.wallet.publicKey, - provider.wallet.publicKey, - 9, - TOKEN_PROGRAM_ID - ); + const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); + assert.strictEqual(myPdaAccount.data, 9); + assert.strictEqual(myPdaAccount.bump, nonce); + }); - await nativeAssert.rejects( - async () => { - await program.rpc.testValidateAssociatedToken({ - accounts: { - token: associatedToken, - mint: otherMint.publicKey, - wallet: provider.wallet.publicKey, - }, - }); - }, - (err) => { - assert.strictEqual(err.error.errorCode.number, 2009); - return true; - } + it("Can write to a zero copy PDA account", async () => { + const [myPda, bump] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], + program.programId ); + await program.rpc.testPdaMutZeroCopy({ + accounts: { + myPda, + myPayer: provider.wallet.publicKey, + }, + }); + + const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); + assert.strictEqual(myPdaAccount.data, 1234); + assert.strictEqual(myPdaAccount.bump, bump); }); - it("associated_token constraints check do not allow authority change", async () => { - const localClient = await client; - await program.rpc.testValidateAssociatedToken({ + it("Can create a token account from seeds pda", async () => { + const [mint, mint_bump] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-mint-seed"))], + program.programId + ); + const [myPda, token_bump] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-token-seed"))], + program.programId + ); + await program.rpc.testTokenSeedsInit({ accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: provider.wallet.publicKey, + myPda, + mint, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + tokenProgram: TOKEN_PROGRAM_ID, }, }); - await localClient.setAuthority( - associatedToken, - anchor.web3.Keypair.generate().publicKey, - "AccountOwner", - wallet.payer, - [] + const mintAccount = new Token( + program.provider.connection, + mint, + TOKEN_PROGRAM_ID, + wallet.payer ); + const account = await mintAccount.getAccountInfo(myPda); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint)); + }); + it("Can execute a fallback function", async () => { await nativeAssert.rejects( async () => { - await program.rpc.testValidateAssociatedToken({ - accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: provider.wallet.publicKey, - }, - }); + await anchor.utils.rpc.invoke(program.programId); }, (err) => { - assert.strictEqual(err.error.errorCode.number, 2015); + assert.isTrue(err.toString().includes("custom program error: 0x4d2")); return true; } ); }); - }); - - it("Can fetch all accounts of a given type", async () => { - // Initialize the accounts. - const data1 = anchor.web3.Keypair.generate(); - const data2 = anchor.web3.Keypair.generate(); - const data3 = anchor.web3.Keypair.generate(); - const data4 = anchor.web3.Keypair.generate(); - // Initialize filterable data. - const filterable1 = anchor.web3.Keypair.generate().publicKey; - const filterable2 = anchor.web3.Keypair.generate().publicKey; - // Set up a secondary wallet and program. - const anotherProvider = new anchor.AnchorProvider( - program.provider.connection, - new anchor.Wallet(anchor.web3.Keypair.generate()), - { commitment: program.provider.connection.commitment } - ); - const anotherProgram = new anchor.Program( - miscIdl, - program.programId, - anotherProvider - ); - // Request airdrop for secondary wallet. - const signature = await program.provider.connection.requestAirdrop( - anotherProvider.wallet.publicKey, - anchor.web3.LAMPORTS_PER_SOL - ); - await program.provider.connection.confirmTransaction(signature); - // Create all the accounts. - await Promise.all([ - program.rpc.testFetchAll(filterable1, { - accounts: { - data: data1.publicKey, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data1], - }), - program.rpc.testFetchAll(filterable1, { - accounts: { - data: data2.publicKey, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data2], - }), - program.rpc.testFetchAll(filterable2, { - accounts: { - data: data3.publicKey, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data3], - }), - anotherProgram.rpc.testFetchAll(filterable1, { - accounts: { - data: data4.publicKey, - authority: anotherProvider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data4], - }), - ]); - // Call for multiple kinds of .all. - const allAccounts = await program.account.dataWithFilter.all(); - const allAccountsFilteredByBuffer = - await program.account.dataWithFilter.all( - provider.wallet.publicKey.toBuffer() - ); - const allAccountsFilteredByProgramFilters1 = - await program.account.dataWithFilter.all([ - { - memcmp: { - offset: 8, - bytes: provider.wallet.publicKey.toBase58(), - }, - }, - { memcmp: { offset: 40, bytes: filterable1.toBase58() } }, - ]); - const allAccountsFilteredByProgramFilters2 = - await program.account.dataWithFilter.all([ - { - memcmp: { - offset: 8, - bytes: provider.wallet.publicKey.toBase58(), - }, - }, - { memcmp: { offset: 40, bytes: filterable2.toBase58() } }, - ]); - // Without filters there should be 4 accounts. - assert.lengthOf(allAccounts, 4); - // Filtering by main wallet there should be 3 accounts. - assert.lengthOf(allAccountsFilteredByBuffer, 3); - // Filtering all the main wallet accounts and matching the filterable1 value - // results in a 2 accounts. - assert.lengthOf(allAccountsFilteredByProgramFilters1, 2); - // Filtering all the main wallet accounts and matching the filterable2 value - // results in 1 account. - assert.lengthOf(allAccountsFilteredByProgramFilters2, 1); - }); - - it("Can use pdas with empty seeds", async () => { - const [pda, bump] = await PublicKey.findProgramAddress( - [], - program.programId - ); - - await program.rpc.testInitWithEmptySeeds({ - accounts: { - pda: pda, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); - await program.rpc.testEmptySeedsConstraint({ - accounts: { - pda: pda, - }, - }); - - const [pda2] = await PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("non-empty")], - program.programId - ); - await nativeAssert.rejects( - program.rpc.testEmptySeedsConstraint({ - accounts: { - pda: pda2, - }, - }), - (err) => { - assert.equal(err.error.errorCode.number, 2006); - return true; - } - ); - }); - - const ifNeededAcc = anchor.web3.Keypair.generate(); - - it("Can init if needed a new account", async () => { - await program.rpc.testInitIfNeeded(1, { - accounts: { - data: ifNeededAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - signers: [ifNeededAcc], - }); - const account = await program.account.dataU16.fetch(ifNeededAcc.publicKey); - assert.strictEqual(account.data, 1); - }); - - it("Can init if needed a previously created account", async () => { - await program.rpc.testInitIfNeeded(3, { - accounts: { - data: ifNeededAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - signers: [ifNeededAcc], - }); - const account = await program.account.dataU16.fetch(ifNeededAcc.publicKey); - assert.strictEqual(account.data, 3); - }); - - it("Can use const for array size", async () => { - const data = anchor.web3.Keypair.generate(); - const tx = await program.rpc.testConstArraySize(99, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataConstArraySize.createInstruction(data), - ], - }); - const dataAccount = await program.account.dataConstArraySize.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, [99, ...new Array(9).fill(0)]); - }); - - it("Can use const for instruction data size", async () => { - const data = anchor.web3.Keypair.generate(); - const dataArray = [99, ...new Array(9).fill(0)]; - const tx = await program.rpc.testConstIxDataSize(dataArray, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataConstArraySize.createInstruction(data), - ], - }); - const dataAccount = await program.account.dataConstArraySize.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, dataArray); - }); - - it("Should include BASE const in IDL", async () => { - assert.isDefined( - miscIdl.constants.find( - (c) => c.name === "BASE" && c.type === "u128" && c.value === "1_000_000" - ) - ); - }); - - it("Should include DECIMALS const in IDL", async () => { - assert.isDefined( - miscIdl.constants.find( - (c) => c.name === "DECIMALS" && c.type === "u8" && c.value === "6" - ) - ); - }); - - it("Should not include NO_IDL const in IDL", async () => { - assert.isUndefined(miscIdl.constants.find((c) => c.name === "NO_IDL")); - }); - - it("init_if_needed throws if account exists but is not owned by the expected program", async () => { - const newAcc = await anchor.web3.PublicKey.findProgramAddress( - [utf8.encode("hello")], - program.programId - ); - await program.rpc.testInitIfNeededChecksOwner({ - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - owner: program.programId, - }, - }); - try { - await program.rpc.testInitIfNeededChecksOwner({ + it("Can init a random account", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testInit({ accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, + data: data.publicKey, payer: provider.wallet.publicKey, - owner: anchor.web3.Keypair.generate().publicKey, + systemProgram: anchor.web3.SystemProgram.programId, }, + signers: [data], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2004); - } - }); - - it("init_if_needed throws if pda account exists but does not have the expected seeds", async () => { - const newAcc = await anchor.web3.PublicKey.findProgramAddress( - [utf8.encode("nothello")], - program.programId - ); - await program.rpc.testInitIfNeededChecksSeeds("nothello", { - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, + + const account = await program.account.dataI8.fetch(data.publicKey); + assert.strictEqual(account.data, 3); }); - // this will throw if it is not a proper PDA - // we need this so we know that the following tx failed - // not because it couldn't create this pda - // but because the two pdas were different - anchor.web3.PublicKey.createProgramAddress( - [utf8.encode("hello")], - program.programId - ); - - try { - await program.rpc.testInitIfNeededChecksSeeds("hello", { + it("Can init a random account prefunded", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testInit({ accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, + data: data.publicKey, payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, }, + signers: [data], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: data.publicKey, + lamports: 4039280, + }), + ], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - }); - - it("init_if_needed throws if account exists but is not the expected space", async () => { - const newAcc = anchor.web3.Keypair.generate(); - const _irrelevantForTest = 3; - await program.rpc.initWithSpace(_irrelevantForTest, { - accounts: { - data: newAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - signers: [newAcc], + + const account = await program.account.dataI8.fetch(data.publicKey); + assert.strictEqual(account.data, 3); }); - try { - await program.rpc.testInitIfNeeded(_irrelevantForTest, { + it("Can init a random zero copy account", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testInitZeroCopy({ accounts: { - data: newAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, + data: data.publicKey, payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, }, - signers: [newAcc], + signers: [data], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2019); - } - }); - - it("init_if_needed throws if mint exists but has the wrong mint authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], + const account = await program.account.dataZeroCopy.fetch(data.publicKey); + assert.strictEqual(account.data, 10); + assert.strictEqual(account.bump, 2); }); - try { - await program.rpc.testInitMintIfNeeded(6, { + let mint = undefined; + + it("Can create a random mint account", async () => { + mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ accounts: { mint: mint.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, rent: anchor.web3.SYSVAR_RENT_PUBKEY, - mintAuthority: anchor.web3.Keypair.generate().publicKey, - freezeAuthority: provider.wallet.publicKey, }, signers: [mint], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2016); - } - }); - - it("init_if_needed throws if mint exists but has the wrong freeze authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - try { - await program.rpc.testInitMintIfNeeded(6, { - accounts: { - mint: mint.publicKey, + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + ); + }); + + it("Can create a random mint account prefunded", async () => { + mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, rent: anchor.web3.SYSVAR_RENT_PUBKEY, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: anchor.web3.Keypair.generate().publicKey, }, signers: [mint], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: mint.publicKey, + lamports: 4039280, + }), + ], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2017); - } - }); - - it("init_if_needed throws if mint exists but has the wrong decimals", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); }); - try { - await program.rpc.testInitMintIfNeeded(9, { + it("Can create a random token account", async () => { + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ accounts: { + token: token.publicKey, mint: mint.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, rent: anchor.web3.SYSVAR_RENT_PUBKEY, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, }, - signers: [mint], + signers: [token], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2018); - } - }); - - it("init_if_needed throws if token exists but has the wrong owner", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await client.getAccountInfo(token.publicKey); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); }); - try { - await program.rpc.testInitTokenIfNeeded({ + it("Can create a random token with prefunding", async () => { + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ accounts: { token: token.publicKey, mint: mint.publicKey, @@ -1344,335 +729,488 @@ describe("misc", () => { systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, rent: anchor.web3.SYSVAR_RENT_PUBKEY, - authority: anchor.web3.Keypair.generate().publicKey, }, signers: [token], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: token.publicKey, + lamports: 4039280, + }), + ], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - } - }); - - it("init_if_needed throws if token exists but has the wrong mint", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const mint2 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint2.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint2], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await client.getAccountInfo(token.publicKey); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); }); - try { - await program.rpc.testInitTokenIfNeeded({ + it("Can create a random token with prefunding under the rent exemption", async () => { + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ accounts: { token: token.publicKey, - mint: mint2.publicKey, + mint: mint.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, rent: anchor.web3.SYSVAR_RENT_PUBKEY, - authority: provider.wallet.publicKey, }, signers: [token], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: token.publicKey, + lamports: 1, + }), + ], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2014); - } - }); - - it("init_if_needed throws if associated token exists but has the wrong owner", async () => { - const mint = Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: SYSVAR_RENT_PUBKEY, - }, - signers: [mint], + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await client.getAccountInfo(token.publicKey); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); + }); + + it("Can initialize multiple accounts via a composite payer", async () => { + const data1 = anchor.web3.Keypair.generate(); + const data2 = anchor.web3.Keypair.generate(); + + const tx = await program.methods + .testCompositePayer() + .accounts({ + composite: { + data: data1.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + data: data2.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }) + .signers([data1, data2]) + .rpc(); + + const account1 = await program.account.dataI8.fetch(data1.publicKey); + assert.strictEqual(account1.data, 1); + + const account2 = await program.account.data.fetch(data2.publicKey); + assert.strictEqual(account2.udata.toNumber(), 2); + assert.strictEqual(account2.idata.toNumber(), 3); }); - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, + describe("associated_token constraints", () => { + let associatedToken = null; + // apparently cannot await here so doing it in the 'it' statements + let client = Token.createMint( + program.provider.connection, + wallet.payer, + provider.wallet.publicKey, + provider.wallet.publicKey, + 9, + TOKEN_PROGRAM_ID + ); + + it("Can create an associated token account", async () => { + const localClient = await client; + associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + localClient.publicKey, + provider.wallet.publicKey + ); + + await program.rpc.testInitAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + }, + }); + + const account = await localClient.getAccountInfo(associatedToken); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(localClient.publicKey)); + }); + + it("Can validate associated_token constraints", async () => { + const localClient = await client; + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + + let otherMint = await Token.createMint( + program.provider.connection, + wallet.payer, + provider.wallet.publicKey, + provider.wallet.publicKey, + 9, + TOKEN_PROGRAM_ID + ); + + await nativeAssert.rejects( + async () => { + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: otherMint.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + }, + (err) => { + assert.strictEqual(err.error.errorCode.number, 2009); + return true; + } + ); + }); + + it("associated_token constraints check do not allow authority change", async () => { + const localClient = await client; + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + + await localClient.setAuthority( + associatedToken, + anchor.web3.Keypair.generate().publicKey, + "AccountOwner", + wallet.payer, + [] + ); + + await nativeAssert.rejects( + async () => { + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + }, + (err) => { + assert.strictEqual(err.error.errorCode.number, 2015); + return true; + } + ); + }); }); - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ + it("Can fetch all accounts of a given type", async () => { + // Initialize the accounts. + const data1 = anchor.web3.Keypair.generate(); + const data2 = anchor.web3.Keypair.generate(); + const data3 = anchor.web3.Keypair.generate(); + const data4 = anchor.web3.Keypair.generate(); + // Initialize filterable data. + const filterable1 = anchor.web3.Keypair.generate().publicKey; + const filterable2 = anchor.web3.Keypair.generate().publicKey; + // Set up a secondary wallet and program. + const anotherProvider = new anchor.AnchorProvider( + program.provider.connection, + new anchor.Wallet(anchor.web3.Keypair.generate()), + { commitment: program.provider.connection.commitment } + ); + const anotherProgram = new anchor.Program( + miscIdl, + program.programId, + anotherProvider + ); + // Request airdrop for secondary wallet. + const signature = await program.provider.connection.requestAirdrop( + anotherProvider.wallet.publicKey, + anchor.web3.LAMPORTS_PER_SOL + ); + await program.provider.connection.confirmTransaction(signature); + // Create all the accounts. + await Promise.all([ + program.rpc.testFetchAll(filterable1, { + accounts: { + data: data1.publicKey, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data1], + }), + program.rpc.testFetchAll(filterable1, { + accounts: { + data: data2.publicKey, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data2], + }), + program.rpc.testFetchAll(filterable2, { + accounts: { + data: data3.publicKey, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data3], + }), + anotherProgram.rpc.testFetchAll(filterable1, { + accounts: { + data: data4.publicKey, + authority: anotherProvider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data4], + }), + ]); + // Call for multiple kinds of .all. + const allAccounts = await program.account.dataWithFilter.all(); + const allAccountsFilteredByBuffer = + await program.account.dataWithFilter.all( + provider.wallet.publicKey.toBuffer() + ); + const allAccountsFilteredByProgramFilters1 = + await program.account.dataWithFilter.all([ + { + memcmp: { + offset: 8, + bytes: provider.wallet.publicKey.toBase58(), + }, + }, + { memcmp: { offset: 40, bytes: filterable1.toBase58() } }, + ]); + const allAccountsFilteredByProgramFilters2 = + await program.account.dataWithFilter.all([ + { + memcmp: { + offset: 8, + bytes: provider.wallet.publicKey.toBase58(), + }, + }, + { memcmp: { offset: 40, bytes: filterable2.toBase58() } }, + ]); + // Without filters there should be 4 accounts. + assert.lengthOf(allAccounts, 4); + // Filtering by main wallet there should be 3 accounts. + assert.lengthOf(allAccountsFilteredByBuffer, 3); + // Filtering all the main wallet accounts and matching the filterable1 value + // results in a 2 accounts. + assert.lengthOf(allAccountsFilteredByProgramFilters1, 2); + // Filtering all the main wallet accounts and matching the filterable2 value + // results in 1 account. + assert.lengthOf(allAccountsFilteredByProgramFilters2, 1); + }); + + it("Can use pdas with empty seeds", async () => { + const [pda, bump] = await PublicKey.findProgramAddress( + [], + program.programId + ); + + await program.rpc.testInitWithEmptySeeds({ accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, + pda: pda, + authority: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: anchor.web3.Keypair.generate().publicKey, }, }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - } - }); - - it("init_if_needed throws if associated token exists but has the wrong mint", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); + await program.rpc.testEmptySeedsConstraint({ + accounts: { + pda: pda, + }, + }); - const mint2 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint2.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint2], + const [pda2] = await PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("non-empty")], + program.programId + ); + await nativeAssert.rejects( + program.rpc.testEmptySeedsConstraint({ + accounts: { + pda: pda2, + }, + }), + (err) => { + assert.equal(err.error.errorCode.number, 2006); + return true; + } + ); }); - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, - }); + const ifNeededAcc = anchor.web3.Keypair.generate(); - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ + it("Can init if needed a new account", async () => { + await program.rpc.testInitIfNeeded(1, { accounts: { - token: associatedToken, - mint: mint2.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, + data: ifNeededAcc.publicKey, systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: provider.wallet.publicKey, + payer: provider.wallet.publicKey, }, + signers: [ifNeededAcc], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2014); - } - }); - - it("init_if_needed throws if token exists with correct owner and mint but is not the ATA", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], + const account = await program.account.dataU16.fetch( + ifNeededAcc.publicKey + ); + assert.strictEqual(account.data, 1); }); - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, + it("Can init if needed a previously created account", async () => { + await program.rpc.testInitIfNeeded(3, { + accounts: { + data: ifNeededAcc.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + }, + signers: [ifNeededAcc], + }); + const account = await program.account.dataU16.fetch( + ifNeededAcc.publicKey + ); + assert.strictEqual(account.data, 3); }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], + it("Can use const for array size", async () => { + const data = anchor.web3.Keypair.generate(); + const tx = await program.rpc.testConstArraySize(99, { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [ + await program.account.dataConstArraySize.createInstruction(data), + ], + }); + const dataAccount = await program.account.dataConstArraySize.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, [99, ...new Array(9).fill(0)]); }); - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ + it("Can use const for instruction data size", async () => { + const data = anchor.web3.Keypair.generate(); + const dataArray = [99, ...new Array(9).fill(0)]; + const tx = await program.rpc.testConstIxDataSize(dataArray, { accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: provider.wallet.publicKey, + data: data.publicKey, }, + signers: [data], + instructions: [ + await program.account.dataConstArraySize.createInstruction(data), + ], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 3014); - } - }); + const dataAccount = await program.account.dataConstArraySize.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, dataArray); + }); - it("Can use multidimensional array", async () => { - const array2d = new Array(10).fill(new Array(10).fill(99)); - const data = anchor.web3.Keypair.generate(); - await program.rpc.testMultidimensionalArray(array2d, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataMultidimensionalArray.createInstruction(data), - ], + it("Should include BASE const in IDL", async () => { + assert.isDefined( + miscIdl.constants.find( + (c) => + c.name === "BASE" && c.type === "u128" && c.value === "1_000_000" + ) + ); }); - const dataAccount = await program.account.dataMultidimensionalArray.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, array2d); - }); - - it("Can use multidimensional array with const sizes", async () => { - const array2d = new Array(10).fill(new Array(11).fill(22)); - const data = anchor.web3.Keypair.generate(); - await program.rpc.testMultidimensionalArrayConstSizes(array2d, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataMultidimensionalArrayConstSizes.createInstruction( - data - ), - ], + + it("Should include DECIMALS const in IDL", async () => { + assert.isDefined( + miscIdl.constants.find( + (c) => c.name === "DECIMALS" && c.type === "u8" && c.value === "6" + ) + ); }); - const dataAccount = - await program.account.dataMultidimensionalArrayConstSizes.fetch( - data.publicKey + + it("Should not include NO_IDL const in IDL", async () => { + assert.isUndefined(miscIdl.constants.find((c) => c.name === "NO_IDL")); + }); + + it("init_if_needed throws if account exists but is not owned by the expected program", async () => { + const newAcc = await anchor.web3.PublicKey.findProgramAddress( + [utf8.encode("hello")], + program.programId ); - assert.deepStrictEqual(dataAccount.data, array2d); - }); - - describe("Can validate PDAs derived from other program ids", () => { - it("With bumps using create_program_address", async () => { - const [firstPDA, firstBump] = - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - ASSOCIATED_TOKEN_PROGRAM_ID - ); - const [secondPDA, secondBump] = - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - program.programId - ); + await program.rpc.testInitIfNeededChecksOwner({ + accounts: { + data: newAcc[0], + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + owner: program.programId, + }, + }); - // correct bump but wrong address - const wrongAddress = anchor.web3.Keypair.generate().publicKey; try { - await program.rpc.testProgramIdConstraint(firstBump, secondBump, { + await program.rpc.testInitIfNeededChecksOwner({ accounts: { - first: wrongAddress, - second: secondPDA, + data: newAcc[0], + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + owner: anchor.web3.Keypair.generate().publicKey, }, }); expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); + assert.strictEqual(err.error.errorCode.number, 2004); } + }); + + it("init_if_needed throws if pda account exists but does not have the expected seeds", async () => { + const newAcc = await anchor.web3.PublicKey.findProgramAddress( + [utf8.encode("nothello")], + program.programId + ); + await program.rpc.testInitIfNeededChecksSeeds("nothello", { + accounts: { + data: newAcc[0], + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + }, + }); + + // this will throw if it is not a proper PDA + // we need this so we know that the following tx failed + // not because it couldn't create this pda + // but because the two pdas were different + anchor.web3.PublicKey.createProgramAddress( + [utf8.encode("hello")], + program.programId + ); - // matching bump seed for wrong address but derived from wrong program try { - await program.rpc.testProgramIdConstraint(secondBump, secondBump, { + await program.rpc.testInitIfNeededChecksSeeds("hello", { accounts: { - first: secondPDA, - second: secondPDA, + data: newAcc[0], + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, }, }); expect(false).to.be.true; @@ -1681,72 +1219,72 @@ describe("misc", () => { const err: AnchorError = _err; assert.strictEqual(err.error.errorCode.number, 2006); } + }); - // correct inputs should lead to successful tx - await program.rpc.testProgramIdConstraint(firstBump, secondBump, { + it("init_if_needed throws if account exists but is not the expected space", async () => { + const newAcc = anchor.web3.Keypair.generate(); + const _irrelevantForTest = 3; + await program.rpc.initWithSpace(_irrelevantForTest, { accounts: { - first: firstPDA, - second: secondPDA, + data: newAcc.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, }, + signers: [newAcc], }); - }); - - it("With bumps using find_program_address", async () => { - const firstPDA = ( - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - ASSOCIATED_TOKEN_PROGRAM_ID - ) - )[0]; - const secondPDA = ( - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - program.programId - ) - )[0]; - // random wrong address - const wrongAddress = anchor.web3.Keypair.generate().publicKey; try { - await program.rpc.testProgramIdConstraintFindPda({ + await program.rpc.testInitIfNeeded(_irrelevantForTest, { accounts: { - first: wrongAddress, - second: secondPDA, + data: newAcc.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, }, + signers: [newAcc], }); expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); + assert.strictEqual(err.error.errorCode.number, 2019); } + }); + + it("init_if_needed throws if mint exists but has the wrong mint authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); - // same seeds but derived from wrong program try { - await program.rpc.testProgramIdConstraintFindPda({ + await program.rpc.testInitMintIfNeeded(6, { accounts: { - first: secondPDA, - second: secondPDA, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + mintAuthority: anchor.web3.Keypair.generate().publicKey, + freezeAuthority: provider.wallet.publicKey, }, + signers: [mint], }); expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); + assert.strictEqual(err.error.errorCode.number, 2016); } - - // correct inputs should lead to successful tx - await program.rpc.testProgramIdConstraintFindPda({ - accounts: { - first: firstPDA, - second: secondPDA, - }, - }); }); - }); - describe("Token Constraint Test", () => { - it("Token Constraint Test(no init) - Can make token::mint and token::authority", async () => { + + it("init_if_needed throws if mint exists but has the wrong freeze authority", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -1759,37 +1297,28 @@ describe("misc", () => { signers: [mint], }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - await program.rpc.testTokenConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - }, - }); - const mintAccount = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await mintAccount.getAccountInfo(token.publicKey); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); + try { + await program.rpc.testInitMintIfNeeded(6, { + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: anchor.web3.Keypair.generate().publicKey, + }, + signers: [mint], + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2017); + } }); - it("Token Constraint Test(no init) - Can make only token::authority", async () => { + it("init_if_needed throws if mint exists but has the wrong decimals", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -1802,36 +1331,28 @@ describe("misc", () => { signers: [mint], }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - await program.rpc.testOnlyAuthConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - }, - }); - const mintAccount = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await mintAccount.getAccountInfo(token.publicKey); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + try { + await program.rpc.testInitMintIfNeeded(9, { + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + signers: [mint], + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2018); + } }); - it("Token Constraint Test(no init) - Can make only token::mint", async () => { + it("init_if_needed throws if token exists but has the wrong owner", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -1856,23 +1377,29 @@ describe("misc", () => { }, signers: [token], }); - await program.rpc.testOnlyMintConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - }, - }); - const mintAccount = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await mintAccount.getAccountInfo(token.publicKey); - assert.isTrue(account.mint.equals(mint.publicKey)); + + try { + await program.rpc.testInitTokenIfNeeded({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + authority: anchor.web3.Keypair.generate().publicKey, + }, + signers: [token], + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); + } }); - it("Token Constraint Test(no init) - throws if token::mint mismatch", async () => { + it("init_if_needed throws if token exists but has the wrong mint", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -1885,16 +1412,16 @@ describe("misc", () => { signers: [mint], }); - const mint1 = anchor.web3.Keypair.generate(); + const mint2 = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { - mint: mint1.publicKey, + mint: mint2.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, - signers: [mint1], + signers: [mint2], }); const token = anchor.web3.Keypair.generate(); @@ -1909,66 +1436,82 @@ describe("misc", () => { }, signers: [token], }); + try { - await program.rpc.testTokenConstraint({ + await program.rpc.testInitTokenIfNeeded({ accounts: { token: token.publicKey, - mint: mint1.publicKey, + mint: mint2.publicKey, payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + authority: provider.wallet.publicKey, }, + signers: [token], }); - assert.isTrue(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; assert.strictEqual(err.error.errorCode.number, 2014); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenMint"); } }); - it("Token Constraint Test(no init) - throws if token::authority mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); + it("init_if_needed throws if associated token exists but has the wrong owner", async () => { + const mint = Keypair.generate(); await program.rpc.testInitMint({ accounts: { mint: mint.publicKey, payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, + systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, + rent: SYSVAR_RENT_PUBKEY, }, signers: [mint], }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ + + const associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + mint.publicKey, + provider.wallet.publicKey + ); + + await program.rpc.testInitAssociatedToken({ accounts: { - token: token.publicKey, + token: associatedToken, mint: mint.publicKey, payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, }, - signers: [token], }); - const fakeAuthority = Keypair.generate(); + try { - await program.rpc.testTokenAuthConstraint({ + await program.rpc.testInitAssociatedTokenIfNeeded({ accounts: { - token: token.publicKey, + token: associatedToken, mint: mint.publicKey, - fakeAuthority: fakeAuthority.publicKey, + payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + authority: anchor.web3.Keypair.generate().publicKey, }, }); - assert.isTrue(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; assert.strictEqual(err.error.errorCode.number, 2015); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); } }); - it("Token Constraint Test(no init) - throws if both token::authority, token::mint mismatch", async () => { + it("init_if_needed throws if associated token exists but has the wrong mint", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -1980,48 +1523,61 @@ describe("misc", () => { }, signers: [mint], }); - const mint1 = anchor.web3.Keypair.generate(); + + const mint2 = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { - mint: mint1.publicKey, + mint: mint2.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, - signers: [mint1], + signers: [mint2], }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ + + const associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + mint.publicKey, + provider.wallet.publicKey + ); + + const txn = await program.rpc.testInitAssociatedToken({ accounts: { - token: token.publicKey, + token: associatedToken, mint: mint.publicKey, payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, }, - signers: [token], }); - const fakeAuthority = Keypair.generate(); + console.log("InitAssocToken:", txn); + try { - await program.rpc.testTokenAuthConstraint({ + await program.rpc.testInitAssociatedTokenIfNeeded({ accounts: { - token: token.publicKey, - mint: mint1.publicKey, - fakeAuthority: fakeAuthority.publicKey, + token: associatedToken, + mint: mint2.publicKey, + payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + authority: provider.wallet.publicKey, }, }); - assert.isTrue(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); + assert.strictEqual(err.error.errorCode.number, 2014); } }); - it("Mint Constraint Test(no init) - mint::decimals, mint::authority, mint::freeze_authority", async () => { + it("init_if_needed throws if token exists with correct owner and mint but is not the ATA", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -2033,266 +1589,769 @@ describe("misc", () => { }, signers: [mint], }); - await program.rpc.testMintConstraint(6, { - accounts: { - mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, + + const associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue( - mintAccount.mintAuthority.equals(provider.wallet.publicKey) - ); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + mint.publicKey, + provider.wallet.publicKey ); - }); - it("Mint Constraint Test(no init) - throws if mint::decimals mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ + await program.rpc.testInitAssociatedToken({ accounts: { + token: associatedToken, mint: mint.publicKey, payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, }, - signers: [mint], }); - const fakeDecimal = 5; - try { - await program.rpc.testMintConstraint(fakeDecimal, { - accounts: { - mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2018); - assert.strictEqual(err.error.errorCode.code, "ConstraintMintDecimals"); - } - }); - it("Mint Constraint Test(no init) - throws if mint::mint_authority mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ accounts: { + token: token.publicKey, mint: mint.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, - signers: [mint], + signers: [token], }); - const fakeAuthority = Keypair.generate(); try { - await program.rpc.testMintConstraint(6, { + await program.rpc.testInitAssociatedTokenIfNeeded({ accounts: { + token: token.publicKey, mint: mint.publicKey, - mintAuthority: fakeAuthority.publicKey, - freezeAuthority: provider.wallet.publicKey, + payer: provider.wallet.publicKey, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + authority: provider.wallet.publicKey, }, }); - assert.isTrue(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2016); - assert.strictEqual( - err.error.errorCode.code, - "ConstraintMintMintAuthority" - ); + assert.strictEqual(err.error.errorCode.number, 3014); } }); - it("Mint Constraint Test(no init) - throws if mint::freeze_authority mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ + it("Can use multidimensional array", async () => { + const array2d = new Array(10).fill(new Array(10).fill(99)); + const data = anchor.web3.Keypair.generate(); + await program.rpc.testMultidimensionalArray(array2d, { accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, + data: data.publicKey, }, - signers: [mint], - }); + signers: [data], + instructions: [ + await program.account.dataMultidimensionalArray.createInstruction( + data + ), + ], + }); + const dataAccount = await program.account.dataMultidimensionalArray.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, array2d); + }); - const fakeAuthority = Keypair.generate(); - try { - await program.rpc.testMintConstraint(6, { + it("Can use multidimensional array with const sizes", async () => { + const array2d = new Array(10).fill(new Array(11).fill(22)); + const data = anchor.web3.Keypair.generate(); + await program.rpc.testMultidimensionalArrayConstSizes(array2d, { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [ + await program.account.dataMultidimensionalArrayConstSizes.createInstruction( + data + ), + ], + }); + const dataAccount = + await program.account.dataMultidimensionalArrayConstSizes.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, array2d); + }); + + describe("Can validate PDAs derived from other program ids", () => { + it("With bumps using create_program_address", async () => { + const [firstPDA, firstBump] = + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + ASSOCIATED_TOKEN_PROGRAM_ID + ); + const [secondPDA, secondBump] = + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + program.programId + ); + + // correct bump but wrong address + const wrongAddress = anchor.web3.Keypair.generate().publicKey; + try { + await program.rpc.testProgramIdConstraint(firstBump, secondBump, { + accounts: { + first: wrongAddress, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // matching bump seed for wrong address but derived from wrong program + try { + await program.rpc.testProgramIdConstraint(secondBump, secondBump, { + accounts: { + first: secondPDA, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // correct inputs should lead to successful tx + await program.rpc.testProgramIdConstraint(firstBump, secondBump, { + accounts: { + first: firstPDA, + second: secondPDA, + }, + }); + }); + + it("With bumps using find_program_address", async () => { + const firstPDA = ( + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + ASSOCIATED_TOKEN_PROGRAM_ID + ) + )[0]; + const secondPDA = ( + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + program.programId + ) + )[0]; + + // random wrong address + const wrongAddress = anchor.web3.Keypair.generate().publicKey; + try { + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: wrongAddress, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // same seeds but derived from wrong program + try { + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: secondPDA, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // correct inputs should lead to successful tx + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: firstPDA, + second: secondPDA, + }, + }); + }); + }); + describe("Token Constraint Test", () => { + it("Token Constraint Test(no init) - Can make token::mint and token::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ accounts: { mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: fakeAuthority.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, + signers: [mint], }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2017); - assert.strictEqual( - err.error.errorCode.code, - "ConstraintMintFreezeAuthority" + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + await program.rpc.testTokenConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer ); - } - }); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); + }); - it("Mint Constraint Test(no init) - can write only mint::decimals", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], + it("Token Constraint Test(no init) - Can make only token::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + await program.rpc.testOnlyAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); }); - await program.rpc.testMintOnlyDecimalsConstraint(6, { - accounts: { - mint: mint.publicKey, - }, + it("Token Constraint Test(no init) - Can make only token::mint", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + await program.rpc.testOnlyMintConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.isTrue(account.mint.equals(mint.publicKey)); }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - }); - it("Mint Constraint Test(no init) - can write only mint::authority and mint::freeze_authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], + it("Token Constraint Test(no init) - throws if token::mint mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const mint1 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint1.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint1], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + try { + await program.rpc.testTokenConstraint({ + accounts: { + token: token.publicKey, + mint: mint1.publicKey, + payer: provider.wallet.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2014); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenMint"); + } }); - await program.rpc.testMintOnlyAuthConstraint({ - accounts: { - mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, - }, + it("Token Constraint Test(no init) - throws if token::authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testTokenAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + fakeAuthority: fakeAuthority.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); + } }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.isTrue( - mintAccount.mintAuthority.equals(provider.wallet.publicKey) - ); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) - ); - }); - it("Mint Constraint Test(no init) - can write only mint::authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], + it("Token Constraint Test(no init) - throws if both token::authority, token::mint mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + const mint1 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint1.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint1], + }); + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [token], + }); + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testTokenAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint1.publicKey, + fakeAuthority: fakeAuthority.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); + } }); - await program.rpc.testMintOnlyOneAuthConstraint({ - accounts: { - mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - }, + it("Mint Constraint Test(no init) - mint::decimals, mint::authority, mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + ); }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.isTrue( - mintAccount.mintAuthority.equals(provider.wallet.publicKey) - ); - }); - it("Mint Constraint Test(no init) - can write only mint::decimals and mint::freeze_authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], + it("Mint Constraint Test(no init) - throws if mint::decimals mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + const fakeDecimal = 5; + try { + await program.rpc.testMintConstraint(fakeDecimal, { + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2018); + assert.strictEqual( + err.error.errorCode.code, + "ConstraintMintDecimals" + ); + } }); - await program.rpc.testMintMissMintAuthConstraint(6, { - accounts: { - mint: mint.publicKey, - freezeAuthority: provider.wallet.publicKey, - }, + it("Mint Constraint Test(no init) - throws if mint::mint_authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: fakeAuthority.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2016); + assert.strictEqual( + err.error.errorCode.code, + "ConstraintMintMintAuthority" + ); + } }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) - ); - }); - it("check versioned transaction is now available", async () => { - let thisTx = new VersionedTransaction( - new Message({ - header: { - numReadonlySignedAccounts: 0, - numReadonlyUnsignedAccounts: 0, - numRequiredSignatures: 0, + + it("Mint Constraint Test(no init) - throws if mint::freeze_authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, }, - accountKeys: [new PublicKey([0]).toString()], - instructions: [{ accounts: [0], data: "", programIdIndex: 0 }], - recentBlockhash: "", - }) - ); - assert.isDefined(thisTx); + signers: [mint], + }); + + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: fakeAuthority.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2017); + assert.strictEqual( + err.error.errorCode.code, + "ConstraintMintFreezeAuthority" + ); + } + }); + + it("Mint Constraint Test(no init) - can write only mint::decimals", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + await program.rpc.testMintOnlyDecimalsConstraint(6, { + accounts: { + mint: mint.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + }); + + it("Mint Constraint Test(no init) - can write only mint::authority and mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + await program.rpc.testMintOnlyAuthConstraint({ + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + ); + }); + + it("Mint Constraint Test(no init) - can write only mint::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + await program.rpc.testMintOnlyOneAuthConstraint({ + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + }); + + it("Mint Constraint Test(no init) - can write only mint::decimals and mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + }, + signers: [mint], + }); + + await program.rpc.testMintMissMintAuthConstraint(6, { + accounts: { + mint: mint.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + ); + }); + it("check versioned transaction is now available", async () => { + let thisTx = new VersionedTransaction( + new Message({ + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 0, + numRequiredSignatures: 0, + }, + accountKeys: [new PublicKey([0]).toString()], + instructions: [{ accounts: [0], data: "", programIdIndex: 0 }], + recentBlockhash: "", + }) + ); + assert.isDefined(thisTx); + }); }); - }); -}); + }; +}; + +export default miscTest; + +describe("misc", miscTest(anchor.workspace.Misc as Program)); + +describe( + "misc-optional", + miscTest(anchor.workspace.MiscOptional as Program) +); From 9daa85cf7d63b6b53e1d9d4be8854628b2a2f214 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 1 Nov 2022 11:24:29 +0000 Subject: [PATCH 095/109] update version --- tests/misc/programs/misc-optional/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/misc/programs/misc-optional/Cargo.toml b/tests/misc/programs/misc-optional/Cargo.toml index 3e217525e2..526b5c5947 100644 --- a/tests/misc/programs/misc-optional/Cargo.toml +++ b/tests/misc/programs/misc-optional/Cargo.toml @@ -19,4 +19,4 @@ default = [] anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } anchor-spl = { path = "../../../../spl" } misc2 = { path = "../misc2", features = ["cpi"] } -spl-associated-token-account = "~1.0.3" +spl-associated-token-account = "1.1.1" From b8f975cc83c26bc0c319c5d1128ed07247523ae4 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Mon, 28 Nov 2022 01:45:18 -0600 Subject: [PATCH 096/109] fix rust version and resolve merge conflicts --- .github/workflows/no-cashing-tests.yaml | 2 +- .github/workflows/tests.yaml | 2 +- avm/Cargo.toml | 2 +- cli/Cargo.toml | 2 +- client/Cargo.toml | 2 +- client/example/Cargo.toml | 2 +- client/example/src/main.rs | 62 + .../basic-0/programs/basic-0/Cargo.toml | 2 +- .../basic-1/programs/basic-1/Cargo.toml | 2 +- .../basic-2/programs/basic-2/Cargo.toml | 2 +- .../basic-3/programs/puppet-master/Cargo.toml | 2 +- .../basic-3/programs/puppet/Cargo.toml | 2 +- .../basic-4/programs/basic-4/Cargo.toml | 2 +- lang/Cargo.toml | 2 +- lang/attribute/access-control/Cargo.toml | 2 +- lang/attribute/account/Cargo.toml | 2 +- lang/attribute/constant/Cargo.toml | 2 +- lang/attribute/error/Cargo.toml | 2 +- lang/attribute/event/Cargo.toml | 2 +- lang/attribute/interface/Cargo.toml | 2 +- lang/attribute/program/Cargo.toml | 2 +- lang/attribute/state/Cargo.toml | 2 +- lang/derive/accounts/Cargo.toml | 2 +- lang/syn/Cargo.toml | 2 +- lang/syn/src/codegen/accounts/constraints.rs | 2 +- spl/Cargo.toml | 2 +- .../programs/bpf-upgradeable-state/Cargo.toml | 2 +- .../programs/cashiers-check/Cargo.toml | 2 +- tests/cfo/programs/cfo/Cargo.toml | 2 +- tests/chat/programs/chat/Cargo.toml | 2 +- tests/composite/programs/composite/Cargo.toml | 2 +- .../programs/native-system/Cargo.toml | 2 +- .../programs/spl-token/Cargo.toml | 2 +- tests/errors/programs/errors/Cargo.toml | 2 +- tests/escrow/programs/escrow/Cargo.toml | 2 +- tests/events/programs/events/Cargo.toml | 2 +- tests/ido-pool/programs/ido-pool/Cargo.toml | 2 +- .../programs/counter-auth/Cargo.toml | 2 +- tests/interface/programs/counter/Cargo.toml | 2 +- tests/lockup/programs/lockup/Cargo.toml | 2 +- tests/lockup/programs/registry/Cargo.toml | 2 +- tests/misc/programs/idl_doc/Cargo.toml | 2 +- .../programs/misc-optional/src/context.rs | 7 - tests/misc/programs/misc/Cargo.toml | 2 +- tests/misc/programs/misc2/Cargo.toml | 2 +- tests/misc/programs/shared/Cargo.toml | 2 +- tests/misc/tests/misc/misc.ts | 3748 +++++++++-------- tests/multisig/programs/multisig/Cargo.toml | 2 +- .../programs/pda-derivation/Cargo.toml | 2 +- tests/pyth/programs/pyth/Cargo.toml | 2 +- .../programs/relations-derivation/Cargo.toml | 2 +- .../programs/token-proxy/Cargo.toml | 2 +- tests/swap/programs/swap/Cargo.toml | 2 +- .../programs/system-accounts/Cargo.toml | 2 +- tests/sysvars/programs/sysvars/Cargo.toml | 2 +- tests/tictactoe/programs/tictactoe/Cargo.toml | 2 +- tests/typescript/programs/shared/Cargo.toml | 2 +- .../typescript/programs/typescript/Cargo.toml | 2 +- tests/zero-copy/programs/shared/Cargo.toml | 2 +- tests/zero-copy/programs/zero-copy/Cargo.toml | 2 +- tests/zero-copy/programs/zero-cpi/Cargo.toml | 2 +- 61 files changed, 2015 insertions(+), 1918 deletions(-) diff --git a/.github/workflows/no-cashing-tests.yaml b/.github/workflows/no-cashing-tests.yaml index 621afaf2e1..94806f7573 100644 --- a/.github/workflows/no-cashing-tests.yaml +++ b/.github/workflows/no-cashing-tests.yaml @@ -5,7 +5,7 @@ on: branches: - master env: - SOLANA_CLI_VERSION: 1.14.7 + SOLANA_CLI_VERSION: 1.13.3 NODE_VERSION: 17.0.1 jobs: diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 74ced8dfe9..e2c34d83b4 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -8,7 +8,7 @@ on: branches: - master env: - SOLANA_CLI_VERSION: 1.14.7 + SOLANA_CLI_VERSION: 1.13.3 NODE_VERSION: 17.0.1 CARGO_PROFILE: debug diff --git a/avm/Cargo.toml b/avm/Cargo.toml index d31ffdface..870d921a5f 100644 --- a/avm/Cargo.toml +++ b/avm/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "avm" version = "0.25.0" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [[bin]] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 17265596ac..627fc007c1 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -2,7 +2,7 @@ name = "anchor-cli" version = "0.25.0" authors = ["armaniferrante "] -rust-version = "1.62" +rust-version = "1.59" edition = "2021" repository = "https://github.com/coral-xyz/anchor" description = "Anchor CLI" diff --git a/client/Cargo.toml b/client/Cargo.toml index 2787d20034..9eba2e6cbb 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -2,7 +2,7 @@ name = "anchor-client" version = "0.25.0" authors = ["Serum Foundation "] -rust-version = "1.62" +rust-version = "1.59" edition = "2021" license = "Apache-2.0" description = "Rust client for Anchor programs" diff --git a/client/example/Cargo.toml b/client/example/Cargo.toml index 1dda0534c7..d08ab238ba 100644 --- a/client/example/Cargo.toml +++ b/client/example/Cargo.toml @@ -2,7 +2,7 @@ name = "example" version = "0.1.0" authors = ["Armani Ferrante "] -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [workspace] diff --git a/client/example/src/main.rs b/client/example/src/main.rs index 8abd386cad..d98ae74a1d 100644 --- a/client/example/src/main.rs +++ b/client/example/src/main.rs @@ -21,6 +21,7 @@ use clap::Parser; use composite::accounts::{Bar, CompositeUpdate, Foo, Initialize}; use composite::instruction as composite_instruction; use composite::{DummyA, DummyB}; +use optional::account::{DataAccount, DataPda}; use std::rc::Rc; use std::time::Duration; @@ -34,6 +35,8 @@ pub struct Opts { basic_4_pid: Pubkey, #[clap(long)] events_pid: Pubkey, + #[clap(long)] + optional_pid: Pubkey, } // This example assumes a local validator is running with the programs @@ -58,6 +61,7 @@ fn main() -> Result<()> { basic_2(&client, opts.basic_2_pid)?; basic_4(&client, opts.basic_4_pid)?; events(&client, opts.events_pid)?; + optional(&client, opts.optional_pid)?; // Success. Ok(()) @@ -226,3 +230,61 @@ pub fn basic_4(client: &Client, pid: Pubkey) -> Result<()> { Ok(()) } + +// Runs a client for tests/optional. +// +// Make sure to run a localnet with the program deploy to run this example. +fn optional(client: &Client, pid: Pubkey) -> Result<()> { + // Program client. + let program = client.program(pid); + + // `Initialize` parameters. + let data_account_keypair = Keypair::new(); + + let data_account_key = data_account_keypair.pubkey(); + + let data_pda_seeds = &[DataPda::PREFIX.as_ref(), data_account_key.as_ref()]; + let data_pda_key = Pubkey::find_program_address(data_pda_seeds, &pid).0; + let required_keypair = Keypair::new(); + let value: u64 = 10; + + // Build and send a transaction. + + program + .request() + .instruction(system_instruction::create_account( + &program.payer(), + &required_keypair.pubkey(), + program + .rpc() + .get_minimum_balance_for_rent_exemption(DataAccount::LEN)?, + DataAccount::LEN as u64, + &program.id(), + )) + .signer(&data_account_keypair) + .signer(&required_keypair) + .accounts(OptionalInitialize { + payer: Some(program.payer()), + required: required_keypair.pubkey(), + system_program: Some(system_program::id()), + optional_account: Some(data_account_keypair.pubkey()), + optional_pda: None, + }) + .args(optional_instruction::Initialize { value, key: pid }) + .send() + .unwrap(); + + // Assert the transaction worked. + let required: DataAccount = program.account(required_keypair.pubkey())?; + assert_eq!(required.data, 0); + + let optional_pda = program.account::(data_pda_key); + assert!(optional_pda.is_err()); + + let optional_account: DataAccount = program.account(data_account_keypair.pubkey())?; + assert_eq!(optional_account.data, value * 2); + + println!("Optional success!"); + + Ok(()) +} diff --git a/examples/tutorial/basic-0/programs/basic-0/Cargo.toml b/examples/tutorial/basic-0/programs/basic-0/Cargo.toml index 24610332de..fff101680f 100644 --- a/examples/tutorial/basic-0/programs/basic-0/Cargo.toml +++ b/examples/tutorial/basic-0/programs/basic-0/Cargo.toml @@ -2,7 +2,7 @@ name = "basic-0" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/examples/tutorial/basic-1/programs/basic-1/Cargo.toml b/examples/tutorial/basic-1/programs/basic-1/Cargo.toml index b0c58db394..f6b91f1689 100644 --- a/examples/tutorial/basic-1/programs/basic-1/Cargo.toml +++ b/examples/tutorial/basic-1/programs/basic-1/Cargo.toml @@ -2,7 +2,7 @@ name = "basic-1" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/examples/tutorial/basic-2/programs/basic-2/Cargo.toml b/examples/tutorial/basic-2/programs/basic-2/Cargo.toml index 17c55c3517..f25afb8a8f 100644 --- a/examples/tutorial/basic-2/programs/basic-2/Cargo.toml +++ b/examples/tutorial/basic-2/programs/basic-2/Cargo.toml @@ -2,7 +2,7 @@ name = "basic-2" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/examples/tutorial/basic-3/programs/puppet-master/Cargo.toml b/examples/tutorial/basic-3/programs/puppet-master/Cargo.toml index 35ad97a139..cbdc18a5e0 100644 --- a/examples/tutorial/basic-3/programs/puppet-master/Cargo.toml +++ b/examples/tutorial/basic-3/programs/puppet-master/Cargo.toml @@ -2,7 +2,7 @@ name = "puppet-master" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/examples/tutorial/basic-3/programs/puppet/Cargo.toml b/examples/tutorial/basic-3/programs/puppet/Cargo.toml index 85d5a8555f..b4d4d11992 100644 --- a/examples/tutorial/basic-3/programs/puppet/Cargo.toml +++ b/examples/tutorial/basic-3/programs/puppet/Cargo.toml @@ -2,7 +2,7 @@ name = "puppet" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/examples/tutorial/basic-4/programs/basic-4/Cargo.toml b/examples/tutorial/basic-4/programs/basic-4/Cargo.toml index ab69c4b739..1d971632d4 100644 --- a/examples/tutorial/basic-4/programs/basic-4/Cargo.toml +++ b/examples/tutorial/basic-4/programs/basic-4/Cargo.toml @@ -2,7 +2,7 @@ name = "basic-4" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/lang/Cargo.toml b/lang/Cargo.toml index 29e4c318e0..e840c3c490 100644 --- a/lang/Cargo.toml +++ b/lang/Cargo.toml @@ -3,7 +3,7 @@ name = "anchor-lang" version = "0.25.0" authors = ["Serum Foundation "] repository = "https://github.com/coral-xyz/anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" license = "Apache-2.0" description = "Solana Sealevel eDSL" diff --git a/lang/attribute/access-control/Cargo.toml b/lang/attribute/access-control/Cargo.toml index dee564471b..7bd124aa77 100644 --- a/lang/attribute/access-control/Cargo.toml +++ b/lang/attribute/access-control/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Serum Foundation "] repository = "https://github.com/coral-xyz/anchor" license = "Apache-2.0" description = "Anchor attribute macro for instruction access control" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/lang/attribute/account/Cargo.toml b/lang/attribute/account/Cargo.toml index 868e3e7510..485057c102 100644 --- a/lang/attribute/account/Cargo.toml +++ b/lang/attribute/account/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Serum Foundation "] repository = "https://github.com/coral-xyz/anchor" license = "Apache-2.0" description = "Anchor attribute macro for defining an account" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/lang/attribute/constant/Cargo.toml b/lang/attribute/constant/Cargo.toml index a5fb8f7a11..51dc782967 100644 --- a/lang/attribute/constant/Cargo.toml +++ b/lang/attribute/constant/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Serum Foundation "] repository = "https://github.com/coral-xyz/anchor" license = "Apache-2.0" description = "Anchor attribute macro for creating constant types" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/lang/attribute/error/Cargo.toml b/lang/attribute/error/Cargo.toml index 5c5910be34..b2fc8854ef 100644 --- a/lang/attribute/error/Cargo.toml +++ b/lang/attribute/error/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Serum Foundation "] repository = "https://github.com/coral-xyz/anchor" license = "Apache-2.0" description = "Anchor attribute macro for creating error types" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/lang/attribute/event/Cargo.toml b/lang/attribute/event/Cargo.toml index 07bbbea1c8..0eb45279dc 100644 --- a/lang/attribute/event/Cargo.toml +++ b/lang/attribute/event/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Serum Foundation "] repository = "https://github.com/coral-xyz/anchor" license = "Apache-2.0" description = "Anchor attribute macro for defining an event" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/lang/attribute/interface/Cargo.toml b/lang/attribute/interface/Cargo.toml index 0143c37992..cafb3d1ba7 100644 --- a/lang/attribute/interface/Cargo.toml +++ b/lang/attribute/interface/Cargo.toml @@ -5,7 +5,7 @@ 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.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/lang/attribute/program/Cargo.toml b/lang/attribute/program/Cargo.toml index c03762f9cc..a5100e86c9 100644 --- a/lang/attribute/program/Cargo.toml +++ b/lang/attribute/program/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Serum Foundation "] repository = "https://github.com/coral-xyz/anchor" license = "Apache-2.0" description = "Anchor attribute macro for defining a program" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/lang/attribute/state/Cargo.toml b/lang/attribute/state/Cargo.toml index 283f17e1d4..b1372b1524 100644 --- a/lang/attribute/state/Cargo.toml +++ b/lang/attribute/state/Cargo.toml @@ -5,7 +5,7 @@ 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.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/lang/derive/accounts/Cargo.toml b/lang/derive/accounts/Cargo.toml index 5dcb3aa143..efffc6105d 100644 --- a/lang/derive/accounts/Cargo.toml +++ b/lang/derive/accounts/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Serum Foundation "] repository = "https://github.com/coral-xyz/anchor" license = "Apache-2.0" description = "Anchor Derive macro for accounts" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/lang/syn/Cargo.toml b/lang/syn/Cargo.toml index c1452dc69c..cc64c48c3e 100644 --- a/lang/syn/Cargo.toml +++ b/lang/syn/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Serum Foundation "] repository = "https://github.com/coral-xyz/anchor" license = "Apache-2.0" description = "Anchor syntax parsing and code generation tools" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [features] diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs index 912a7769c5..17a041aa6b 100644 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ b/lang/syn/src/codegen/accounts/constraints.rs @@ -1096,7 +1096,7 @@ fn generate_create_account( let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); anchor_lang::system_program::create_account(cpi_context.with_signer(&[#seeds_with_nonce]), lamports, #space as u64, #owner)?; } else { - require_keys_neq!(payer.key(), #field.key(), anchor_lang::error::ErrorCode::TryingToInitPayerAsProgramAccount); + require_keys_neq!(#payer.key(), #field.key(), anchor_lang::error::ErrorCode::TryingToInitPayerAsProgramAccount); // Fund the account for rent exemption. let required_lamports = __anchor_rent .minimum_balance(#space) diff --git a/spl/Cargo.toml b/spl/Cargo.toml index 8c2ed70693..9239db32db 100644 --- a/spl/Cargo.toml +++ b/spl/Cargo.toml @@ -2,7 +2,7 @@ name = "anchor-spl" version = "0.25.0" authors = ["Serum Foundation "] -rust-version = "1.62" +rust-version = "1.59" edition = "2021" license = "Apache-2.0" description = "CPI clients for SPL programs" diff --git a/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Cargo.toml b/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Cargo.toml index e9fca19019..65ae7cb596 100644 --- a/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Cargo.toml +++ b/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Cargo.toml @@ -2,7 +2,7 @@ name = "bpf-upgradeable-state" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/cashiers-check/programs/cashiers-check/Cargo.toml b/tests/cashiers-check/programs/cashiers-check/Cargo.toml index 165c5f432c..94ef74ad75 100644 --- a/tests/cashiers-check/programs/cashiers-check/Cargo.toml +++ b/tests/cashiers-check/programs/cashiers-check/Cargo.toml @@ -2,7 +2,7 @@ name = "cashiers-check" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/cfo/programs/cfo/Cargo.toml b/tests/cfo/programs/cfo/Cargo.toml index fdbf14ccf1..90f65e72fb 100644 --- a/tests/cfo/programs/cfo/Cargo.toml +++ b/tests/cfo/programs/cfo/Cargo.toml @@ -2,7 +2,7 @@ name = "cfo" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/chat/programs/chat/Cargo.toml b/tests/chat/programs/chat/Cargo.toml index 6ba8679d6e..45b1f10d95 100644 --- a/tests/chat/programs/chat/Cargo.toml +++ b/tests/chat/programs/chat/Cargo.toml @@ -2,7 +2,7 @@ name = "chat" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/composite/programs/composite/Cargo.toml b/tests/composite/programs/composite/Cargo.toml index 951b32c884..e8508d2fda 100644 --- a/tests/composite/programs/composite/Cargo.toml +++ b/tests/composite/programs/composite/Cargo.toml @@ -2,7 +2,7 @@ name = "composite" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/custom-coder/programs/native-system/Cargo.toml b/tests/custom-coder/programs/native-system/Cargo.toml index 86d2efc67c..c12fa81f67 100644 --- a/tests/custom-coder/programs/native-system/Cargo.toml +++ b/tests/custom-coder/programs/native-system/Cargo.toml @@ -2,7 +2,7 @@ name = "native-system" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/custom-coder/programs/spl-token/Cargo.toml b/tests/custom-coder/programs/spl-token/Cargo.toml index 99788da163..988b54507d 100644 --- a/tests/custom-coder/programs/spl-token/Cargo.toml +++ b/tests/custom-coder/programs/spl-token/Cargo.toml @@ -2,7 +2,7 @@ name = "spl-token" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/errors/programs/errors/Cargo.toml b/tests/errors/programs/errors/Cargo.toml index 8eb7236028..d4f5d665fc 100644 --- a/tests/errors/programs/errors/Cargo.toml +++ b/tests/errors/programs/errors/Cargo.toml @@ -2,7 +2,7 @@ name = "errors" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/escrow/programs/escrow/Cargo.toml b/tests/escrow/programs/escrow/Cargo.toml index 9da089348c..934997939b 100644 --- a/tests/escrow/programs/escrow/Cargo.toml +++ b/tests/escrow/programs/escrow/Cargo.toml @@ -2,7 +2,7 @@ name = "escrow" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/events/programs/events/Cargo.toml b/tests/events/programs/events/Cargo.toml index 62fc6f6d58..252eb6d9ac 100644 --- a/tests/events/programs/events/Cargo.toml +++ b/tests/events/programs/events/Cargo.toml @@ -2,7 +2,7 @@ name = "events" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/ido-pool/programs/ido-pool/Cargo.toml b/tests/ido-pool/programs/ido-pool/Cargo.toml index 2eac5accee..4ed124f34a 100644 --- a/tests/ido-pool/programs/ido-pool/Cargo.toml +++ b/tests/ido-pool/programs/ido-pool/Cargo.toml @@ -2,7 +2,7 @@ name = "ido-pool" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/interface/programs/counter-auth/Cargo.toml b/tests/interface/programs/counter-auth/Cargo.toml index 0fd7e53b6b..abf20bb5d0 100644 --- a/tests/interface/programs/counter-auth/Cargo.toml +++ b/tests/interface/programs/counter-auth/Cargo.toml @@ -2,7 +2,7 @@ name = "counter-auth" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/interface/programs/counter/Cargo.toml b/tests/interface/programs/counter/Cargo.toml index b8a3ed4a2f..d73bc60d09 100644 --- a/tests/interface/programs/counter/Cargo.toml +++ b/tests/interface/programs/counter/Cargo.toml @@ -2,7 +2,7 @@ name = "counter" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/lockup/programs/lockup/Cargo.toml b/tests/lockup/programs/lockup/Cargo.toml index f7d08c8c65..c9b37e3d01 100644 --- a/tests/lockup/programs/lockup/Cargo.toml +++ b/tests/lockup/programs/lockup/Cargo.toml @@ -2,7 +2,7 @@ name = "lockup" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/lockup/programs/registry/Cargo.toml b/tests/lockup/programs/registry/Cargo.toml index b0063e5aab..1f93b1e770 100644 --- a/tests/lockup/programs/registry/Cargo.toml +++ b/tests/lockup/programs/registry/Cargo.toml @@ -2,7 +2,7 @@ name = "registry" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/misc/programs/idl_doc/Cargo.toml b/tests/misc/programs/idl_doc/Cargo.toml index fb9ce877db..447a29b96f 100644 --- a/tests/misc/programs/idl_doc/Cargo.toml +++ b/tests/misc/programs/idl_doc/Cargo.toml @@ -2,7 +2,7 @@ name = "idl_doc" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/misc/programs/misc-optional/src/context.rs b/tests/misc/programs/misc-optional/src/context.rs index 46ed539648..663792c2df 100644 --- a/tests/misc/programs/misc-optional/src/context.rs +++ b/tests/misc/programs/misc-optional/src/context.rs @@ -30,7 +30,6 @@ pub struct TestTokenSeedsInit<'info> { /// CHECK: pub authority: Option>, pub system_program: Option>, - pub rent: Option>, pub token_program: Option>, } @@ -46,7 +45,6 @@ pub struct TestInitAssociatedToken<'info> { pub mint: Option>, #[account(mut)] pub payer: Option>, - pub rent: Option>, pub system_program: Option>, pub token_program: Option>, pub associated_token_program: Option>, @@ -252,7 +250,6 @@ pub struct TestInitMint<'info> { pub mint: Option>, #[account(mut)] pub payer: Option>, - pub rent: Option>, pub system_program: Option>, pub token_program: Option>, } @@ -264,7 +261,6 @@ pub struct TestInitToken<'info> { pub mint: Option>, #[account(mut)] pub payer: Option>, - pub rent: Option>, pub system_program: Option>, pub token_program: Option>, } @@ -343,7 +339,6 @@ pub struct TestInitMintIfNeeded<'info> { pub mint: Option>, #[account(mut)] pub payer: Option>, - pub rent: Option>, pub system_program: Option>, pub token_program: Option>, /// CHECK: @@ -359,7 +354,6 @@ pub struct TestInitTokenIfNeeded<'info> { pub mint: Option>, #[account(mut)] pub payer: Option>, - pub rent: Option>, pub system_program: Option>, pub token_program: Option>, /// CHECK: @@ -378,7 +372,6 @@ pub struct TestInitAssociatedTokenIfNeeded<'info> { pub mint: Option>, #[account(mut)] pub payer: Option>, - pub rent: Option>, pub system_program: Option>, pub token_program: Option>, pub associated_token_program: Option>, diff --git a/tests/misc/programs/misc/Cargo.toml b/tests/misc/programs/misc/Cargo.toml index 248b3e35aa..2ff11fcc71 100644 --- a/tests/misc/programs/misc/Cargo.toml +++ b/tests/misc/programs/misc/Cargo.toml @@ -2,7 +2,7 @@ name = "misc" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/misc/programs/misc2/Cargo.toml b/tests/misc/programs/misc2/Cargo.toml index da4824bb11..2a66a37aa1 100644 --- a/tests/misc/programs/misc2/Cargo.toml +++ b/tests/misc/programs/misc2/Cargo.toml @@ -2,7 +2,7 @@ name = "misc2" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/misc/programs/shared/Cargo.toml b/tests/misc/programs/shared/Cargo.toml index b8fb9b9c18..c95b4a603f 100644 --- a/tests/misc/programs/shared/Cargo.toml +++ b/tests/misc/programs/shared/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "shared" version = "0.1.0" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/misc/tests/misc/misc.ts b/tests/misc/tests/misc/misc.ts index 977b68d4a9..9f1f3aa70a 100644 --- a/tests/misc/tests/misc/misc.ts +++ b/tests/misc/tests/misc/misc.ts @@ -22,1736 +22,659 @@ import { ASSOCIATED_TOKEN_PROGRAM_ID, } 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"); const nativeAssert = require("assert"); const miscIdl = require("../../target/idl/misc.json"); -describe("misc", () => { - // Configure the client to use the local cluster. - const provider = anchor.AnchorProvider.env(); - const wallet = provider.wallet as Wallet; - anchor.setProvider(provider); - const program = anchor.workspace.Misc as Program; - 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(); - - it("Can use u128 and i128", async () => { - const tx = await program.rpc.initialize( - new anchor.BN(1234), - new anchor.BN(22), - { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [await program.account.data.createInstruction(data)], - } - ); - const dataAccount = await program.account.data.fetch(data.publicKey); - assert.isTrue(dataAccount.udata.eq(new anchor.BN(1234))); - assert.isTrue(dataAccount.idata.eq(new anchor.BN(22))); - }); - - it("Can use u16", async () => { - const data = anchor.web3.Keypair.generate(); - const tx = await program.rpc.testU16(99, { - accounts: { - myAccount: data.publicKey, - }, - signers: [data], - instructions: [await program.account.dataU16.createInstruction(data)], - }); - const dataAccount = await program.account.dataU16.fetch(data.publicKey); - assert.strictEqual(dataAccount.data, 99); - }); - - it("Can embed programs into genesis from the Anchor.toml", async () => { - const pid = new anchor.web3.PublicKey( - "FtMNMKp9DZHKWUyVAsj3Q5QV8ow4P3fUPP7ZrWEQJzKr" - ); - let accInfo = await anchor.getProvider().connection.getAccountInfo(pid); - assert.isTrue(accInfo.executable); - }); - - it("Can use the owner constraint", async () => { - await program.rpc.testOwner({ - accounts: { - data: data.publicKey, - misc: program.programId, - }, - }); - - await nativeAssert.rejects( - async () => { - await program.rpc.testOwner({ - accounts: { - data: provider.wallet.publicKey, - misc: program.programId, - }, - }); - }, - (err) => { - return true; - } - ); - }); - - it("Can use the executable attribute", async () => { - await program.rpc.testExecutable({ - accounts: { - program: program.programId, - }, +const miscTest = ( + program: anchor.Program | anchor.Program +) => { + return () => { + // Configure the client to use the local cluster. + 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); }); - await nativeAssert.rejects( - async () => { - await program.rpc.testExecutable({ - accounts: { - program: provider.wallet.publicKey, + it("Can use remaining accounts for a state instruction", async () => { + await program.state.rpc.remainingAccounts({ + remainingAccounts: [ + { + pubkey: misc2Program.programId, + isWritable: false, + isSigner: false, }, - }); - }, - (err) => { - return true; - } - ); - }); - - it("Can CPI to state instructions", async () => { - const oldData = new anchor.BN(0); - 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 = [ - "Program 3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh invoke [1]", - "Program log: Instruction: TestSimulate", - "Program data: NgyCA9omwbMsAAAA", - "Program data: fPhuIELK/k7SBAAA", - "Program data: jvbowsvlmkcJAAAA", - "Program data: zxM5neEnS1kBAgMEBQYHCAkK", - "Program data: g06Ei2GL1gIBAgMEBQYHCAkKCw==", - ]; - - assert.deepStrictEqual(expectedRaw, resp.raw.slice(0, -2)); - assert.strictEqual(resp.events[0].name, "E1"); - assert.strictEqual(resp.events[0].data.data, 44); - assert.strictEqual(resp.events[1].name, "E2"); - assert.strictEqual(resp.events[1].data.data, 1234); - assert.strictEqual(resp.events[2].name, "E3"); - assert.strictEqual(resp.events[2].data.data, 9); - assert.strictEqual(resp.events[3].name, "E5"); - assert.deepStrictEqual( - resp.events[3].data.data, - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - ); - assert.strictEqual(resp.events[4].name, "E6"); - assert.deepStrictEqual( - resp.events[4].data.data, - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - ); - }); - - it("Can use enum in idl", async () => { - const resp1 = await program.methods.testInputEnum({ first: {} }).simulate(); - const event1 = resp1.events[0].data as IdlEvents["E7"]; - assert.deepEqual(event1.data.first, {}); - - const resp2 = await program.methods - .testInputEnum({ second: { x: new BN(1), y: new BN(2) } }) - .simulate(); - const event2 = resp2.events[0].data as IdlEvents["E7"]; - assert.isTrue(new BN(1).eq(event2.data.second.x)); - assert.isTrue(new BN(2).eq(event2.data.second.y)); - - const resp3 = await program.methods - .testInputEnum({ - tupleStructTest: [ - { data1: 1, data2: 11, data3: 111, data4: new BN(1111) }, ], - }) - .simulate(); - const event3 = resp3.events[0].data as IdlEvents["E7"]; - assert.strictEqual(event3.data.tupleStructTest[0].data1, 1); - assert.strictEqual(event3.data.tupleStructTest[0].data2, 11); - assert.strictEqual(event3.data.tupleStructTest[0].data3, 111); - assert.isTrue(event3.data.tupleStructTest[0].data4.eq(new BN(1111))); - - const resp4 = await program.methods - .testInputEnum({ tupleTest: [1, 2, 3, 4] }) - .simulate(); - const event4 = resp4.events[0].data as IdlEvents["E7"]; - assert.strictEqual(event4.data.tupleTest[0], 1); - assert.strictEqual(event4.data.tupleTest[1], 2); - assert.strictEqual(event4.data.tupleTest[2], 3); - assert.strictEqual(event4.data.tupleTest[3], 4); - }); - - let dataI8; - - it("Can use i8 in the idl", async () => { - dataI8 = anchor.web3.Keypair.generate(); - await program.rpc.testI8(-3, { - accounts: { - data: dataI8.publicKey, - }, - instructions: [await program.account.dataI8.createInstruction(dataI8)], - signers: [dataI8], - }); - const dataAccount = await program.account.dataI8.fetch(dataI8.publicKey); - assert.strictEqual(dataAccount.data, -3); - }); - - let dataPubkey; - - it("Can use i16 in the idl", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testI16(-2048, { - accounts: { - data: data.publicKey, - }, - instructions: [await program.account.dataI16.createInstruction(data)], - signers: [data], - }); - const dataAccount = await program.account.dataI16.fetch(data.publicKey); - assert.strictEqual(dataAccount.data, -2048); - - dataPubkey = data.publicKey; - }); - - it("Can use base58 strings to fetch an account", async () => { - const dataAccount = await program.account.dataI16.fetch( - dataPubkey.toString() - ); - assert.strictEqual(dataAccount.data, -2048); - }); - - it("Should fail to close an account when sending lamports to itself", async () => { - try { - await program.rpc.testClose({ - accounts: { - data: data.publicKey, - solDest: data.publicKey, - }, }); - expect(false).to.be.true; - } catch (err) { - const errMsg = "A close constraint was violated"; - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number, 2011); - } - }); - - it("Can close an account", async () => { - const connection = program.provider.connection; - const openAccount = await connection.getAccountInfo(data.publicKey); - - assert.isNotNull(openAccount); - const openAccountBalance = openAccount.lamports; - // double balance to calculate closed balance correctly - const transferIx = anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: data.publicKey, - lamports: openAccountBalance, - }); - const transferTransaction = new anchor.web3.Transaction().add(transferIx); - await provider.sendAndConfirm(transferTransaction); - - let beforeBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; - - await program.methods - .testClose() - .accounts({ - data: data.publicKey, - solDest: provider.wallet.publicKey, - }) - .postInstructions([transferIx]) - .rpc(); - - let afterBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; - - // Retrieved rent exemption sol. - expect(afterBalance > beforeBalance).to.be.true; - - const closedAccount = await connection.getAccountInfo(data.publicKey); - - assert.isTrue(closedAccount.data.length === 0); - assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); - }); - - it("Can close an account twice", async () => { - const data = anchor.web3.Keypair.generate(); - await program.methods - .initialize(new anchor.BN(10), new anchor.BN(10)) - .accounts({ data: data.publicKey }) - .preInstructions([await program.account.data.createInstruction(data)]) - .signers([data]) - .rpc(); - - const connection = program.provider.connection; - const openAccount = await connection.getAccountInfo(data.publicKey); - assert.isNotNull(openAccount); - - const openAccountBalance = openAccount.lamports; - // double balance to calculate closed balance correctly - const transferIx = anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: data.publicKey, - lamports: openAccountBalance, - }); - const transferTransaction = new anchor.web3.Transaction().add(transferIx); - await provider.sendAndConfirm(transferTransaction); - - let beforeBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; - - await program.methods - .testCloseTwice() - .accounts({ - data: data.publicKey, - solDest: provider.wallet.publicKey, - }) - .postInstructions([transferIx]) - .rpc(); - - let afterBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; - - // Retrieved rent exemption sol. - expect(afterBalance > beforeBalance).to.be.true; - - const closedAccount = await connection.getAccountInfo(data.publicKey); - assert.isTrue(closedAccount.data.length === 0); - assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); - }); - - it("Can close a mut account manually", async () => { - const data = anchor.web3.Keypair.generate(); - await program.methods - .initialize(new anchor.BN(10), new anchor.BN(10)) - .accounts({ data: data.publicKey }) - .preInstructions([await program.account.data.createInstruction(data)]) - .signers([data]) - .rpc(); - - const connection = program.provider.connection; - const openAccount = await connection.getAccountInfo(data.publicKey); - - assert.isNotNull(openAccount); - const openAccountBalance = openAccount.lamports; - // double balance to calculate closed balance correctly - const transferIx = anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: data.publicKey, - lamports: openAccountBalance, - }); - const transferTransaction = new anchor.web3.Transaction().add(transferIx); - await provider.sendAndConfirm(transferTransaction); - - let beforeBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; - - await program.methods - .testCloseMut() - .accounts({ - data: data.publicKey, - solDest: provider.wallet.publicKey, - }) - .postInstructions([transferIx]) - .rpc(); - - let afterBalance = ( - await connection.getAccountInfo(provider.wallet.publicKey) - ).lamports; - - // Retrieved rent exemption sol. - expect(afterBalance > beforeBalance).to.be.true; - - const closedAccount = await connection.getAccountInfo(data.publicKey); - assert.isTrue(closedAccount.data.length === 0); - assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); - }); - - it("Can use instruction data in accounts constraints", async () => { - // b"my-seed" - const seed = Buffer.from([109, 121, 45, 115, 101, 101, 100]); - const [myPda, nonce] = await PublicKey.findProgramAddress( - [seed, anchor.web3.SYSVAR_RENT_PUBKEY.toBuffer()], - program.programId - ); - - await program.rpc.testInstructionConstraint(nonce, { - accounts: { - myPda, - myAccount: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - }); - }); - - it("Can create a PDA account with instruction data", async () => { - const seed = Buffer.from([1, 2, 3, 4]); - const domain = "my-domain"; - const foo = anchor.web3.SYSVAR_RENT_PUBKEY; - const [myPda, nonce] = await PublicKey.findProgramAddress( - [ - Buffer.from(anchor.utils.bytes.utf8.encode("my-seed")), - Buffer.from(anchor.utils.bytes.utf8.encode(domain)), - foo.toBuffer(), - seed, - ], - program.programId - ); - - await program.rpc.testPdaInit(domain, seed, nonce, { - accounts: { - myPda, - myPayer: provider.wallet.publicKey, - foo, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); - - const myPdaAccount = await program.account.dataU16.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 6); - }); - - it("Can create a zero copy PDA account", async () => { - const [myPda, nonce] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], - program.programId - ); - await program.rpc.testPdaInitZeroCopy({ - accounts: { - myPda, - myPayer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); - - const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 9); - assert.strictEqual(myPdaAccount.bump, nonce); - }); - - it("Can write to a zero copy PDA account", async () => { - const [myPda, bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], - program.programId - ); - await program.rpc.testPdaMutZeroCopy({ - accounts: { - myPda, - myPayer: provider.wallet.publicKey, - }, - }); - - const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 1234); - assert.strictEqual(myPdaAccount.bump, bump); - }); - - it("Can create a token account from seeds pda", async () => { - const [mint, mint_bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-mint-seed"))], - program.programId - ); - const [myPda, token_bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-token-seed"))], - program.programId - ); - await program.rpc.testTokenSeedsInit({ - accounts: { - myPda, - mint, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, }); - const mintAccount = new Token( - program.provider.connection, - mint, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await mintAccount.getAccountInfo(myPda); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint)); - }); - - it("Can execute a fallback function", async () => { - await nativeAssert.rejects( - async () => { - await anchor.utils.rpc.invoke(program.programId); - }, - (err) => { - assert.isTrue(err.toString().includes("custom program error: 0x4d2")); - return true; - } - ); - }); - - it("Can init a random account", async () => { const data = anchor.web3.Keypair.generate(); - await program.rpc.testInit({ - accounts: { - data: data.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - }); - - const account = await program.account.dataI8.fetch(data.publicKey); - assert.strictEqual(account.data, 3); - }); - it("Can init a random account prefunded", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testInit({ - accounts: { - data: data.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: data.publicKey, - lamports: 4039280, - }), - ], + it("Can use u128 and i128", async () => { + const tx = await program.rpc.initialize( + new anchor.BN(1234), + new anchor.BN(22), + { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [await program.account.data.createInstruction(data)], + } + ); + const dataAccount = await program.account.data.fetch(data.publicKey); + assert.isTrue(dataAccount.udata.eq(new anchor.BN(1234))); + assert.isTrue(dataAccount.idata.eq(new anchor.BN(22))); }); - const account = await program.account.dataI8.fetch(data.publicKey); - assert.strictEqual(account.data, 3); - }); - - it("Should fail when trying to init the payer as a program account", async () => { - try { - await program.rpc.testInit({ + it("Can use u16", async () => { + const data = anchor.web3.Keypair.generate(); + const tx = await program.rpc.testU16(99, { accounts: { - data: provider.wallet.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, + myAccount: data.publicKey, }, + signers: [data], + instructions: [await program.account.dataU16.createInstruction(data)], }); - assert.fail("Transaction should fail"); - } catch (e) { - // "Error Code: TryingToInitPayerAsProgramAccount. Error Number: 4101. Error Message: You cannot/should not initialize the payer account as a program account." - assert.strictEqual(e.error.errorCode.number, 4101); - } - }); - - it("Can init a random zero copy account", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testInitZeroCopy({ - accounts: { - data: data.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - }); - const account = await program.account.dataZeroCopy.fetch(data.publicKey); - assert.strictEqual(account.data, 10); - assert.strictEqual(account.bump, 2); - }); - - let mint = undefined; - - it("Can create a random mint account", async () => { - mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue(mintAccount.mintAuthority.equals(provider.wallet.publicKey)); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) - ); - }); - - it("Can create a random mint account prefunded", async () => { - mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: mint.publicKey, - lamports: 4039280, - }), - ], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue(mintAccount.mintAuthority.equals(provider.wallet.publicKey)); - }); - - it("Can create a random token account", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [token], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can create a random token with prefunding", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [token], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: token.publicKey, - lamports: 4039280, - }), - ], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can create a random token with prefunding under the rent exemption", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [token], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: token.publicKey, - lamports: 1, - }), - ], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can initialize multiple accounts via a composite payer", async () => { - const data1 = anchor.web3.Keypair.generate(); - const data2 = anchor.web3.Keypair.generate(); - - const tx = await program.rpc.testCompositePayer({ - accounts: { - composite: { - data: data1.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - data: data2.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data1, data2], + const dataAccount = await program.account.dataU16.fetch(data.publicKey); + assert.strictEqual(dataAccount.data, 99); }); - const account1 = await program.account.dataI8.fetch(data1.publicKey); - assert.strictEqual(account1.data, 1); - - const account2 = await program.account.data.fetch(data2.publicKey); - assert.strictEqual(account2.udata.toNumber(), 2); - assert.strictEqual(account2.idata.toNumber(), 3); - }); - - describe("associated_token constraints", () => { - let associatedToken = null; - // apparently cannot await here so doing it in the 'it' statements - let client = Token.createMint( - program.provider.connection, - wallet.payer, - provider.wallet.publicKey, - provider.wallet.publicKey, - 9, - TOKEN_PROGRAM_ID - ); - - it("Can create an associated token account", async () => { - const localClient = await client; - associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - localClient.publicKey, - provider.wallet.publicKey + it("Can embed programs into genesis from the Anchor.toml", async () => { + const pid = new anchor.web3.PublicKey( + "FtMNMKp9DZHKWUyVAsj3Q5QV8ow4P3fUPP7ZrWEQJzKr" ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: localClient.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, - }); - - const account = await localClient.getAccountInfo(associatedToken); - // @ts-expect-error - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(localClient.publicKey)); + let accInfo = await anchor.getProvider().connection.getAccountInfo(pid); + assert.isTrue(accInfo.executable); }); - it("Can validate associated_token constraints", async () => { - const localClient = await client; - await program.rpc.testValidateAssociatedToken({ + it("Can use the owner constraint", async () => { + await program.rpc.testOwner({ accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: provider.wallet.publicKey, + data: data.publicKey, + misc: program.programId, }, }); - let otherMint = await Token.createMint( - program.provider.connection, - wallet.payer, - provider.wallet.publicKey, - provider.wallet.publicKey, - 9, - TOKEN_PROGRAM_ID - ); - await nativeAssert.rejects( async () => { - await program.rpc.testValidateAssociatedToken({ + await program.rpc.testOwner({ accounts: { - token: associatedToken, - mint: otherMint.publicKey, - wallet: provider.wallet.publicKey, + data: provider.wallet.publicKey, + misc: program.programId, }, }); }, (err) => { - assert.strictEqual(err.error.errorCode.number, 2009); return true; } ); }); - it("associated_token constraints check do not allow authority change", async () => { - const localClient = await client; - await program.rpc.testValidateAssociatedToken({ + it("Can use the executable attribute", async () => { + await program.rpc.testExecutable({ accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: provider.wallet.publicKey, + program: program.programId, }, }); - await localClient.setAuthority( - associatedToken, - anchor.web3.Keypair.generate().publicKey, - "AccountOwner", - wallet.payer, - [] - ); - await nativeAssert.rejects( async () => { - await program.rpc.testValidateAssociatedToken({ + await program.rpc.testExecutable({ accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: provider.wallet.publicKey, + program: provider.wallet.publicKey, }, }); }, (err) => { - assert.strictEqual(err.error.errorCode.number, 2015); return true; } ); }); - }); - - it("Can fetch all accounts of a given type", async () => { - // Initialize the accounts. - const data1 = anchor.web3.Keypair.generate(); - const data2 = anchor.web3.Keypair.generate(); - const data3 = anchor.web3.Keypair.generate(); - const data4 = anchor.web3.Keypair.generate(); - // Initialize filterable data. - const filterable1 = anchor.web3.Keypair.generate().publicKey; - const filterable2 = anchor.web3.Keypair.generate().publicKey; - // Set up a secondary wallet and program. - const anotherProvider = new anchor.AnchorProvider( - program.provider.connection, - new anchor.Wallet(anchor.web3.Keypair.generate()), - { commitment: program.provider.connection.commitment } - ); - const anotherProgram = new anchor.Program( - miscIdl, - program.programId, - anotherProvider - ); - // Request airdrop for secondary wallet. - const signature = await program.provider.connection.requestAirdrop( - anotherProvider.wallet.publicKey, - anchor.web3.LAMPORTS_PER_SOL - ); - await program.provider.connection.confirmTransaction(signature); - // Create all the accounts. - await Promise.all([ - program.rpc.testFetchAll(filterable1, { - accounts: { - data: data1.publicKey, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data1], - }), - program.rpc.testFetchAll(filterable1, { - accounts: { - data: data2.publicKey, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data2], - }), - program.rpc.testFetchAll(filterable2, { + + 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: { - data: data3.publicKey, authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data3], - }), - anotherProgram.rpc.testFetchAll(filterable1, { - accounts: { - data: data4.publicKey, - authority: anotherProvider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, + cpiState: await misc2Program.state.address(), + misc2Program: misc2Program.programId, }, - signers: [data4], - }), - ]); - // Call for multiple kinds of .all. - const allAccounts = await program.account.dataWithFilter.all(); - const allAccountsFilteredByBuffer = - await program.account.dataWithFilter.all( - provider.wallet.publicKey.toBuffer() + }); + 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 = [ + `Program ${program.programId.toString()} invoke [1]`, + "Program log: Instruction: TestSimulate", + "Program data: NgyCA9omwbMsAAAA", + "Program data: fPhuIELK/k7SBAAA", + "Program data: jvbowsvlmkcJAAAA", + "Program data: zxM5neEnS1kBAgMEBQYHCAkK", + "Program data: g06Ei2GL1gIBAgMEBQYHCAkKCw==", + ]; + + assert.deepStrictEqual(expectedRaw, resp.raw.slice(0, -2)); + assert.strictEqual(resp.events[0].name, "E1"); + assert.strictEqual(resp.events[0].data.data, 44); + assert.strictEqual(resp.events[1].name, "E2"); + assert.strictEqual(resp.events[1].data.data, 1234); + assert.strictEqual(resp.events[2].name, "E3"); + assert.strictEqual(resp.events[2].data.data, 9); + assert.strictEqual(resp.events[3].name, "E5"); + assert.deepStrictEqual( + resp.events[3].data.data, + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ); + assert.strictEqual(resp.events[4].name, "E6"); + assert.deepStrictEqual( + resp.events[4].data.data, + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] ); - const allAccountsFilteredByProgramFilters1 = - await program.account.dataWithFilter.all([ - { - memcmp: { - offset: 8, - bytes: provider.wallet.publicKey.toBase58(), - }, - }, - { memcmp: { offset: 40, bytes: filterable1.toBase58() } }, - ]); - const allAccountsFilteredByProgramFilters2 = - await program.account.dataWithFilter.all([ - { - memcmp: { - offset: 8, - bytes: provider.wallet.publicKey.toBase58(), - }, - }, - { memcmp: { offset: 40, bytes: filterable2.toBase58() } }, - ]); - // Without filters there should be 4 accounts. - assert.lengthOf(allAccounts, 4); - // Filtering by main wallet there should be 3 accounts. - assert.lengthOf(allAccountsFilteredByBuffer, 3); - // Filtering all the main wallet accounts and matching the filterable1 value - // results in a 2 accounts. - assert.lengthOf(allAccountsFilteredByProgramFilters1, 2); - // Filtering all the main wallet accounts and matching the filterable2 value - // results in 1 account. - assert.lengthOf(allAccountsFilteredByProgramFilters2, 1); - }); - - it("Can use pdas with empty seeds", async () => { - const [pda, bump] = await PublicKey.findProgramAddress( - [], - program.programId - ); - - await program.rpc.testInitWithEmptySeeds({ - accounts: { - pda: pda, - authority: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, }); - await program.rpc.testEmptySeedsConstraint({ - accounts: { - pda: pda, - }, + + it("Can use enum in idl", async () => { + const resp1 = await program.methods + .testInputEnum({ first: {} }) + .simulate(); + const event1 = resp1.events[0].data as IdlEvents< + typeof program.idl + >["E7"]; + assert.deepEqual(event1.data.first, {}); + + const resp2 = await program.methods + .testInputEnum({ second: { x: new BN(1), y: new BN(2) } }) + .simulate(); + const event2 = resp2.events[0].data as IdlEvents< + typeof program.idl + >["E7"]; + assert.isTrue(new BN(1).eq(event2.data.second.x)); + assert.isTrue(new BN(2).eq(event2.data.second.y)); + + const resp3 = await program.methods + .testInputEnum({ + tupleStructTest: [ + { data1: 1, data2: 11, data3: 111, data4: new BN(1111) }, + ], + }) + .simulate(); + const event3 = resp3.events[0].data as IdlEvents< + typeof program.idl + >["E7"]; + assert.strictEqual(event3.data.tupleStructTest[0].data1, 1); + assert.strictEqual(event3.data.tupleStructTest[0].data2, 11); + assert.strictEqual(event3.data.tupleStructTest[0].data3, 111); + assert.isTrue(event3.data.tupleStructTest[0].data4.eq(new BN(1111))); + + const resp4 = await program.methods + .testInputEnum({ tupleTest: [1, 2, 3, 4] }) + .simulate(); + const event4 = resp4.events[0].data as IdlEvents< + typeof program.idl + >["E7"]; + assert.strictEqual(event4.data.tupleTest[0], 1); + assert.strictEqual(event4.data.tupleTest[1], 2); + assert.strictEqual(event4.data.tupleTest[2], 3); + assert.strictEqual(event4.data.tupleTest[3], 4); + }); + + let dataI8; + + it("Can use i8 in the idl", async () => { + dataI8 = anchor.web3.Keypair.generate(); + await program.rpc.testI8(-3, { + accounts: { + data: dataI8.publicKey, + }, + instructions: [await program.account.dataI8.createInstruction(dataI8)], + signers: [dataI8], + }); + const dataAccount = await program.account.dataI8.fetch(dataI8.publicKey); + assert.strictEqual(dataAccount.data, -3); }); - const [pda2] = await PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("non-empty")], - program.programId - ); - await nativeAssert.rejects( - program.rpc.testEmptySeedsConstraint({ + let dataPubkey; + + it("Can use i16 in the idl", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testI16(-2048, { accounts: { - pda: pda2, + data: data.publicKey, }, - }), - (err) => { - assert.equal(err.error.errorCode.number, 2006); - return true; - } - ); - }); - - const ifNeededAcc = anchor.web3.Keypair.generate(); - - it("Can init if needed a new account", async () => { - await program.rpc.testInitIfNeeded(1, { - accounts: { - data: ifNeededAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - signers: [ifNeededAcc], + instructions: [await program.account.dataI16.createInstruction(data)], + signers: [data], + }); + const dataAccount = await program.account.dataI16.fetch(data.publicKey); + assert.strictEqual(dataAccount.data, -2048); + + dataPubkey = data.publicKey; }); - const account = await program.account.dataU16.fetch(ifNeededAcc.publicKey); - assert.strictEqual(account.data, 1); - }); - - it("Can init if needed a previously created account", async () => { - await program.rpc.testInitIfNeeded(3, { - accounts: { - data: ifNeededAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - signers: [ifNeededAcc], + + it("Can use base58 strings to fetch an account", async () => { + const dataAccount = await program.account.dataI16.fetch( + dataPubkey.toString() + ); + assert.strictEqual(dataAccount.data, -2048); }); - const account = await program.account.dataU16.fetch(ifNeededAcc.publicKey); - assert.strictEqual(account.data, 3); - }); - it("Can use const for array size", async () => { - const data = anchor.web3.Keypair.generate(); - const tx = await program.rpc.testConstArraySize(99, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataConstArraySize.createInstruction(data), - ], + it("Should fail to close an account when sending lamports to itself", async () => { + try { + await program.rpc.testClose({ + accounts: { + data: data.publicKey, + solDest: data.publicKey, + }, + }); + expect(false).to.be.true; + } catch (err) { + const errMsg = "A close constraint was violated"; + assert.strictEqual(err.error.errorMessage, errMsg); + assert.strictEqual(err.error.errorCode.number, 2011); + } }); - const dataAccount = await program.account.dataConstArraySize.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, [99, ...new Array(9).fill(0)]); - }); - it("Can use const for instruction data size", async () => { - const data = anchor.web3.Keypair.generate(); - const dataArray = [99, ...new Array(9).fill(0)]; - const tx = await program.rpc.testConstIxDataSize(dataArray, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataConstArraySize.createInstruction(data), - ], + it("Can close an account", async () => { + const connection = program.provider.connection; + const openAccount = await connection.getAccountInfo(data.publicKey); + + assert.isNotNull(openAccount); + const openAccountBalance = openAccount.lamports; + // double balance to calculate closed balance correctly + const transferIx = anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: data.publicKey, + lamports: openAccountBalance, + }); + const transferTransaction = new anchor.web3.Transaction().add(transferIx); + await provider.sendAndConfirm(transferTransaction); + + let beforeBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; + + await program.methods + .testClose() + .accounts({ + data: data.publicKey, + solDest: provider.wallet.publicKey, + }) + .postInstructions([transferIx]) + .rpc(); + + let afterBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; + + // Retrieved rent exemption sol. + expect(afterBalance > beforeBalance).to.be.true; + + const closedAccount = await connection.getAccountInfo(data.publicKey); + + assert.isTrue(closedAccount.data.length === 0); + assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); }); - const dataAccount = await program.account.dataConstArraySize.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, dataArray); - }); - - it("Should include BASE const in IDL", async () => { - assert.isDefined( - miscIdl.constants.find( - (c) => c.name === "BASE" && c.type === "u128" && c.value === "1_000_000" - ) - ); - }); - - it("Should include DECIMALS const in IDL", async () => { - assert.isDefined( - miscIdl.constants.find( - (c) => c.name === "DECIMALS" && c.type === "u8" && c.value === "6" - ) - ); - }); - - it("Should include BYTES_STR const in IDL", async () => { - assert.isDefined( - miscIdl.constants.find( - (c) => - c.name === "BYTES_STR" && - c.type === "bytes" && - c.value === "[116, 101, 115, 116]" - ) - ); - }); - - it("Should include BYTE_STR const in IDL", async () => { - assert.isDefined( - miscIdl.constants.find( - (c) => c.name === "BYTE_STR" && c.type === "u8" && c.value === "116" - ) - ); - }); - - it("Should not include NO_IDL const in IDL", async () => { - assert.isUndefined(miscIdl.constants.find((c) => c.name === "NO_IDL")); - }); - - it("init_if_needed throws if account exists but is not owned by the expected program", async () => { - const newAcc = await anchor.web3.PublicKey.findProgramAddress( - [utf8.encode("hello")], - program.programId - ); - await program.rpc.testInitIfNeededChecksOwner({ - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - owner: program.programId, - }, + + it("Can close an account twice", async () => { + const data = anchor.web3.Keypair.generate(); + await program.methods + .initialize(new anchor.BN(10), new anchor.BN(10)) + .accounts({ data: data.publicKey }) + .preInstructions([await program.account.data.createInstruction(data)]) + .signers([data]) + .rpc(); + + const connection = program.provider.connection; + const openAccount = await connection.getAccountInfo(data.publicKey); + assert.isNotNull(openAccount); + + const openAccountBalance = openAccount.lamports; + // double balance to calculate closed balance correctly + const transferIx = anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: data.publicKey, + lamports: openAccountBalance, + }); + const transferTransaction = new anchor.web3.Transaction().add(transferIx); + await provider.sendAndConfirm(transferTransaction); + + let beforeBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; + + await program.methods + .testCloseTwice() + .accounts({ + data: data.publicKey, + solDest: provider.wallet.publicKey, + }) + .postInstructions([transferIx]) + .rpc(); + + let afterBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; + + // Retrieved rent exemption sol. + expect(afterBalance > beforeBalance).to.be.true; + + const closedAccount = await connection.getAccountInfo(data.publicKey); + assert.isTrue(closedAccount.data.length === 0); + assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); + }); + + it("Can close a mut account manually", async () => { + const data = anchor.web3.Keypair.generate(); + await program.methods + .initialize(new anchor.BN(10), new anchor.BN(10)) + .accounts({ data: data.publicKey }) + .preInstructions([await program.account.data.createInstruction(data)]) + .signers([data]) + .rpc(); + + const connection = program.provider.connection; + const openAccount = await connection.getAccountInfo(data.publicKey); + + assert.isNotNull(openAccount); + const openAccountBalance = openAccount.lamports; + // double balance to calculate closed balance correctly + const transferIx = anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: data.publicKey, + lamports: openAccountBalance, + }); + const transferTransaction = new anchor.web3.Transaction().add(transferIx); + await provider.sendAndConfirm(transferTransaction); + + let beforeBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; + + await program.methods + .testCloseMut() + .accounts({ + data: data.publicKey, + solDest: provider.wallet.publicKey, + }) + .postInstructions([transferIx]) + .rpc(); + + let afterBalance = ( + await connection.getAccountInfo(provider.wallet.publicKey) + ).lamports; + + // Retrieved rent exemption sol. + expect(afterBalance > beforeBalance).to.be.true; + + const closedAccount = await connection.getAccountInfo(data.publicKey); + assert.isTrue(closedAccount.data.length === 0); + assert.isTrue(closedAccount.owner.equals(SystemProgram.programId)); }); - try { - await program.rpc.testInitIfNeededChecksOwner({ + it("Can use instruction data in accounts constraints", async () => { + // b"my-seed" + const seed = Buffer.from([109, 121, 45, 115, 101, 101, 100]); + const [myPda, nonce] = await PublicKey.findProgramAddress( + [seed, anchor.web3.SYSVAR_RENT_PUBKEY.toBuffer()], + program.programId + ); + + await program.rpc.testInstructionConstraint(nonce, { accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - owner: anchor.web3.Keypair.generate().publicKey, + myPda, + myAccount: anchor.web3.SYSVAR_RENT_PUBKEY, }, }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2004); - } - }); - - it("init_if_needed throws if pda account exists but does not have the expected seeds", async () => { - const newAcc = await anchor.web3.PublicKey.findProgramAddress( - [utf8.encode("nothello")], - program.programId - ); - await program.rpc.testInitIfNeededChecksSeeds("nothello", { - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, }); - // this will throw if it is not a proper PDA - // we need this so we know that the following tx failed - // not because it couldn't create this pda - // but because the two pdas were different - anchor.web3.PublicKey.createProgramAddress( - [utf8.encode("hello")], - program.programId - ); - - try { - await program.rpc.testInitIfNeededChecksSeeds("hello", { + it("Can create a PDA account with instruction data", async () => { + const seed = Buffer.from([1, 2, 3, 4]); + const domain = "my-domain"; + const foo = anchor.web3.SYSVAR_RENT_PUBKEY; + const [myPda, nonce] = await PublicKey.findProgramAddress( + [ + Buffer.from(anchor.utils.bytes.utf8.encode("my-seed")), + Buffer.from(anchor.utils.bytes.utf8.encode(domain)), + foo.toBuffer(), + seed, + ], + program.programId + ); + + await program.rpc.testPdaInit(domain, seed, nonce, { accounts: { - data: newAcc[0], + myPda, + myPayer: provider.wallet.publicKey, + foo, systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, }, }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - }); - - it("init_if_needed throws if account exists but is not the expected space", async () => { - const newAcc = anchor.web3.Keypair.generate(); - const _irrelevantForTest = 3; - await program.rpc.initWithSpace(_irrelevantForTest, { - accounts: { - data: newAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, - }, - signers: [newAcc], + + const myPdaAccount = await program.account.dataU16.fetch(myPda); + assert.strictEqual(myPdaAccount.data, 6); }); - try { - await program.rpc.testInitIfNeeded(_irrelevantForTest, { + it("Can create a zero copy PDA account", async () => { + const [myPda, nonce] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], + program.programId + ); + await program.rpc.testPdaInitZeroCopy({ accounts: { - data: newAcc.publicKey, + myPda, + myPayer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, - payer: provider.wallet.publicKey, }, - signers: [newAcc], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2019); - } - }); - - it("init_if_needed throws if mint exists but has the wrong mint authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], + + const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); + assert.strictEqual(myPdaAccount.data, 9); + assert.strictEqual(myPdaAccount.bump, nonce); }); - try { - await program.rpc.testInitMintIfNeeded(6, { + it("Can write to a zero copy PDA account", async () => { + const [myPda, bump] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], + program.programId + ); + await program.rpc.testPdaMutZeroCopy({ accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - mintAuthority: anchor.web3.Keypair.generate().publicKey, - freezeAuthority: provider.wallet.publicKey, + myPda, + myPayer: provider.wallet.publicKey, }, - signers: [mint], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2016); - } - }); - - it("init_if_needed throws if mint exists but has the wrong freeze authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], + + const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); + assert.strictEqual(myPdaAccount.data, 1234); + assert.strictEqual(myPdaAccount.bump, bump); }); - try { - await program.rpc.testInitMintIfNeeded(6, { + it("Can create a token account from seeds pda", async () => { + const [mint, mint_bump] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-mint-seed"))], + program.programId + ); + const [myPda, token_bump] = await PublicKey.findProgramAddress( + [Buffer.from(anchor.utils.bytes.utf8.encode("my-token-seed"))], + program.programId + ); + await program.rpc.testTokenSeedsInit({ accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, + myPda, + mint, + authority: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: anchor.web3.Keypair.generate().publicKey, }, - signers: [mint], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2017); - } - }); - - it("init_if_needed throws if mint exists but has the wrong decimals", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], + + const mintAccount = new Token( + program.provider.connection, + mint, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await mintAccount.getAccountInfo(myPda); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint)); + }); + + it("Can execute a fallback function", async () => { + await nativeAssert.rejects( + async () => { + await anchor.utils.rpc.invoke(program.programId); + }, + (err) => { + assert.isTrue(err.toString().includes("custom program error: 0x4d2")); + return true; + } + ); }); - try { - await program.rpc.testInitMintIfNeeded(9, { + it("Can init a random account", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testInit({ accounts: { - mint: mint.publicKey, + data: data.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, }, - signers: [mint], + signers: [data], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2018); - } - }); - - it("init_if_needed throws if token exists but has the wrong owner", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], - }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [token], + const account = await program.account.dataI8.fetch(data.publicKey); + assert.strictEqual(account.data, 3); }); - try { - await program.rpc.testInitTokenIfNeeded({ + it("Can init a random account prefunded", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testInit({ accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - authority: anchor.web3.Keypair.generate().publicKey, - }, - signers: [token], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - } - }); - - it("init_if_needed throws if token exists but has the wrong mint", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], - }); - - const mint2 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint2.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint2], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [token], - }); - - try { - await program.rpc.testInitTokenIfNeeded({ - accounts: { - token: token.publicKey, - mint: mint2.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - authority: provider.wallet.publicKey, - }, - signers: [token], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2014); - } - }); - - it("init_if_needed throws if associated token exists but has the wrong owner", async () => { - const mint = Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], - }); - - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, - }); - - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ - accounts: { - token: associatedToken, - mint: mint.publicKey, + data: data.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: anchor.web3.Keypair.generate().publicKey, }, + signers: [data], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: data.publicKey, + lamports: 4039280, + }), + ], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - } - }); - - it("init_if_needed throws if associated token exists but has the wrong mint", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], - }); - const mint2 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint2.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint2], + const account = await program.account.dataI8.fetch(data.publicKey); + assert.strictEqual(account.data, 3); }); - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, + it("Should fail when trying to init the payer as a program account", async () => { + try { + await program.rpc.testInit({ + accounts: { + data: provider.wallet.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + }); + assert.fail("Transaction should fail"); + } catch (e) { + // "Error Code: TryingToInitPayerAsProgramAccount. Error Number: 4101. Error Message: You cannot/should not initialize the payer account as a program account." + assert.strictEqual(e.error.errorCode.number, 4101); + } }); - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ + it("Can init a random zero copy account", async () => { + const data = anchor.web3.Keypair.generate(); + await program.rpc.testInitZeroCopy({ accounts: { - token: associatedToken, - mint: mint2.publicKey, + data: data.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: provider.wallet.publicKey, }, + signers: [data], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2014); - } - }); - - it("init_if_needed throws if token exists with correct owner and mint but is not the ATA", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], - }); - - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, + const account = await program.account.dataZeroCopy.fetch(data.publicKey); + assert.strictEqual(account.data, 10); + assert.strictEqual(account.bump, 2); }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [token], - }); + let mint = undefined; - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ + it("Can create a random mint account", async () => { + mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ accounts: { - token: token.publicKey, mint: mint.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: provider.wallet.publicKey, }, + signers: [mint], }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 3014); - } - }); - - it("Can use multidimensional array", async () => { - const array2d = new Array(10).fill(new Array(10).fill(99)); - const data = anchor.web3.Keypair.generate(); - await program.rpc.testMultidimensionalArray(array2d, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataMultidimensionalArray.createInstruction(data), - ], - }); - const dataAccount = await program.account.dataMultidimensionalArray.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, array2d); - }); - - it("Can use multidimensional array with const sizes", async () => { - const array2d = new Array(10).fill(new Array(11).fill(22)); - const data = anchor.web3.Keypair.generate(); - await program.rpc.testMultidimensionalArrayConstSizes(array2d, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataMultidimensionalArrayConstSizes.createInstruction( - data - ), - ], - }); - const dataAccount = - await program.account.dataMultidimensionalArrayConstSizes.fetch( - data.publicKey + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) ); - assert.deepStrictEqual(dataAccount.data, array2d); - }); - - describe("Can validate PDAs derived from other program ids", () => { - it("With bumps using create_program_address", async () => { - const [firstPDA, firstBump] = - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - ASSOCIATED_TOKEN_PROGRAM_ID - ); - const [secondPDA, secondBump] = - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - program.programId - ); - - // correct bump but wrong address - const wrongAddress = anchor.web3.Keypair.generate().publicKey; - try { - await program.rpc.testProgramIdConstraint(firstBump, secondBump, { - accounts: { - first: wrongAddress, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // matching bump seed for wrong address but derived from wrong program - try { - await program.rpc.testProgramIdConstraint(secondBump, secondBump, { - accounts: { - first: secondPDA, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // correct inputs should lead to successful tx - await program.rpc.testProgramIdConstraint(firstBump, secondBump, { - accounts: { - first: firstPDA, - second: secondPDA, - }, - }); }); - it("With bumps using find_program_address", async () => { - const firstPDA = ( - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - ASSOCIATED_TOKEN_PROGRAM_ID - ) - )[0]; - const secondPDA = ( - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - program.programId - ) - )[0]; - - // random wrong address - const wrongAddress = anchor.web3.Keypair.generate().publicKey; - try { - await program.rpc.testProgramIdConstraintFindPda({ - accounts: { - first: wrongAddress, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // same seeds but derived from wrong program - try { - await program.rpc.testProgramIdConstraintFindPda({ - accounts: { - first: secondPDA, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // correct inputs should lead to successful tx - await program.rpc.testProgramIdConstraintFindPda({ - accounts: { - first: firstPDA, - second: secondPDA, - }, - }); - }); - }); - describe("Token Constraint Test", () => { - it("Token Constraint Test(no init) - Can make token::mint and token::authority", async () => { - const mint = anchor.web3.Keypair.generate(); + it("Can create a random mint account prefunded", async () => { + mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { mint: mint.publicKey, @@ -1760,8 +683,28 @@ describe("misc", () => { tokenProgram: TOKEN_PROGRAM_ID, }, signers: [mint], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: mint.publicKey, + lamports: 4039280, + }), + ], }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + }); + it("Can create a random token account", async () => { const token = anchor.web3.Keypair.generate(); await program.rpc.testInitToken({ accounts: { @@ -1773,36 +716,22 @@ describe("misc", () => { }, signers: [token], }); - await program.rpc.testTokenConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - }, - }); - const mintAccount = new Token( + const client = new Token( program.provider.connection, mint.publicKey, TOKEN_PROGRAM_ID, wallet.payer ); - const account = await mintAccount.getAccountInfo(token.publicKey); + const account = await client.getAccountInfo(token.publicKey); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); assert.isTrue(account.owner.equals(provider.wallet.publicKey)); assert.isTrue(account.mint.equals(mint.publicKey)); }); - it("Token Constraint Test(no init) - Can make only token::authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], - }); - + it("Can create a random token with prefunding", async () => { const token = anchor.web3.Keypair.generate(); await program.rpc.testInitToken({ accounts: { @@ -1813,36 +742,30 @@ describe("misc", () => { tokenProgram: TOKEN_PROGRAM_ID, }, signers: [token], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: token.publicKey, + lamports: 4039280, + }), + ], }); - await program.rpc.testOnlyAuthConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, - }, - }); - const mintAccount = new Token( + const client = new Token( program.provider.connection, mint.publicKey, TOKEN_PROGRAM_ID, wallet.payer ); - const account = await mintAccount.getAccountInfo(token.publicKey); + const account = await client.getAccountInfo(token.publicKey); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); }); - it("Token Constraint Test(no init) - Can make only token::mint", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], - }); - + it("Can create a random token with prefunding under the rent exemption", async () => { const token = anchor.web3.Keypair.generate(); await program.rpc.testInitToken({ accounts: { @@ -1853,165 +776,490 @@ describe("misc", () => { tokenProgram: TOKEN_PROGRAM_ID, }, signers: [token], + instructions: [ + anchor.web3.SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: token.publicKey, + lamports: 1, + }), + ], }); - await program.rpc.testOnlyMintConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - }, - }); - const mintAccount = new Token( + const client = new Token( program.provider.connection, mint.publicKey, TOKEN_PROGRAM_ID, wallet.payer ); - const account = await mintAccount.getAccountInfo(token.publicKey); + const account = await client.getAccountInfo(token.publicKey); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); assert.isTrue(account.mint.equals(mint.publicKey)); }); - it("Token Constraint Test(no init) - throws if token::mint mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], - }); + it("Can initialize multiple accounts via a composite payer", async () => { + const data1 = anchor.web3.Keypair.generate(); + const data2 = anchor.web3.Keypair.generate(); - const mint1 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint1.publicKey, - payer: provider.wallet.publicKey, + const tx = await program.methods + .testCompositePayer() + .accounts({ + composite: { + data: data1.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + data: data2.publicKey, systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint1], + }) + .signers([data1, data2]) + .rpc(); + + const account1 = await program.account.dataI8.fetch(data1.publicKey); + assert.strictEqual(account1.data, 1); + + const account2 = await program.account.data.fetch(data2.publicKey); + assert.strictEqual(account2.udata.toNumber(), 2); + assert.strictEqual(account2.idata.toNumber(), 3); + }); + + describe("associated_token constraints", () => { + let associatedToken = null; + // apparently cannot await here so doing it in the 'it' statements + let client = Token.createMint( + program.provider.connection, + wallet.payer, + provider.wallet.publicKey, + provider.wallet.publicKey, + 9, + TOKEN_PROGRAM_ID + ); + + it("Can create an associated token account", async () => { + const localClient = await client; + associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + localClient.publicKey, + provider.wallet.publicKey + ); + + await program.rpc.testInitAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + }, + }); + + const account = await localClient.getAccountInfo(associatedToken); + // @ts-expect-error + assert.strictEqual(account.state, 1); + assert.strictEqual(account.amount.toNumber(), 0); + assert.isTrue(account.isInitialized); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(localClient.publicKey)); }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ + it("Can validate associated_token constraints", async () => { + const localClient = await client; + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + + let otherMint = await Token.createMint( + program.provider.connection, + wallet.payer, + provider.wallet.publicKey, + provider.wallet.publicKey, + 9, + TOKEN_PROGRAM_ID + ); + + await nativeAssert.rejects( + async () => { + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: otherMint.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + }, + (err) => { + assert.strictEqual(err.error.errorCode.number, 2009); + return true; + } + ); + }); + + it("associated_token constraints check do not allow authority change", async () => { + const localClient = await client; + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + + await localClient.setAuthority( + associatedToken, + anchor.web3.Keypair.generate().publicKey, + "AccountOwner", + wallet.payer, + [] + ); + + await nativeAssert.rejects( + async () => { + await program.rpc.testValidateAssociatedToken({ + accounts: { + token: associatedToken, + mint: localClient.publicKey, + wallet: provider.wallet.publicKey, + }, + }); + }, + (err) => { + assert.strictEqual(err.error.errorCode.number, 2015); + return true; + } + ); + }); + }); + + it("Can fetch all accounts of a given type", async () => { + // Initialize the accounts. + const data1 = anchor.web3.Keypair.generate(); + const data2 = anchor.web3.Keypair.generate(); + const data3 = anchor.web3.Keypair.generate(); + const data4 = anchor.web3.Keypair.generate(); + // Initialize filterable data. + const filterable1 = anchor.web3.Keypair.generate().publicKey; + const filterable2 = anchor.web3.Keypair.generate().publicKey; + // Set up a secondary wallet and program. + const anotherProvider = new anchor.AnchorProvider( + program.provider.connection, + new anchor.Wallet(anchor.web3.Keypair.generate()), + { commitment: program.provider.connection.commitment } + ); + const anotherProgram = new anchor.Program( + miscIdl, + program.programId, + anotherProvider + ); + // Request airdrop for secondary wallet. + const signature = await program.provider.connection.requestAirdrop( + anotherProvider.wallet.publicKey, + anchor.web3.LAMPORTS_PER_SOL + ); + await program.provider.connection.confirmTransaction(signature); + // Create all the accounts. + await Promise.all([ + program.rpc.testFetchAll(filterable1, { + accounts: { + data: data1.publicKey, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data1], + }), + program.rpc.testFetchAll(filterable1, { + accounts: { + data: data2.publicKey, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data2], + }), + program.rpc.testFetchAll(filterable2, { + accounts: { + data: data3.publicKey, + authority: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data3], + }), + anotherProgram.rpc.testFetchAll(filterable1, { + accounts: { + data: data4.publicKey, + authority: anotherProvider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }, + signers: [data4], + }), + ]); + // Call for multiple kinds of .all. + const allAccounts = await program.account.dataWithFilter.all(); + const allAccountsFilteredByBuffer = + await program.account.dataWithFilter.all( + provider.wallet.publicKey.toBuffer() + ); + const allAccountsFilteredByProgramFilters1 = + await program.account.dataWithFilter.all([ + { + memcmp: { + offset: 8, + bytes: provider.wallet.publicKey.toBase58(), + }, + }, + { memcmp: { offset: 40, bytes: filterable1.toBase58() } }, + ]); + const allAccountsFilteredByProgramFilters2 = + await program.account.dataWithFilter.all([ + { + memcmp: { + offset: 8, + bytes: provider.wallet.publicKey.toBase58(), + }, + }, + { memcmp: { offset: 40, bytes: filterable2.toBase58() } }, + ]); + // Without filters there should be 4 accounts. + assert.lengthOf(allAccounts, 4); + // Filtering by main wallet there should be 3 accounts. + assert.lengthOf(allAccountsFilteredByBuffer, 3); + // Filtering all the main wallet accounts and matching the filterable1 value + // results in a 2 accounts. + assert.lengthOf(allAccountsFilteredByProgramFilters1, 2); + // Filtering all the main wallet accounts and matching the filterable2 value + // results in 1 account. + assert.lengthOf(allAccountsFilteredByProgramFilters2, 1); + }); + + it("Can use pdas with empty seeds", async () => { + const [pda, bump] = await PublicKey.findProgramAddress( + [], + program.programId + ); + + await program.rpc.testInitWithEmptySeeds({ accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, + pda: pda, + authority: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, }, - signers: [token], }); - try { - await program.rpc.testTokenConstraint({ + await program.rpc.testEmptySeedsConstraint({ + accounts: { + pda: pda, + }, + }); + + const [pda2] = await PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("non-empty")], + program.programId + ); + await nativeAssert.rejects( + program.rpc.testEmptySeedsConstraint({ accounts: { - token: token.publicKey, - mint: mint1.publicKey, - payer: provider.wallet.publicKey, + pda: pda2, }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2014); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenMint"); - } + }), + (err) => { + assert.equal(err.error.errorCode.number, 2006); + return true; + } + ); }); - it("Token Constraint Test(no init) - throws if token::authority mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ + const ifNeededAcc = anchor.web3.Keypair.generate(); + + it("Can init if needed a new account", async () => { + await program.rpc.testInitIfNeeded(1, { accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, + data: ifNeededAcc.publicKey, systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, + payer: provider.wallet.publicKey, }, - signers: [mint], + signers: [ifNeededAcc], }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ + const account = await program.account.dataU16.fetch( + ifNeededAcc.publicKey + ); + assert.strictEqual(account.data, 1); + }); + + it("Can init if needed a previously created account", async () => { + await program.rpc.testInitIfNeeded(3, { accounts: { - token: token.publicKey, - mint: mint.publicKey, + data: ifNeededAcc.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, payer: provider.wallet.publicKey, + }, + signers: [ifNeededAcc], + }); + const account = await program.account.dataU16.fetch( + ifNeededAcc.publicKey + ); + assert.strictEqual(account.data, 3); + }); + + it("Can use const for array size", async () => { + const data = anchor.web3.Keypair.generate(); + const tx = await program.rpc.testConstArraySize(99, { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [ + await program.account.dataConstArraySize.createInstruction(data), + ], + }); + const dataAccount = await program.account.dataConstArraySize.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, [99, ...new Array(9).fill(0)]); + }); + + it("Can use const for instruction data size", async () => { + const data = anchor.web3.Keypair.generate(); + const dataArray = [99, ...new Array(9).fill(0)]; + const tx = await program.rpc.testConstIxDataSize(dataArray, { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [ + await program.account.dataConstArraySize.createInstruction(data), + ], + }); + const dataAccount = await program.account.dataConstArraySize.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, dataArray); + }); + + it("Should include BASE const in IDL", async () => { + assert.isDefined( + miscIdl.constants.find( + (c) => + c.name === "BASE" && c.type === "u128" && c.value === "1_000_000" + ) + ); + }); + + it("Should include DECIMALS const in IDL", async () => { + assert.isDefined( + miscIdl.constants.find( + (c) => c.name === "DECIMALS" && c.type === "u8" && c.value === "6" + ) + ); + }); + + it("Should not include NO_IDL const in IDL", async () => { + assert.isUndefined(miscIdl.constants.find((c) => c.name === "NO_IDL")); + }); + + it("init_if_needed throws if account exists but is not owned by the expected program", async () => { + const newAcc = await anchor.web3.PublicKey.findProgramAddress( + [utf8.encode("hello")], + program.programId + ); + await program.rpc.testInitIfNeededChecksOwner({ + accounts: { + data: newAcc[0], systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, + payer: provider.wallet.publicKey, + owner: program.programId, }, - signers: [token], }); - const fakeAuthority = Keypair.generate(); + try { - await program.rpc.testTokenAuthConstraint({ + await program.rpc.testInitIfNeededChecksOwner({ accounts: { - token: token.publicKey, - mint: mint.publicKey, - fakeAuthority: fakeAuthority.publicKey, + data: newAcc[0], + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + owner: anchor.web3.Keypair.generate().publicKey, }, }); - assert.isTrue(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); + assert.strictEqual(err.error.errorCode.number, 2004); } }); - it("Token Constraint Test(no init) - throws if both token::authority, token::mint mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ + it("init_if_needed throws if pda account exists but does not have the expected seeds", async () => { + const newAcc = await anchor.web3.PublicKey.findProgramAddress( + [utf8.encode("nothello")], + program.programId + ); + await program.rpc.testInitIfNeededChecksSeeds("nothello", { accounts: { - mint: mint.publicKey, - payer: provider.wallet.publicKey, + data: newAcc[0], systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [mint], - }); - const mint1 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint1.publicKey, payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, }, - signers: [mint1], }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ + + // this will throw if it is not a proper PDA + // we need this so we know that the following tx failed + // not because it couldn't create this pda + // but because the two pdas were different + anchor.web3.PublicKey.createProgramAddress( + [utf8.encode("hello")], + program.programId + ); + + try { + await program.rpc.testInitIfNeededChecksSeeds("hello", { + accounts: { + data: newAcc[0], + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + }); + + it("init_if_needed throws if account exists but is not the expected space", async () => { + const newAcc = anchor.web3.Keypair.generate(); + const _irrelevantForTest = 3; + await program.rpc.initWithSpace(_irrelevantForTest, { accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: provider.wallet.publicKey, + data: newAcc.publicKey, systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, + payer: provider.wallet.publicKey, }, - signers: [token], + signers: [newAcc], }); - const fakeAuthority = Keypair.generate(); + try { - await program.rpc.testTokenAuthConstraint({ + await program.rpc.testInitIfNeeded(_irrelevantForTest, { accounts: { - token: token.publicKey, - mint: mint1.publicKey, - fakeAuthority: fakeAuthority.publicKey, + data: newAcc.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + payer: provider.wallet.publicKey, }, + signers: [newAcc], }); - assert.isTrue(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); + assert.strictEqual(err.error.errorCode.number, 2019); } }); - it("Mint Constraint Test(no init) - mint::decimals, mint::authority, mint::freeze_authority", async () => { + it("init_if_needed throws if mint exists but has the wrong mint authority", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -2022,30 +1270,28 @@ describe("misc", () => { }, signers: [mint], }); - await program.rpc.testMintConstraint(6, { - accounts: { - mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue( - mintAccount.mintAuthority.equals(provider.wallet.publicKey) - ); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) - ); + + try { + await program.rpc.testInitMintIfNeeded(6, { + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + mintAuthority: anchor.web3.Keypair.generate().publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + signers: [mint], + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2016); + } }); - it("Mint Constraint Test(no init) - throws if mint::decimals mismatch", async () => { + it("init_if_needed throws if mint exists but has the wrong freeze authority", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -2056,25 +1302,28 @@ describe("misc", () => { }, signers: [mint], }); - const fakeDecimal = 5; + try { - await program.rpc.testMintConstraint(fakeDecimal, { + await program.rpc.testInitMintIfNeeded(6, { accounts: { mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, + freezeAuthority: anchor.web3.Keypair.generate().publicKey, }, + signers: [mint], }); - assert.isTrue(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2018); - assert.strictEqual(err.error.errorCode.code, "ConstraintMintDecimals"); + assert.strictEqual(err.error.errorCode.number, 2017); } }); - it("Mint Constraint Test(no init) - throws if mint::mint_authority mismatch", async () => { + it("init_if_needed throws if mint exists but has the wrong decimals", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -2086,28 +1335,27 @@ describe("misc", () => { signers: [mint], }); - const fakeAuthority = Keypair.generate(); try { - await program.rpc.testMintConstraint(6, { + await program.rpc.testInitMintIfNeeded(9, { accounts: { mint: mint.publicKey, - mintAuthority: fakeAuthority.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + mintAuthority: provider.wallet.publicKey, freezeAuthority: provider.wallet.publicKey, }, + signers: [mint], }); - assert.isTrue(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2016); - assert.strictEqual( - err.error.errorCode.code, - "ConstraintMintMintAuthority" - ); + assert.strictEqual(err.error.errorCode.number, 2018); } }); - it("Mint Constraint Test(no init) - throws if mint::freeze_authority mismatch", async () => { + it("init_if_needed throws if token exists but has the wrong owner", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -2119,28 +1367,39 @@ describe("misc", () => { signers: [mint], }); - const fakeAuthority = Keypair.generate(); + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [token], + }); + try { - await program.rpc.testMintConstraint(6, { + await program.rpc.testInitTokenIfNeeded({ accounts: { + token: token.publicKey, mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: fakeAuthority.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + authority: anchor.web3.Keypair.generate().publicKey, }, + signers: [token], }); - assert.isTrue(false); + expect(false).to.be.true; } catch (_err) { assert.isTrue(_err instanceof AnchorError); const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2017); - assert.strictEqual( - err.error.errorCode.code, - "ConstraintMintFreezeAuthority" - ); + assert.strictEqual(err.error.errorCode.number, 2015); } }); - it("Mint Constraint Test(no init) - can write only mint::decimals", async () => { + it("init_if_needed throws if token exists but has the wrong mint", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -2152,56 +1411,100 @@ describe("misc", () => { signers: [mint], }); - await program.rpc.testMintOnlyDecimalsConstraint(6, { + const mint2 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ accounts: { - mint: mint.publicKey, + mint: mint2.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, }, + signers: [mint2], }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - }); - it("Mint Constraint Test(no init) - can write only mint::authority and mint::freeze_authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ accounts: { + token: token.publicKey, mint: mint.publicKey, payer: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, }, - signers: [mint], + signers: [token], }); - await program.rpc.testMintOnlyAuthConstraint({ + try { + await program.rpc.testInitTokenIfNeeded({ + accounts: { + token: token.publicKey, + mint: mint2.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + authority: provider.wallet.publicKey, + }, + signers: [token], + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2014); + } + }); + + it("init_if_needed throws if associated token exists but has the wrong owner", async () => { + const mint = Keypair.generate(); + await program.rpc.testInitMint({ accounts: { mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, - freezeAuthority: provider.wallet.publicKey, + payer: provider.wallet.publicKey, + systemProgram: SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, }, + signers: [mint], }); - const client = new Token( - program.provider.connection, - mint.publicKey, + + const associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.isTrue( - mintAccount.mintAuthority.equals(provider.wallet.publicKey) - ); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + mint.publicKey, + provider.wallet.publicKey ); + + await program.rpc.testInitAssociatedToken({ + accounts: { + token: associatedToken, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + }, + }); + + try { + await program.rpc.testInitAssociatedTokenIfNeeded({ + accounts: { + token: associatedToken, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + authority: anchor.web3.Keypair.generate().publicKey, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); + } }); - it("Mint Constraint Test(no init) - can write only mint::authority", async () => { + it("init_if_needed throws if associated token exists but has the wrong mint", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -2213,25 +1516,57 @@ describe("misc", () => { signers: [mint], }); - await program.rpc.testMintOnlyOneAuthConstraint({ + const mint2 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ accounts: { - mint: mint.publicKey, - mintAuthority: provider.wallet.publicKey, + mint: mint2.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, }, + signers: [mint2], }); - const client = new Token( - program.provider.connection, - mint.publicKey, + + const associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.isTrue( - mintAccount.mintAuthority.equals(provider.wallet.publicKey) + mint.publicKey, + provider.wallet.publicKey ); + + const txn = await program.rpc.testInitAssociatedToken({ + accounts: { + token: associatedToken, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + }, + }); + console.log("InitAssocToken:", txn); + + try { + await program.rpc.testInitAssociatedTokenIfNeeded({ + accounts: { + token: associatedToken, + mint: mint2.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + authority: provider.wallet.publicKey, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2014); + } }); - it("Mint Constraint Test(no init) - can write only mint::decimals and mint::freeze_authority", async () => { + it("init_if_needed throws if token exists with correct owner and mint but is not the ATA", async () => { const mint = anchor.web3.Keypair.generate(); await program.rpc.testInitMint({ accounts: { @@ -2243,38 +1578,745 @@ describe("misc", () => { signers: [mint], }); - await program.rpc.testMintMissMintAuthConstraint(6, { + const associatedToken = await Token.getAssociatedTokenAddress( + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + mint.publicKey, + provider.wallet.publicKey + ); + + await program.rpc.testInitAssociatedToken({ accounts: { + token: associatedToken, mint: mint.publicKey, - freezeAuthority: provider.wallet.publicKey, + payer: provider.wallet.publicKey, + + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, }, }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue( - mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [token], + }); + + try { + await program.rpc.testInitAssociatedTokenIfNeeded({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + authority: provider.wallet.publicKey, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 3014); + } + }); + + it("Can use multidimensional array", async () => { + const array2d = new Array(10).fill(new Array(10).fill(99)); + const data = anchor.web3.Keypair.generate(); + await program.rpc.testMultidimensionalArray(array2d, { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [ + await program.account.dataMultidimensionalArray.createInstruction( + data + ), + ], + }); + const dataAccount = await program.account.dataMultidimensionalArray.fetch( + data.publicKey ); + assert.deepStrictEqual(dataAccount.data, array2d); + }); + + it("Can use multidimensional array with const sizes", async () => { + const array2d = new Array(10).fill(new Array(11).fill(22)); + const data = anchor.web3.Keypair.generate(); + await program.rpc.testMultidimensionalArrayConstSizes(array2d, { + accounts: { + data: data.publicKey, + }, + signers: [data], + instructions: [ + await program.account.dataMultidimensionalArrayConstSizes.createInstruction( + data + ), + ], + }); + const dataAccount = + await program.account.dataMultidimensionalArrayConstSizes.fetch( + data.publicKey + ); + assert.deepStrictEqual(dataAccount.data, array2d); + }); + + describe("Can validate PDAs derived from other program ids", () => { + it("With bumps using create_program_address", async () => { + const [firstPDA, firstBump] = + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + ASSOCIATED_TOKEN_PROGRAM_ID + ); + const [secondPDA, secondBump] = + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + program.programId + ); + + // correct bump but wrong address + const wrongAddress = anchor.web3.Keypair.generate().publicKey; + try { + await program.rpc.testProgramIdConstraint(firstBump, secondBump, { + accounts: { + first: wrongAddress, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // matching bump seed for wrong address but derived from wrong program + try { + await program.rpc.testProgramIdConstraint(secondBump, secondBump, { + accounts: { + first: secondPDA, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // correct inputs should lead to successful tx + await program.rpc.testProgramIdConstraint(firstBump, secondBump, { + accounts: { + first: firstPDA, + second: secondPDA, + }, + }); + }); + + it("With bumps using find_program_address", async () => { + const firstPDA = ( + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + ASSOCIATED_TOKEN_PROGRAM_ID + ) + )[0]; + const secondPDA = ( + await anchor.web3.PublicKey.findProgramAddress( + [anchor.utils.bytes.utf8.encode("seed")], + program.programId + ) + )[0]; + + // random wrong address + const wrongAddress = anchor.web3.Keypair.generate().publicKey; + try { + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: wrongAddress, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // same seeds but derived from wrong program + try { + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: secondPDA, + second: secondPDA, + }, + }); + expect(false).to.be.true; + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2006); + } + + // correct inputs should lead to successful tx + await program.rpc.testProgramIdConstraintFindPda({ + accounts: { + first: firstPDA, + second: secondPDA, + }, + }); + }); }); - it("check versioned transaction is now available", async () => { - let thisTx = new VersionedTransaction( - new Message({ - header: { - numReadonlySignedAccounts: 0, - numReadonlyUnsignedAccounts: 0, - numRequiredSignatures: 0, + describe("Token Constraint Test", () => { + it("Token Constraint Test(no init) - Can make token::mint and token::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, }, - accountKeys: [new PublicKey([0]).toString()], - instructions: [{ accounts: [0], data: "", programIdIndex: 0 }], - recentBlockhash: "", - }) - ); - assert.isDefined(thisTx); + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [token], + }); + await program.rpc.testTokenConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + assert.isTrue(account.mint.equals(mint.publicKey)); + }); + + it("Token Constraint Test(no init) - Can make only token::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [token], + }); + await program.rpc.testOnlyAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.isTrue(account.owner.equals(provider.wallet.publicKey)); + }); + + it("Token Constraint Test(no init) - Can make only token::mint", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [token], + }); + await program.rpc.testOnlyMintConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + }, + }); + const mintAccount = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const account = await mintAccount.getAccountInfo(token.publicKey); + assert.isTrue(account.mint.equals(mint.publicKey)); + }); + + it("Token Constraint Test(no init) - throws if token::mint mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + + const mint1 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint1.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint1], + }); + + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [token], + }); + try { + await program.rpc.testTokenConstraint({ + accounts: { + token: token.publicKey, + mint: mint1.publicKey, + payer: provider.wallet.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2014); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenMint"); + } + }); + + it("Token Constraint Test(no init) - throws if token::authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [token], + }); + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testTokenAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + fakeAuthority: fakeAuthority.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); + } + }); + + it("Token Constraint Test(no init) - throws if both token::authority, token::mint mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + const mint1 = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint1.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint1], + }); + const token = anchor.web3.Keypair.generate(); + await program.rpc.testInitToken({ + accounts: { + token: token.publicKey, + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [token], + }); + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testTokenAuthConstraint({ + accounts: { + token: token.publicKey, + mint: mint1.publicKey, + fakeAuthority: fakeAuthority.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2015); + assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); + } + }); + + it("Mint Constraint Test(no init) - mint::decimals, mint::authority, mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + ); + }); + + it("Mint Constraint Test(no init) - throws if mint::decimals mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + const fakeDecimal = 5; + try { + await program.rpc.testMintConstraint(fakeDecimal, { + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2018); + assert.strictEqual( + err.error.errorCode.code, + "ConstraintMintDecimals" + ); + } + }); + + it("Mint Constraint Test(no init) - throws if mint::mint_authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: fakeAuthority.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2016); + assert.strictEqual( + err.error.errorCode.code, + "ConstraintMintMintAuthority" + ); + } + }); + + it("Mint Constraint Test(no init) - throws if mint::freeze_authority mismatch", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + + const fakeAuthority = Keypair.generate(); + try { + await program.rpc.testMintConstraint(6, { + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: fakeAuthority.publicKey, + }, + }); + assert.isTrue(false); + } catch (_err) { + assert.isTrue(_err instanceof AnchorError); + const err: AnchorError = _err; + assert.strictEqual(err.error.errorCode.number, 2017); + assert.strictEqual( + err.error.errorCode.code, + "ConstraintMintFreezeAuthority" + ); + } + }); + + it("Mint Constraint Test(no init) - can write only mint::decimals", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + + await program.rpc.testMintOnlyDecimalsConstraint(6, { + accounts: { + mint: mint.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + }); + + it("Mint Constraint Test(no init) - can write only mint::authority and mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + + await program.rpc.testMintOnlyAuthConstraint({ + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + ); + }); + + it("Mint Constraint Test(no init) - can write only mint::authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + + await program.rpc.testMintOnlyOneAuthConstraint({ + accounts: { + mint: mint.publicKey, + mintAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.isTrue( + mintAccount.mintAuthority.equals(provider.wallet.publicKey) + ); + }); + + it("Mint Constraint Test(no init) - can write only mint::decimals and mint::freeze_authority", async () => { + const mint = anchor.web3.Keypair.generate(); + await program.rpc.testInitMint({ + accounts: { + mint: mint.publicKey, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + tokenProgram: TOKEN_PROGRAM_ID, + }, + signers: [mint], + }); + + await program.rpc.testMintMissMintAuthConstraint(6, { + accounts: { + mint: mint.publicKey, + freezeAuthority: provider.wallet.publicKey, + }, + }); + const client = new Token( + program.provider.connection, + mint.publicKey, + TOKEN_PROGRAM_ID, + wallet.payer + ); + const mintAccount = await client.getMintInfo(); + assert.strictEqual(mintAccount.decimals, 6); + assert.isTrue( + mintAccount.freezeAuthority.equals(provider.wallet.publicKey) + ); + }); + it("check versioned transaction is now available", async () => { + let thisTx = new VersionedTransaction( + new Message({ + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 0, + numRequiredSignatures: 0, + }, + accountKeys: [new PublicKey([0]).toString()], + instructions: [{ accounts: [0], data: "", programIdIndex: 0 }], + recentBlockhash: "", + }) + ); + assert.isDefined(thisTx); + }); }); - }); -}); + }; +}; + +export default miscTest; + +describe("misc", miscTest(anchor.workspace.Misc as Program)); + +describe( + "misc-optional", + miscTest(anchor.workspace.MiscOptional as Program) +); diff --git a/tests/multisig/programs/multisig/Cargo.toml b/tests/multisig/programs/multisig/Cargo.toml index 99a164258b..7d3cb4f051 100644 --- a/tests/multisig/programs/multisig/Cargo.toml +++ b/tests/multisig/programs/multisig/Cargo.toml @@ -2,7 +2,7 @@ name = "multisig" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/pda-derivation/programs/pda-derivation/Cargo.toml b/tests/pda-derivation/programs/pda-derivation/Cargo.toml index 09c3b500bc..1592a4c33b 100644 --- a/tests/pda-derivation/programs/pda-derivation/Cargo.toml +++ b/tests/pda-derivation/programs/pda-derivation/Cargo.toml @@ -2,7 +2,7 @@ name = "pda-derivation" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/pyth/programs/pyth/Cargo.toml b/tests/pyth/programs/pyth/Cargo.toml index 32989d6b3d..2d83d93e7b 100644 --- a/tests/pyth/programs/pyth/Cargo.toml +++ b/tests/pyth/programs/pyth/Cargo.toml @@ -2,7 +2,7 @@ name = "pyth" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/relations-derivation/programs/relations-derivation/Cargo.toml b/tests/relations-derivation/programs/relations-derivation/Cargo.toml index 03544ce9c4..cabd3b855b 100644 --- a/tests/relations-derivation/programs/relations-derivation/Cargo.toml +++ b/tests/relations-derivation/programs/relations-derivation/Cargo.toml @@ -2,7 +2,7 @@ name = "relations-derivation" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/spl/token-proxy/programs/token-proxy/Cargo.toml b/tests/spl/token-proxy/programs/token-proxy/Cargo.toml index 9aea4d798e..d84572afa4 100644 --- a/tests/spl/token-proxy/programs/token-proxy/Cargo.toml +++ b/tests/spl/token-proxy/programs/token-proxy/Cargo.toml @@ -2,7 +2,7 @@ name = "token-proxy" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/swap/programs/swap/Cargo.toml b/tests/swap/programs/swap/Cargo.toml index 5fbb3964d1..28d1d145b7 100644 --- a/tests/swap/programs/swap/Cargo.toml +++ b/tests/swap/programs/swap/Cargo.toml @@ -2,7 +2,7 @@ name = "swap" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/system-accounts/programs/system-accounts/Cargo.toml b/tests/system-accounts/programs/system-accounts/Cargo.toml index dff6b21d6d..968b976fd5 100644 --- a/tests/system-accounts/programs/system-accounts/Cargo.toml +++ b/tests/system-accounts/programs/system-accounts/Cargo.toml @@ -2,7 +2,7 @@ name = "system-accounts" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/sysvars/programs/sysvars/Cargo.toml b/tests/sysvars/programs/sysvars/Cargo.toml index 82f9726ac9..1acfea7b41 100644 --- a/tests/sysvars/programs/sysvars/Cargo.toml +++ b/tests/sysvars/programs/sysvars/Cargo.toml @@ -2,7 +2,7 @@ name = "sysvars" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/tictactoe/programs/tictactoe/Cargo.toml b/tests/tictactoe/programs/tictactoe/Cargo.toml index e7a016d8a4..914d56b70e 100644 --- a/tests/tictactoe/programs/tictactoe/Cargo.toml +++ b/tests/tictactoe/programs/tictactoe/Cargo.toml @@ -2,7 +2,7 @@ name = "tictactoe" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/typescript/programs/shared/Cargo.toml b/tests/typescript/programs/shared/Cargo.toml index b8fb9b9c18..c95b4a603f 100644 --- a/tests/typescript/programs/shared/Cargo.toml +++ b/tests/typescript/programs/shared/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "shared" version = "0.1.0" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/typescript/programs/typescript/Cargo.toml b/tests/typescript/programs/typescript/Cargo.toml index 4475fb56c7..2ea32b293f 100644 --- a/tests/typescript/programs/typescript/Cargo.toml +++ b/tests/typescript/programs/typescript/Cargo.toml @@ -2,7 +2,7 @@ name = "typescript" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/zero-copy/programs/shared/Cargo.toml b/tests/zero-copy/programs/shared/Cargo.toml index b8fb9b9c18..c95b4a603f 100644 --- a/tests/zero-copy/programs/shared/Cargo.toml +++ b/tests/zero-copy/programs/shared/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "shared" version = "0.1.0" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tests/zero-copy/programs/zero-copy/Cargo.toml b/tests/zero-copy/programs/zero-copy/Cargo.toml index ec388ff40f..05b012bea5 100644 --- a/tests/zero-copy/programs/zero-copy/Cargo.toml +++ b/tests/zero-copy/programs/zero-copy/Cargo.toml @@ -2,7 +2,7 @@ name = "zero-copy" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] diff --git a/tests/zero-copy/programs/zero-cpi/Cargo.toml b/tests/zero-copy/programs/zero-cpi/Cargo.toml index 158dae7226..eb9da9db8f 100644 --- a/tests/zero-copy/programs/zero-cpi/Cargo.toml +++ b/tests/zero-copy/programs/zero-cpi/Cargo.toml @@ -2,7 +2,7 @@ name = "zero-cpi" version = "0.1.0" description = "Created with Anchor" -rust-version = "1.62" +rust-version = "1.59" edition = "2021" [lib] From 0baf2c65279e766dab0944509e8d34938e490838 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 29 Nov 2022 15:53:51 -0600 Subject: [PATCH 097/109] prevent Option on composite accounts --- lang/syn/src/parser/accounts/mod.rs | 7 +++++++ tests/misc/programs/misc-optional/src/context.rs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lang/syn/src/parser/accounts/mod.rs b/lang/syn/src/parser/accounts/mod.rs index 26cd20d8c6..b5def650d7 100644 --- a/lang/syn/src/parser/accounts/mod.rs +++ b/lang/syn/src/parser/accounts/mod.rs @@ -260,6 +260,13 @@ pub fn parse_account_field(f: &syn::Field) -> ParseResult { }) } false => { + let (_, optional, _) = ident_string(f)?; + if optional { + return Err(ParseError::new( + f.ty.span(), + "Cannot have Optional composite accounts", + )); + } let account_constraints = constraints::parse(f, None)?; AccountField::CompositeField(CompositeField { ident, diff --git a/tests/misc/programs/misc-optional/src/context.rs b/tests/misc/programs/misc-optional/src/context.rs index 663792c2df..4f75a1019c 100644 --- a/tests/misc/programs/misc-optional/src/context.rs +++ b/tests/misc/programs/misc-optional/src/context.rs @@ -219,7 +219,7 @@ pub struct TestI8<'info> { #[derive(Accounts)] pub struct TestCompositePayer<'info> { - pub composite: Option>, + pub composite: TestInit<'info>, #[account(init, payer = payer.as_ref().unwrap(), space = Data::LEN + 8)] pub data: Option>, pub payer: Option>, From f19fb5b7385d9f2ec8d4df4e6c2e1e0cc022f38e Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 6 Dec 2022 02:15:16 -0600 Subject: [PATCH 098/109] hopefully fixed ts stuff? --- ts/build-packages.sh | 0 ts/packages/anchor/src/idl.ts | 6 ++ .../anchor/src/program/accounts-resolver.ts | 70 +++++++++++++------ .../src/program/namespace/instruction.ts | 31 ++++---- .../anchor/src/program/namespace/methods.ts | 48 ++++++------- 5 files changed, 88 insertions(+), 67 deletions(-) mode change 100644 => 100755 ts/build-packages.sh diff --git a/ts/build-packages.sh b/ts/build-packages.sh old mode 100644 new mode 100755 diff --git a/ts/packages/anchor/src/idl.ts b/ts/packages/anchor/src/idl.ts index 7ac0fb7707..488fdad981 100644 --- a/ts/packages/anchor/src/idl.ts +++ b/ts/packages/anchor/src/idl.ts @@ -52,6 +52,12 @@ export type IdlStateMethod = IdlInstruction; export type IdlAccountItem = IdlAccount | IdlAccounts; +export function isIdlAccounts( + accountItem: IdlAccountItem +): accountItem is IdlAccounts { + return "accounts" in accountItem; +} + export type IdlAccount = { name: string; isMut: boolean; diff --git a/ts/packages/anchor/src/program/accounts-resolver.ts b/ts/packages/anchor/src/program/accounts-resolver.ts index f732927941..2c743151bf 100644 --- a/ts/packages/anchor/src/program/accounts-resolver.ts +++ b/ts/packages/anchor/src/program/accounts-resolver.ts @@ -22,17 +22,26 @@ import Provider from "../provider.js"; import { AccountNamespace } from "./namespace/account.js"; import { BorshAccountsCoder } from "src/coder/index.js"; import { decodeTokenAccount } from "./token-account-layout"; -import { Program } from "./index.js"; +import { Program, translateAddress } from "./index.js"; +import { isPartialAccounts, PartialAccounts } from "./namespace/methods"; -type Accounts = { [name: string]: PublicKey | null | Accounts }; +export type AccountsGeneric = { + [name: string]: PublicKey | AccountsGeneric; +}; + +export function isAccountsGeneric( + accounts: PublicKey | AccountsGeneric +): accounts is AccountsGeneric { + return !(accounts instanceof PublicKey); +} export type CustomAccountResolver = (params: { args: Array; - accounts: Accounts; + accounts: AccountsGeneric; provider: Provider; programId: PublicKey; idlIx: AllInstructions; -}) => Promise<{ accounts: Accounts; resolved: number }>; +}) => Promise<{ accounts: AccountsGeneric; resolved: number }>; // Populates a given accounts context with PDAs and common missing accounts. export class AccountsResolver { @@ -49,7 +58,7 @@ export class AccountsResolver { constructor( _args: Array, - private _accounts: Accounts, + private _accounts: AccountsGeneric, private _provider: Provider, private _programId: PublicKey, private _idlIx: AllInstructions, @@ -99,13 +108,40 @@ export class AccountsResolver { return 0; } - public isOptional(name: string): boolean { - const accountDesc = this._idlIx.accounts.find((acc) => acc.name === name); - if (accountDesc !== undefined && "isOptional" in accountDesc) { - return accountDesc.isOptional ?? false; - } else { - return false; + private resolveOptionalsHelper( + partialAccounts: PartialAccounts, + accountItems: IdlAccountItem[] + ): AccountsGeneric { + const nestedAccountsGeneric = {}; + // Looping through accountItem array instead of on partialAccounts, so + // we only traverse array once + for (const accountItem of accountItems) { + const accountName = accountItem.name; + const partialAccount = partialAccounts[accountName]; + // Only continue if this account is included in the partialAccounts param + if (partialAccount !== undefined) { + if (!isPartialAccounts(partialAccount)) { + // if not compound accounts, do null/optional check and proceed + if (partialAccount !== null) { + nestedAccountsGeneric[accountName] = + translateAddress(partialAccount); + } else if (accountItem["isOptional"]) { + nestedAccountsGeneric[accountName] = this._programId; + } + } else { + // is compound accounts, recurse one level deeper + nestedAccountsGeneric[accountName] = this.resolveOptionalsHelper( + partialAccount, + accountItem["accounts"] as IdlAccountItem[] + ); + } + } } + return nestedAccountsGeneric; + } + + public resolveOptionals(accounts: PartialAccounts) { + this.resolveOptionalsHelper(accounts, this._idlIx.accounts); } private get(path: string[]): PublicKey | undefined { @@ -129,7 +165,7 @@ export class AccountsResolver { } curr[p] = curr[p] || {}; - curr = curr[p] as Accounts; + curr = curr[p] as AccountsGeneric; }); } @@ -150,11 +186,6 @@ export class AccountsResolver { const accountDesc = accountDescOrAccounts as IdlAccount; const accountDescName = camelCase(accountDescOrAccounts.name); - if (accountDesc.isOptional && this._accounts[accountDescName] === null) { - this._accounts[accountDescName] = this._programId; - continue; - } - // Signers default to the provider. if (accountDesc.isSigner && !this.get([...path, accountDescName])) { // @ts-expect-error @@ -260,11 +291,6 @@ export class AccountsResolver { if (!accountDesc.pda || !accountDesc.pda.seeds) throw new Error("Must have seeds"); - if (accountDesc.isOptional && this._accounts[accountDesc.name] === null) { - this._accounts[accountDesc.name] = this._programId; - return; - } - const seeds: (Buffer | undefined)[] = await Promise.all( accountDesc.pda.seeds.map((seedDesc: IdlSeed) => this.toBuffer(seedDesc, path) diff --git a/ts/packages/anchor/src/program/namespace/instruction.ts b/ts/packages/anchor/src/program/namespace/instruction.ts index 285b1eda2f..37c5db4498 100644 --- a/ts/packages/anchor/src/program/namespace/instruction.ts +++ b/ts/packages/anchor/src/program/namespace/instruction.ts @@ -99,25 +99,20 @@ export default class InstructionNamespaceFactory { ).flat(); } else { const account: IdlAccount = acc as IdlAccount; - let pubkey; - if (ctx[acc.name] === null && account.isOptional) { - pubkey = programId; - } else { - try { - pubkey = translateAddress(ctx[acc.name] as Address); - } catch (err) { - throw new Error( - `Wrong input type for account "${ - acc.name - }" in the instruction accounts object${ - ixName !== undefined - ? ' for instruction "' + ixName + '"' - : "" - }. Expected PublicKey or string.` - ); - } + let pubkey: PublicKey; + try { + pubkey = translateAddress(ctx[acc.name] as Address); + } catch (err) { + throw new Error( + `Wrong input type for account "${ + acc.name + }" in the instruction accounts object${ + ixName !== undefined ? ' for instruction "' + ixName + '"' : "" + }. Expected PublicKey or string.` + ); } - const optional = account.isOptional && pubkey === programId; + + const optional = account.isOptional && pubkey.equals(programId); const isWritable = account.isMut && !optional; const isSigner = account.isSigner && !optional; return { diff --git a/ts/packages/anchor/src/program/namespace/methods.ts b/ts/packages/anchor/src/program/namespace/methods.ts index 86f94549fe..c78a3a66d3 100644 --- a/ts/packages/anchor/src/program/namespace/methods.ts +++ b/ts/packages/anchor/src/program/namespace/methods.ts @@ -10,10 +10,11 @@ import { import { Idl, IdlAccountItem, IdlAccounts, IdlTypeDef } from "../../idl.js"; import Provider from "../../provider.js"; import { + AccountsGeneric, AccountsResolver, CustomAccountResolver, } from "../accounts-resolver.js"; -import { Address } from "../common.js"; +import { Address, translateAddress } from "../common.js"; import { Accounts } from "../context.js"; import { AccountNamespace } from "./account.js"; import { InstructionFn } from "./instruction.js"; @@ -65,16 +66,27 @@ export class MethodsBuilderFactory { } } -type PartialAccounts = Partial<{ - [N in A["name"]]: PartialAccount; -}>; +export type PartialAccounts = + Partial<{ + [N in A["name"]]: PartialAccount; + }>; type PartialAccount = A extends IdlAccounts - ? Partial> + ? PartialAccounts + : A extends { isOptional: true } + ? Address | null : Address; +export function isPartialAccounts( + partialAccount: PartialAccount +): partialAccount is PartialAccounts { + return ( + !(partialAccount instanceof PublicKey) && typeof partialAccount === "object" + ); +} + export class MethodsBuilder> { - private readonly _accounts: { [name: string]: PublicKey | null } = {}; + private readonly _accounts: AccountsGeneric = {}; private _remainingAccounts: Array = []; private _signers: Array = []; private _preInstructions: Array = []; @@ -91,7 +103,7 @@ export class MethodsBuilder> { private _simulateFn: SimulateFn, private _viewFn: ViewFn | undefined, _provider: Provider, - _programId: PublicKey, + private _programId: PublicKey, _idlIx: AllInstructions, _accountNamespace: AccountNamespace, _idlTypes: IdlTypeDef[], @@ -128,16 +140,7 @@ export class MethodsBuilder> { public accounts(accounts: PartialAccounts): MethodsBuilder { this._autoResolveAccounts = true; - for (const accountName in accounts) { - if (accounts[accountName]) { - this._accounts[accountName] = accounts[accountName]; - } else if ( - accounts[accountName] === null && - this._accountsResolver.isOptional(accountName) - ) { - this._accounts[accountName] = null; - } - } + this._accountsResolver.resolveOptionals(accounts); return this; } @@ -145,16 +148,7 @@ export class MethodsBuilder> { accounts: Accounts ): MethodsBuilder { this._autoResolveAccounts = false; - for (const accountName in accounts) { - if (accounts[accountName]) { - this._accounts[accountName] = accounts[accountName]; - } else if ( - accounts[accountName] === null && - this._accountsResolver.isOptional(accountName) - ) { - this._accounts[accountName] = null; - } - } + this._accountsResolver.resolveOptionals(accounts); return this; } From 3fd1dcd8145d822007c927418433da8cb68c8acf Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 6 Dec 2022 03:00:07 -0600 Subject: [PATCH 099/109] hopefully fixed ts stuff? --- .../anchor/src/program/accounts-resolver.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/ts/packages/anchor/src/program/accounts-resolver.ts b/ts/packages/anchor/src/program/accounts-resolver.ts index 2c743151bf..8790e8a2c6 100644 --- a/ts/packages/anchor/src/program/accounts-resolver.ts +++ b/ts/packages/anchor/src/program/accounts-resolver.ts @@ -14,6 +14,7 @@ import { IdlTypeDef, IdlTypeDefTyStruct, IdlType, + isIdlAccounts, } from "../idl.js"; import * as utf8 from "../utils/bytes/utf8.js"; import { TOKEN_PROGRAM_ID, ASSOCIATED_PROGRAM_ID } from "../utils/token.js"; @@ -130,10 +131,21 @@ export class AccountsResolver { } } else { // is compound accounts, recurse one level deeper - nestedAccountsGeneric[accountName] = this.resolveOptionalsHelper( - partialAccount, - accountItem["accounts"] as IdlAccountItem[] - ); + if (isIdlAccounts(accountItem)) { + nestedAccountsGeneric[accountName] = this.resolveOptionalsHelper( + partialAccount, + accountItem["accounts"] as IdlAccountItem[] + ); + } else { + console.error(`Partial Account: ${JSON.stringify(partialAccount)}`); + console.error( + `\t isPartialAccounts: ${isPartialAccounts(partialAccount)}` + ); + console.error(`accountItem: ${JSON.stringify(accountItem)}`); + throw new Error( + "Type mismatch somehow. This shouldn't be possible!" + ); + } } } } From 9b198d22507104126a50191a29f7a39e166df818 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 6 Dec 2022 03:05:16 -0600 Subject: [PATCH 100/109] testing --- ts/packages/anchor/src/program/accounts-resolver.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ts/packages/anchor/src/program/accounts-resolver.ts b/ts/packages/anchor/src/program/accounts-resolver.ts index 8790e8a2c6..a7068ae1f3 100644 --- a/ts/packages/anchor/src/program/accounts-resolver.ts +++ b/ts/packages/anchor/src/program/accounts-resolver.ts @@ -113,7 +113,7 @@ export class AccountsResolver { partialAccounts: PartialAccounts, accountItems: IdlAccountItem[] ): AccountsGeneric { - const nestedAccountsGeneric = {}; + const nestedAccountsGeneric: AccountsGeneric = {}; // Looping through accountItem array instead of on partialAccounts, so // we only traverse array once for (const accountItem of accountItems) { @@ -142,9 +142,8 @@ export class AccountsResolver { `\t isPartialAccounts: ${isPartialAccounts(partialAccount)}` ); console.error(`accountItem: ${JSON.stringify(accountItem)}`); - throw new Error( - "Type mismatch somehow. This shouldn't be possible!" - ); + // @ts-ignore + nestedAccountsGeneric[accountName] = partialAccount; } } } From e957e5b4496859f3f27583cf0dea91ef8d2840fe Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Tue, 6 Dec 2022 03:50:38 -0600 Subject: [PATCH 101/109] hopefully done? --- .../anchor/src/program/accounts-resolver.ts | 17 ++++++++----- .../anchor/src/program/namespace/methods.ts | 25 ++++++++++++++++++- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/ts/packages/anchor/src/program/accounts-resolver.ts b/ts/packages/anchor/src/program/accounts-resolver.ts index a7068ae1f3..85d638dd5a 100644 --- a/ts/packages/anchor/src/program/accounts-resolver.ts +++ b/ts/packages/anchor/src/program/accounts-resolver.ts @@ -24,7 +24,11 @@ import { AccountNamespace } from "./namespace/account.js"; import { BorshAccountsCoder } from "src/coder/index.js"; import { decodeTokenAccount } from "./token-account-layout"; import { Program, translateAddress } from "./index.js"; -import { isPartialAccounts, PartialAccounts } from "./namespace/methods"; +import { + flattenPartialAccounts, + isPartialAccounts, + PartialAccounts, +} from "./namespace/methods"; export type AccountsGeneric = { [name: string]: PublicKey | AccountsGeneric; @@ -137,13 +141,14 @@ export class AccountsResolver { accountItem["accounts"] as IdlAccountItem[] ); } else { - console.error(`Partial Account: ${JSON.stringify(partialAccount)}`); console.error( - `\t isPartialAccounts: ${isPartialAccounts(partialAccount)}` + `Type mismatch between IDL and Input accounts. Optional resolver might fail.` + ); + // Here we try our best to recover gracefully. If there are optionals we can't check, we will fail then. + nestedAccountsGeneric[accountName] = flattenPartialAccounts( + partialAccount, + true ); - console.error(`accountItem: ${JSON.stringify(accountItem)}`); - // @ts-ignore - nestedAccountsGeneric[accountName] = partialAccount; } } } diff --git a/ts/packages/anchor/src/program/namespace/methods.ts b/ts/packages/anchor/src/program/namespace/methods.ts index c78a3a66d3..3d3d954c78 100644 --- a/ts/packages/anchor/src/program/namespace/methods.ts +++ b/ts/packages/anchor/src/program/namespace/methods.ts @@ -81,10 +81,33 @@ export function isPartialAccounts( partialAccount: PartialAccount ): partialAccount is PartialAccounts { return ( - !(partialAccount instanceof PublicKey) && typeof partialAccount === "object" + !(partialAccount instanceof PublicKey) && + partialAccount !== null && + typeof partialAccount === "object" ); } +export function flattenPartialAccounts( + partialAccounts: PartialAccounts, + throwOnNull: boolean +): AccountsGeneric { + const toReturn: AccountsGeneric = {}; + for (const accountName in partialAccounts) { + const account = partialAccounts[accountName]; + if (account === null) { + if (throwOnNull) + throw new Error( + "Failed to resolve optionals due to IDL type mismatch with input accounts!" + ); + continue; + } + toReturn[accountName] = isPartialAccounts(account) + ? flattenPartialAccounts(account, true) + : translateAddress(account); + } + return toReturn; +} + export class MethodsBuilder> { private readonly _accounts: AccountsGeneric = {}; private _remainingAccounts: Array = []; From b76d4ad8f6cad0f718ed062a51468da0950ab232 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Wed, 7 Dec 2022 19:40:57 -0600 Subject: [PATCH 102/109] update misc test --- tests/misc/tests/misc/misc.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/misc/tests/misc/misc.ts b/tests/misc/tests/misc/misc.ts index 9f1f3aa70a..6c032917d8 100644 --- a/tests/misc/tests/misc/misc.ts +++ b/tests/misc/tests/misc/misc.ts @@ -866,6 +866,22 @@ const miscTest = ( assert.isTrue(account.mint.equals(localClient.publicKey)); }); + it("Can use fetchNullable() on accounts with only a balance", async () => { + const account = anchor.web3.Keypair.generate(); + + // Airdrop 1 SOL to the account. + const signature = await program.provider.connection.requestAirdrop( + account.publicKey, + anchor.web3.LAMPORTS_PER_SOL + ); + await program.provider.connection.confirmTransaction(signature); + + const data = await program.account.data.fetchNullable( + account.publicKey + ); + assert.isNull(data); + }); + it("Can validate associated_token constraints", async () => { const localClient = await client; await program.rpc.testValidateAssociatedToken({ From 4e8454f008826476ab27ee581d72aa17d575f6c8 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Wed, 7 Dec 2022 20:21:27 -0600 Subject: [PATCH 103/109] fix optional tests --- .github/workflows/no-cashing-tests.yaml | 2 +- tests/misc/programs/misc-optional/src/lib.rs | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/no-cashing-tests.yaml b/.github/workflows/no-cashing-tests.yaml index a26025a0ee..2e97ef88ea 100644 --- a/.github/workflows/no-cashing-tests.yaml +++ b/.github/workflows/no-cashing-tests.yaml @@ -5,7 +5,7 @@ on: branches: - master env: - SOLANA_CLI_VERSION: 1.13.3 + SOLANA_CLI_VERSION: 1.14.7 NODE_VERSION: 17.0.1 jobs: diff --git a/tests/misc/programs/misc-optional/src/lib.rs b/tests/misc/programs/misc-optional/src/lib.rs index 9b93f6cb75..aa7e042d6c 100644 --- a/tests/misc/programs/misc-optional/src/lib.rs +++ b/tests/misc/programs/misc-optional/src/lib.rs @@ -214,14 +214,7 @@ pub mod misc_optional { } pub fn test_composite_payer(ctx: Context) -> Result<()> { - ctx.accounts - .composite - .as_mut() - .unwrap() - .data - .as_mut() - .unwrap() - .data = 1; + ctx.accounts.composite.data.as_mut().unwrap().data = 1; ctx.accounts.data.as_mut().unwrap().udata = 2; ctx.accounts.data.as_mut().unwrap().idata = 3; Ok(()) From d4f41d79f9f918c1d9fb156655276ec9f756d42f Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 8 Dec 2022 22:02:17 -0600 Subject: [PATCH 104/109] fix ts --- .../anchor/src/program/accounts-resolver.ts | 52 +++++++++---------- .../anchor/src/program/namespace/methods.ts | 4 +- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/ts/packages/anchor/src/program/accounts-resolver.ts b/ts/packages/anchor/src/program/accounts-resolver.ts index 85d638dd5a..3d144f7f1f 100644 --- a/ts/packages/anchor/src/program/accounts-resolver.ts +++ b/ts/packages/anchor/src/program/accounts-resolver.ts @@ -123,33 +123,28 @@ export class AccountsResolver { for (const accountItem of accountItems) { const accountName = accountItem.name; const partialAccount = partialAccounts[accountName]; - // Only continue if this account is included in the partialAccounts param - if (partialAccount !== undefined) { - if (!isPartialAccounts(partialAccount)) { - // if not compound accounts, do null/optional check and proceed - if (partialAccount !== null) { - nestedAccountsGeneric[accountName] = - translateAddress(partialAccount); - } else if (accountItem["isOptional"]) { - nestedAccountsGeneric[accountName] = this._programId; - } + // Skip if the account isn't included (thus would be undefined) + if (partialAccount === undefined) continue; + if (isPartialAccounts(partialAccount)) { + // is compound accounts, recurse one level deeper + if (isIdlAccounts(accountItem)) { + nestedAccountsGeneric[accountName] = this.resolveOptionalsHelper( + partialAccount, + accountItem["accounts"] as IdlAccountItem[] + ); } else { - // is compound accounts, recurse one level deeper - if (isIdlAccounts(accountItem)) { - nestedAccountsGeneric[accountName] = this.resolveOptionalsHelper( - partialAccount, - accountItem["accounts"] as IdlAccountItem[] - ); - } else { - console.error( - `Type mismatch between IDL and Input accounts. Optional resolver might fail.` - ); - // Here we try our best to recover gracefully. If there are optionals we can't check, we will fail then. - nestedAccountsGeneric[accountName] = flattenPartialAccounts( - partialAccount, - true - ); - } + // Here we try our best to recover gracefully. If there are optionals we can't check, we will fail then. + nestedAccountsGeneric[accountName] = flattenPartialAccounts( + partialAccount, + true + ); + } + } else { + // if not compound accounts, do null/optional check and proceed + if (partialAccount !== null) { + nestedAccountsGeneric[accountName] = translateAddress(partialAccount); + } else if (accountItem["isOptional"]) { + nestedAccountsGeneric[accountName] = this._programId; } } } @@ -157,7 +152,10 @@ export class AccountsResolver { } public resolveOptionals(accounts: PartialAccounts) { - this.resolveOptionalsHelper(accounts, this._idlIx.accounts); + Object.assign( + this._accounts, + this.resolveOptionalsHelper(accounts, this._idlIx.accounts) + ); } private get(path: string[]): PublicKey | undefined { diff --git a/ts/packages/anchor/src/program/namespace/methods.ts b/ts/packages/anchor/src/program/namespace/methods.ts index 3d3d954c78..4fbfdc815b 100644 --- a/ts/packages/anchor/src/program/namespace/methods.ts +++ b/ts/packages/anchor/src/program/namespace/methods.ts @@ -81,9 +81,9 @@ export function isPartialAccounts( partialAccount: PartialAccount ): partialAccount is PartialAccounts { return ( - !(partialAccount instanceof PublicKey) && + typeof partialAccount === "object" && partialAccount !== null && - typeof partialAccount === "object" + !("_bn" in partialAccount) // Ensures not a pubkey ); } From 7c24da7c0ab1742d641b7abfc8901dae574b594d Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 8 Dec 2022 22:20:32 -0600 Subject: [PATCH 105/109] fix ts again! --- tests/package.json | 5 +- tests/yarn.lock | 204 ++---------------- .../anchor/src/program/namespace/methods.ts | 2 +- 3 files changed, 26 insertions(+), 185 deletions(-) diff --git a/tests/package.json b/tests/package.json index 4963ec94cd..c96d9e7d44 100644 --- a/tests/package.json +++ b/tests/package.json @@ -48,7 +48,10 @@ "@project-serum/common": "^0.0.1-beta.3", "@project-serum/serum": "^0.13.60", "@solana/spl-token": "^0.1.8", - "@solana/web3.js": "^1.64.0" + "@solana/web3.js": "^1.68.0" + }, + "resolutions": { + "@project-serum/anchor/@solana/web3.js": "^1.68.0" }, "devDependencies": { "@types/chai": "^4.3.0", diff --git a/tests/yarn.lock b/tests/yarn.lock index 3059337114..f54c222f48 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5": +"@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== @@ -16,26 +16,13 @@ dependencies: regenerator-runtime "^0.13.4" -"@ethersproject/bytes@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" - integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== +"@coral-xyz/borsh@^0.2.6": + version "0.2.6" + resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.2.6.tgz#0f11b223bf2967574310705afd3c53ce26688ada" + integrity sha512-y6nmHw1bFcJib7sMHsQPpC8r47xhqDZVvhUdna7NUPzpSbOZG6f46N21+aXsQ2w/tG8Ggls488J/ZmwbgVmyjg== dependencies: - "@ethersproject/logger" "^5.5.0" - -"@ethersproject/logger@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" - integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== - -"@ethersproject/sha2@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" - integrity sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - hash.js "1.1.7" + bn.js "^5.1.2" + buffer-layout "^1.2.0" "@native-to-anchor/buffer-layout@=0.1.0": version "0.1.0" @@ -63,8 +50,8 @@ "@project-serum/anchor@=0.25.0", "@project-serum/anchor@file:../ts/packages/anchor": version "0.25.0" dependencies: - "@project-serum/borsh" "^0.2.5" - "@solana/web3.js" "^1.64.0" + "@coral-xyz/borsh" "^0.2.6" + "@solana/web3.js" "^1.68.0" base64-js "^1.5.1" bn.js "^5.1.2" bs58 "^4.0.1" @@ -99,7 +86,7 @@ snake-case "^3.0.4" toml "^3.0.0" -"@project-serum/borsh@^0.2.2", "@project-serum/borsh@^0.2.5": +"@project-serum/borsh@^0.2.2": version "0.2.5" resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663" integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q== @@ -156,13 +143,6 @@ dependencies: buffer "~6.0.3" -"@solana/buffer-layout@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-3.0.0.tgz#b9353caeb9a1589cb77a1b145bcb1a9a93114326" - integrity sha512-MVdgAKKL39tEs0l8je0hKaXLQFb7Rdfb0Xg2LjFZd8Lfdazkg6xiS98uAZrEKvaoF3i4M95ei9RydkGIDMeo3w== - dependencies: - buffer "~6.0.3" - "@solana/spl-token@^0.1.6", "@solana/spl-token@^0.1.8": version "0.1.8" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" @@ -175,10 +155,10 @@ buffer-layout "^1.2.0" dotenv "10.0.0" -"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.32.0": - version "1.66.2" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.66.2.tgz#80b43c5868b846124fe3ebac7d3943930c3fa60c" - integrity sha512-RyaHMR2jGmaesnYP045VLeBGfR/gAW3cvZHzMFGg7bkO+WOYOYp1nEllf0/la4U4qsYGKCsO9eEevR5fhHiVHg== +"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.32.0": + version "1.64.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.64.0.tgz#b7f5a976976039a0161242e94d6e1224ab5d30f9" + integrity sha512-AcFaoy48GxSmzBryVwB88C/UPJd/UQa+nFrO/uPc8ww6RCjanZY2vEZxdfTZub+q1NMUckwXpPwF32jJLe7SPA== dependencies: "@babel/runtime" "^7.12.5" "@noble/ed25519" "^1.7.0" @@ -196,30 +176,10 @@ rpc-websockets "^7.5.0" superstruct "^0.14.2" -"@solana/web3.js@^1.21.0": - version "1.30.2" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.30.2.tgz#e85da75e0825dc64f53eb64a1ff0115b27bec135" - integrity sha512-hznCj+rkfvM5taRP3Z+l5lumB7IQnDrB4l55Wpsg4kDU9Zds8pE5YOH5Z9bbF/pUzZJKQjyBjnY/6kScBm3Ugg== - dependencies: - "@babel/runtime" "^7.12.5" - "@ethersproject/sha2" "^5.5.0" - "@solana/buffer-layout" "^3.0.0" - bn.js "^5.0.0" - borsh "^0.4.0" - bs58 "^4.0.1" - buffer "6.0.1" - cross-fetch "^3.1.4" - jayson "^3.4.4" - js-sha3 "^0.8.0" - rpc-websockets "^7.4.2" - secp256k1 "^4.0.2" - superstruct "^0.14.2" - tweetnacl "^1.0.0" - -"@solana/web3.js@^1.64.0": - version "1.64.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.64.0.tgz#b7f5a976976039a0161242e94d6e1224ab5d30f9" - integrity sha512-AcFaoy48GxSmzBryVwB88C/UPJd/UQa+nFrO/uPc8ww6RCjanZY2vEZxdfTZub+q1NMUckwXpPwF32jJLe7SPA== +"@solana/web3.js@^1.68.0": + version "1.70.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.70.0.tgz#14ad207f431861397db85921aad8df4e8374e7c8" + integrity sha512-HwdI9LaHaszfpzgxJI44iP68mJWUeqK1TeSheKQsGkH5zlVyGWGmim50MyDWu2vXiuL8Akf2xEMSrDYyLordgg== dependencies: "@babel/runtime" "^7.12.5" "@noble/ed25519" "^1.7.0" @@ -237,13 +197,6 @@ rpc-websockets "^7.5.0" superstruct "^0.14.2" -"@types/bn.js@^4.11.5": - version "4.11.6" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" - integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== - dependencies: - "@types/node" "*" - "@types/chai@^4.3.0": version "4.3.0" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc" @@ -406,11 +359,6 @@ bindings@^1.3.0: dependencies: file-uri-to-path "1.0.0" -bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2: version "5.2.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" @@ -421,16 +369,6 @@ bn.js@^5.2.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -borsh@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.4.0.tgz#9dd6defe741627f1315eac2a73df61421f6ddb9f" - integrity sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g== - dependencies: - "@types/bn.js" "^4.11.5" - bn.js "^5.0.0" - bs58 "^4.0.0" - text-encoding-utf-8 "^1.0.2" - borsh@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" @@ -462,11 +400,6 @@ braces@~3.0.2: dependencies: fill-range "^7.0.1" -brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -582,11 +515,6 @@ chokidar@3.5.3: optionalDependencies: fsevents "~2.3.2" -circular-json@^0.5.9: - version "0.5.9" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d" - integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== - cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -618,13 +546,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -cross-fetch@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" - integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== - dependencies: - node-fetch "2.6.1" - cross-fetch@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" @@ -691,19 +612,6 @@ dotenv@10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -elliptic@^6.5.2: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -839,28 +747,11 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -874,7 +765,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@^2.0.4: +inherits@2: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -954,11 +845,6 @@ js-sha256@^0.9.0: resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== -js-sha3@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" - integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== - js-yaml@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -1020,16 +906,6 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -1142,11 +1018,6 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-addon-api@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" - integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== - node-fetch@2, node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -1154,12 +1025,7 @@ node-fetch@2, node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: +node-gyp-build@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== @@ -1244,20 +1110,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -rpc-websockets@^7.4.2: - version "7.4.16" - resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.4.16.tgz#eb701cdef577d4357ba5f526d50e25f370396fac" - integrity sha512-0b7OVhutzwRIaYAtJo5tqtaQTWKfwAsKnaThOSOy+VkhVdleNUgb8eZnWSdWITRZZEigV5uPEIDr5KZe4DBrdQ== - dependencies: - "@babel/runtime" "^7.11.2" - circular-json "^0.5.9" - eventemitter3 "^4.0.7" - uuid "^8.3.0" - ws "^7.4.5" - optionalDependencies: - bufferutil "^4.0.1" - utf-8-validate "^5.0.2" - rpc-websockets@^7.5.0: version "7.5.0" resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.0.tgz#bbeb87572e66703ff151e50af1658f98098e2748" @@ -1276,15 +1128,6 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -secp256k1@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" - integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== - dependencies: - elliptic "^6.5.2" - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -1451,11 +1294,6 @@ tslib@^2.0.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tweetnacl@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== - type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -1478,7 +1316,7 @@ uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.0, uuid@^8.3.2: +uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== diff --git a/ts/packages/anchor/src/program/namespace/methods.ts b/ts/packages/anchor/src/program/namespace/methods.ts index 4fbfdc815b..8a3cfa0a3b 100644 --- a/ts/packages/anchor/src/program/namespace/methods.ts +++ b/ts/packages/anchor/src/program/namespace/methods.ts @@ -161,7 +161,7 @@ export class MethodsBuilder> { >; } - public accounts(accounts: PartialAccounts): MethodsBuilder { + public accounts(accounts: PartialAccounts): MethodsBuilder { this._autoResolveAccounts = true; this._accountsResolver.resolveOptionals(accounts); return this; From f9c0f8525801f466efe13c66ab875d0412f3e8a1 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Thu, 8 Dec 2022 22:27:18 -0600 Subject: [PATCH 106/109] linting urg --- ts/packages/anchor/src/program/namespace/methods.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ts/packages/anchor/src/program/namespace/methods.ts b/ts/packages/anchor/src/program/namespace/methods.ts index 8a3cfa0a3b..e29a608f71 100644 --- a/ts/packages/anchor/src/program/namespace/methods.ts +++ b/ts/packages/anchor/src/program/namespace/methods.ts @@ -161,7 +161,9 @@ export class MethodsBuilder> { >; } - public accounts(accounts: PartialAccounts): MethodsBuilder { + public accounts( + accounts: PartialAccounts + ): MethodsBuilder { this._autoResolveAccounts = true; this._accountsResolver.resolveOptionals(accounts); return this; From cdc582998bca4bfb9929b1404f92f474ff878031 Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sun, 11 Dec 2022 14:18:59 -0600 Subject: [PATCH 107/109] allow-missing-optionals feature --- lang/Cargo.toml | 1 + lang/derive/accounts/Cargo.toml | 1 + lang/src/accounts/option.rs | 23 ++++--- lang/syn/Cargo.toml | 1 + lang/syn/src/codegen/accounts/try_accounts.rs | 7 ++- tests/optional/Anchor.toml | 3 +- .../allow-missing-optionals/Cargo.toml | 16 +++++ .../allow-missing-optionals/Xargo.toml | 2 + .../allow-missing-optionals/src/lib.rs | 38 ++++++++++++ tests/optional/tests/optional.ts | 61 +++++++++++++++++++ 10 files changed, 143 insertions(+), 10 deletions(-) create mode 100644 tests/optional/programs/allow-missing-optionals/Cargo.toml create mode 100644 tests/optional/programs/allow-missing-optionals/Xargo.toml create mode 100644 tests/optional/programs/allow-missing-optionals/src/lib.rs diff --git a/lang/Cargo.toml b/lang/Cargo.toml index 82c56c73e2..4331741710 100644 --- a/lang/Cargo.toml +++ b/lang/Cargo.toml @@ -9,6 +9,7 @@ license = "Apache-2.0" description = "Solana Sealevel eDSL" [features] +allow-missing-optionals = ["anchor-derive-accounts/allow-missing-optionals"] init-if-needed = ["anchor-derive-accounts/init-if-needed"] derive = [] default = [] diff --git a/lang/derive/accounts/Cargo.toml b/lang/derive/accounts/Cargo.toml index efffc6105d..6278056598 100644 --- a/lang/derive/accounts/Cargo.toml +++ b/lang/derive/accounts/Cargo.toml @@ -12,6 +12,7 @@ edition = "2021" proc-macro = true [features] +allow-missing-optionals = ["anchor-syn/allow-missing-optionals"] init-if-needed = ["anchor-syn/init-if-needed"] default = [] anchor-debug = ["anchor-syn/anchor-debug"] diff --git a/lang/src/accounts/option.rs b/lang/src/accounts/option.rs index c06cd97e27..0958716235 100644 --- a/lang/src/accounts/option.rs +++ b/lang/src/accounts/option.rs @@ -14,7 +14,9 @@ use solana_program::account_info::AccountInfo; use solana_program::instruction::AccountMeta; use solana_program::pubkey::Pubkey; -use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas}; +use crate::{ + error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas, +}; impl<'info, T: Accounts<'info>> Accounts<'info> for Option { fn try_accounts( @@ -24,14 +26,19 @@ impl<'info, T: Accounts<'info>> Accounts<'info> for Option { bumps: &mut BTreeMap, reallocs: &mut BTreeSet, ) -> Result { - // There are three cases we have to handle. We don't care if - // accounts is empty, so if that's the case we return None. This - // allows adding optional accounts at the end of the Accounts - // struct without causing a breaking change. This is safe and will error - // out if a required account is then added after the optional account and - // the accounts aren't passed in. if accounts.is_empty() { - return Ok(None); + return if cfg!(feature = "allow-missing-optionals") { + // We don't care if accounts is empty (when this feature is active), + // so if that's the case we return None. This allows adding optional + // accounts at the end of the Accounts struct without causing a breaking + // change. This is safe and will error out if a required account is then + // added after the optional account and the accounts aren't passed in. + Ok(None) + } else { + // If the feature is inactive (it is off by default), then we error out + // like every other Account. + Err(ErrorCode::AccountNotEnoughKeys.into()) + }; } // If there are enough accounts, it will check the program_id and return diff --git a/lang/syn/Cargo.toml b/lang/syn/Cargo.toml index cc64c48c3e..826b9783b6 100644 --- a/lang/syn/Cargo.toml +++ b/lang/syn/Cargo.toml @@ -9,6 +9,7 @@ rust-version = "1.59" edition = "2021" [features] +allow-missing-optionals = [] init-if-needed = [] idl = [] hash = [] diff --git a/lang/syn/src/codegen/accounts/try_accounts.rs b/lang/syn/src/codegen/accounts/try_accounts.rs index dbedec97ca..0929a2d980 100644 --- a/lang/syn/src/codegen/accounts/try_accounts.rs +++ b/lang/syn/src/codegen/accounts/try_accounts.rs @@ -39,9 +39,14 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { if f.is_optional { // Thus, this block essentially reimplements the try_accounts // behavior with optional accounts minus the deserialziation. + let empty_behavior = if cfg!(feature = "allow-missing-optionals") { + quote!{ None } + } else { + quote!{ return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); } + }; quote! { let #name = if accounts.is_empty() { - None + #empty_behavior } else if accounts[0].key == program_id { *accounts = &accounts[1..]; None diff --git a/tests/optional/Anchor.toml b/tests/optional/Anchor.toml index 6e8d0d3f6d..7731251f63 100644 --- a/tests/optional/Anchor.toml +++ b/tests/optional/Anchor.toml @@ -4,9 +4,10 @@ wallet = "~/.config/solana/id.json" [programs.localnet] optional = "FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG" +allow_missing_optionals = "ErjUjtqKE5AGWUsjseSJCVLtddM6rhaMbDqmhzraF9h6" [workspace] -members = ["programs/optional"] +members = ["programs/optional", "programs/allow-missing-optionals"] [scripts] test = "yarn run ts-mocha -t 1000000 -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tests/optional/programs/allow-missing-optionals/Cargo.toml b/tests/optional/programs/allow-missing-optionals/Cargo.toml new file mode 100644 index 0000000000..90ae32316b --- /dev/null +++ b/tests/optional/programs/allow-missing-optionals/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "allow-missing-optionals" +version = "0.1.0" +description = "Created with Anchor" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "allow_missing_optionals" + +[features] +no-entrypoint = [] +cpi = ["no-entrypoint"] + +[dependencies] +anchor-lang = { path = "../../../../lang", features = ["allow-missing-optionals"] } diff --git a/tests/optional/programs/allow-missing-optionals/Xargo.toml b/tests/optional/programs/allow-missing-optionals/Xargo.toml new file mode 100644 index 0000000000..1744f098ae --- /dev/null +++ b/tests/optional/programs/allow-missing-optionals/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] \ No newline at end of file diff --git a/tests/optional/programs/allow-missing-optionals/src/lib.rs b/tests/optional/programs/allow-missing-optionals/src/lib.rs new file mode 100644 index 0000000000..1981611afc --- /dev/null +++ b/tests/optional/programs/allow-missing-optionals/src/lib.rs @@ -0,0 +1,38 @@ +//! This tests that the `allow-missing-optionals` feature works + +use anchor_lang::prelude::*; + +declare_id!("ErjUjtqKE5AGWUsjseSJCVLtddM6rhaMbDqmhzraF9h6"); + +#[program] +mod allow_missing_optionals { + use super::*; + + pub fn do_stuff(ctx: Context) -> Result<()> { + msg!("Doing stuff..."); + let optional_2 = &mut ctx.accounts.optional_2; + if let Some(data_account) = optional_2 { + data_account.data = 42; + } + + Ok(()) + } +} + +#[account] +pub struct DataAccount { + pub data: u64, +} + +impl DataAccount { + pub const LEN: usize = 8 + 8; +} + +#[derive(Accounts)] +pub struct DoStuff<'info> { + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Option>, + #[account(init, payer = payer, space = DataAccount::LEN)] + pub optional_2: Option>, +} diff --git a/tests/optional/tests/optional.ts b/tests/optional/tests/optional.ts index 18d6d017d1..9c12bb4bae 100644 --- a/tests/optional/tests/optional.ts +++ b/tests/optional/tests/optional.ts @@ -6,8 +6,11 @@ import { AnchorError, LangErrorCode, LangErrorMessage, + translateError, + parseIdlErrors, } from "@project-serum/anchor"; import { Optional } from "../target/types/optional"; +import { AllowMissingOptionals } from "../target/types/allow_missing_optionals"; import { assert, expect } from "chai"; describe("Optional", () => { @@ -67,6 +70,64 @@ describe("Optional", () => { createRequiredIx2 = (await createRequired(requiredKeypair2))[1]; }); + describe("Missing optionals feature tests", async () => { + it("Fails with missing optional accounts at the end by default", async () => { + const [requiredKeypair, createRequiredIx] = await createRequired(); + const initializeIx = await program.methods + .initialize(initializeValue1, initializeKey) + .accounts({ + payer: null, + optionalAccount: null, + systemProgram, + required: requiredKeypair.publicKey, + optionalPda: null, + }) + .signers([requiredKeypair]) + .instruction(); + initializeIx.keys.pop(); + const initializeTxn = new web3.Transaction() + .add(createRequiredIx) + .add(initializeIx); + try { + await anchorProvider + .sendAndConfirm(initializeTxn, [requiredKeypair]) + .catch((e) => { + throw translateError(e, parseIdlErrors(program.idl)); + }); + assert.fail( + "Unexpected success in creating a transaction that should have failed with `AccountNotEnoughKeys` error" + ); + } catch (e) { + // @ts-ignore + assert.isTrue(e instanceof AnchorError, e.toString()); + const err: AnchorError = e; + const errorCode = LangErrorCode.AccountNotEnoughKeys; + assert.strictEqual( + err.error.errorMessage, + LangErrorMessage.get(errorCode) + ); + assert.strictEqual(err.error.errorCode.number, errorCode); + } + }); + + it("Succeeds with missing optional accounts at the end with the feature on", async () => { + const allowMissingOptionals = anchor.workspace + .AllowMissingOptionals as Program; + const doStuffIx = await allowMissingOptionals.methods + .doStuff() + .accounts({ + payer, + systemProgram, + optional2: null, + }) + .instruction(); + doStuffIx.keys.pop(); + doStuffIx.keys.pop(); + const doStuffTxn = new web3.Transaction().add(doStuffIx); + await anchorProvider.sendAndConfirm(doStuffTxn); + }); + }); + describe("Initialize tests", async () => { it("Initialize with required null fails anchor-ts validation", async () => { const [requiredKeypair, createRequiredIx] = await createRequired(); From 37204dfb54cc86285fdcee92bbfa7fa44642439f Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sun, 11 Dec 2022 14:42:09 -0600 Subject: [PATCH 108/109] fix client tests --- client/example/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/example/src/main.rs b/client/example/src/main.rs index d98ae74a1d..90e4a65090 100644 --- a/client/example/src/main.rs +++ b/client/example/src/main.rs @@ -12,6 +12,8 @@ use basic_2::instruction as basic_2_instruction; use basic_2::Counter; use events::instruction as events_instruction; use events::MyEvent; +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; From 7a6f594c7453dd9e7499e78841964f2bf30928bf Mon Sep 17 00:00:00 2001 From: Sammy <41593264+stegaBOB@users.noreply.github.com> Date: Sun, 11 Dec 2022 14:45:20 -0600 Subject: [PATCH 109/109] add bnjs types to tests --- tests/package.json | 1 + tests/yarn.lock | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/tests/package.json b/tests/package.json index c96d9e7d44..e24b4bc17e 100644 --- a/tests/package.json +++ b/tests/package.json @@ -57,6 +57,7 @@ "@types/chai": "^4.3.0", "@types/mocha": "^9.1.0", "@types/node": "^14.14.37", + "@types/bn.js": "^5.1.1", "chai": "^4.3.4", "mocha": "^10.0.0", "prettier": "^2.5.1", diff --git a/tests/yarn.lock b/tests/yarn.lock index f54c222f48..a5efec447b 100644 --- a/tests/yarn.lock +++ b/tests/yarn.lock @@ -197,6 +197,13 @@ rpc-websockets "^7.5.0" superstruct "^0.14.2" +"@types/bn.js@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" + integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== + dependencies: + "@types/node" "*" + "@types/chai@^4.3.0": version "4.3.0" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc"