Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

to_account_metas()macro with Vec::with_capacity(#length) #2399

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions lang/src/accounts/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, ToAccountMeta, ToAccountMetas,
};
use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta;
Expand Down Expand Up @@ -356,16 +356,23 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsCl
}
}

impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountMetas
impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountMeta
for Account<'info, T>
{
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
fn to_account_meta(&self, is_signer: Option<bool>) -> AccountMeta {
let is_signer = is_signer.unwrap_or(self.info.is_signer);
let meta = match self.info.is_writable {
match self.info.is_writable {
false => AccountMeta::new_readonly(*self.info.key, is_signer),
true => AccountMeta::new(*self.info.key, is_signer),
};
vec![meta]
}
}
}

impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountMetas
for Account<'info, T>
{
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
vec![self.to_account_meta(is_signer)]
}
}

Expand Down
17 changes: 11 additions & 6 deletions lang/src/accounts/account_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, ToAccountMeta, ToAccountMetas};
use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta;
use solana_program::pubkey::Pubkey;
Expand All @@ -26,14 +26,19 @@ impl<'info> Accounts<'info> for AccountInfo<'info> {
}
}

impl<'info> ToAccountMetas for AccountInfo<'info> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
impl<'info> ToAccountMeta for AccountInfo<'info> {
fn to_account_meta(&self, is_signer: Option<bool>) -> AccountMeta {
let is_signer = is_signer.unwrap_or(self.is_signer);
let meta = match self.is_writable {
match self.is_writable {
false => AccountMeta::new_readonly(*self.key, is_signer),
true => AccountMeta::new(*self.key, is_signer),
};
vec![meta]
}
}
}

impl<'info> ToAccountMetas for AccountInfo<'info> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
vec![self.to_account_meta(is_signer)]
}
}

Expand Down
17 changes: 11 additions & 6 deletions lang/src/accounts/account_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
ToAccountMeta, ToAccountMetas, ZeroCopy,
};
use arrayref::array_ref;
use solana_program::account_info::AccountInfo;
Expand Down Expand Up @@ -253,14 +253,19 @@ impl<'info, T: ZeroCopy + Owner> AccountsClose<'info> for AccountLoader<'info, T
}
}

impl<'info, T: ZeroCopy + Owner> ToAccountMetas for AccountLoader<'info, T> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
impl<'info, T: ZeroCopy + Owner> ToAccountMeta for AccountLoader<'info, T> {
fn to_account_meta(&self, is_signer: Option<bool>) -> AccountMeta {
let is_signer = is_signer.unwrap_or(self.acc_info.is_signer);
let meta = match self.acc_info.is_writable {
match self.acc_info.is_writable {
false => AccountMeta::new_readonly(*self.acc_info.key, is_signer),
true => AccountMeta::new(*self.acc_info.key, is_signer),
};
vec![meta]
}
}
}

impl<'info, T: ZeroCopy + Owner> ToAccountMetas for AccountLoader<'info, T> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
vec![self.to_account_meta(is_signer)]
}
}

Expand Down
10 changes: 9 additions & 1 deletion lang/src/accounts/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
//! }
//! ```

use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas};
use crate::{
Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMeta, ToAccountMetas,
};
use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta;
use solana_program::pubkey::Pubkey;
Expand Down Expand Up @@ -44,6 +46,12 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Box<T> {
}
}

impl<T: ToAccountMeta> ToAccountMeta for Box<T> {
fn to_account_meta(&self, is_signer: Option<bool>) -> AccountMeta {
T::to_account_meta(self, is_signer)
}
}

impl<T: ToAccountMetas> ToAccountMetas for Box<T> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
T::to_account_metas(self, is_signer)
Expand Down
11 changes: 10 additions & 1 deletion lang/src/accounts/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use solana_program::instruction::AccountMeta;
use solana_program::pubkey::Pubkey;

use crate::{
error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas,
error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMeta,
ToAccountMetas,
};

impl<'info, T: Accounts<'info>> Accounts<'info> for Option<T> {
Expand Down Expand Up @@ -62,6 +63,14 @@ impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option<T> {
}
}

impl<T: ToAccountMeta> ToAccountMeta for Option<T> {
fn to_account_meta(&self, is_signer: Option<bool>) -> AccountMeta {
self.as_ref()
.expect("Cannot run `to_account_meta` on None")
.to_account_meta(is_signer)
}
}

impl<T: ToAccountMetas> ToAccountMetas for Option<T> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
self.as_ref()
Expand Down
18 changes: 12 additions & 6 deletions lang/src/accounts/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, ToAccountInfos, ToAccountMeta,
ToAccountMetas,
};
use solana_program::account_info::AccountInfo;
use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState};
Expand Down Expand Up @@ -158,14 +159,19 @@ where
}
}

impl<'info, T: Id + Clone> ToAccountMetas for Program<'info, T> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
impl<'info, T: Id + Clone> ToAccountMeta for Program<'info, T> {
fn to_account_meta(&self, is_signer: Option<bool>) -> AccountMeta {
let is_signer = is_signer.unwrap_or(self.info.is_signer);
let meta = match self.info.is_writable {
match self.info.is_writable {
false => AccountMeta::new_readonly(*self.info.key, is_signer),
true => AccountMeta::new(*self.info.key, is_signer),
};
vec![meta]
}
}
}

impl<'info, T: Id + Clone> ToAccountMetas for Program<'info, T> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
vec![self.to_account_meta(is_signer)]
}
}

Expand Down
17 changes: 11 additions & 6 deletions lang/src/accounts/signer.rs
Original file line number Diff line number Diff line change
@@ -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, ToAccountMeta, ToAccountMetas};
use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta;
use solana_program::pubkey::Pubkey;
Expand Down Expand Up @@ -74,14 +74,19 @@ impl<'info> Accounts<'info> for Signer<'info> {

impl<'info> AccountsExit<'info> for Signer<'info> {}

impl<'info> ToAccountMetas for Signer<'info> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
impl<'info> ToAccountMeta for Signer<'info> {
fn to_account_meta(&self, is_signer: Option<bool>) -> AccountMeta {
let is_signer = is_signer.unwrap_or(self.info.is_signer);
let meta = match self.info.is_writable {
match self.info.is_writable {
false => AccountMeta::new_readonly(*self.info.key, is_signer),
true => AccountMeta::new(*self.info.key, is_signer),
};
vec![meta]
}
}
}

impl<'info> ToAccountMetas for Signer<'info> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
vec![self.to_account_meta(is_signer)]
}
}

Expand Down
15 changes: 10 additions & 5 deletions lang/src/accounts/system_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,19 @@ impl<'info> Accounts<'info> for SystemAccount<'info> {

impl<'info> AccountsExit<'info> for SystemAccount<'info> {}

impl<'info> ToAccountMetas for SystemAccount<'info> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
impl<'info> ToAccountMeta for SystemAccount<'info> {
fn to_account_meta(&self, is_signer: Option<bool>) -> AccountMeta {
let is_signer = is_signer.unwrap_or(self.info.is_signer);
let meta = match self.info.is_writable {
match self.info.is_writable {
false => AccountMeta::new_readonly(*self.info.key, is_signer),
true => AccountMeta::new(*self.info.key, is_signer),
};
vec![meta]
}
}
}

impl<'info> ToAccountMetas for SystemAccount<'info> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
vec![self.to_account_meta(is_signer)]
}
}

Expand Down
8 changes: 7 additions & 1 deletion lang/src/accounts/sysvar.rs
Original file line number Diff line number Diff line change
@@ -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, ToAccountMeta, ToAccountMetas};
use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta;
use solana_program::pubkey::Pubkey;
Expand Down Expand Up @@ -82,6 +82,12 @@ impl<'info, T: solana_program::sysvar::Sysvar> Accounts<'info> for Sysvar<'info,
}
}

impl<'info, T: solana_program::sysvar::Sysvar> ToAccountMeta for Sysvar<'info, T> {
fn to_account_meta(&self, _is_signer: Option<bool>) -> AccountMeta {
AccountMeta::new_readonly(*self.info.key, false)
}
}

impl<'info, T: solana_program::sysvar::Sysvar> ToAccountMetas for Sysvar<'info, T> {
fn to_account_metas(&self, _is_signer: Option<bool>) -> Vec<AccountMeta> {
vec![AccountMeta::new_readonly(*self.info.key, false)]
Expand Down
17 changes: 11 additions & 6 deletions lang/src/accounts/unchecked_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, ToAccountMeta, ToAccountMetas};
use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta;
use solana_program::pubkey::Pubkey;
Expand Down Expand Up @@ -37,14 +37,19 @@ impl<'info> Accounts<'info> for UncheckedAccount<'info> {
}
}

impl<'info> ToAccountMetas for UncheckedAccount<'info> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
impl<'info> ToAccountMeta for UncheckedAccount<'info> {
fn to_account_meta(&self, is_signer: Option<bool>) -> AccountMeta {
let is_signer = is_signer.unwrap_or(self.is_signer);
let meta = match self.is_writable {
match self.is_writable {
false => AccountMeta::new_readonly(*self.key, is_signer),
true => AccountMeta::new(*self.key, is_signer),
};
vec![meta]
}
}
}

impl<'info> ToAccountMetas for UncheckedAccount<'info> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
vec![self.to_account_meta(is_signer)]
}
}

Expand Down
16 changes: 14 additions & 2 deletions lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ pub trait ToAccountMetas {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta>;
}

/// Transformation to an
/// [`AccountMeta`](../solana_program/instruction/struct.AccountMeta.html)
/// struct.
pub trait ToAccountMeta {
/// `is_signer` is given as an optional override for the signer meta field.
/// This covers the edge case when a program-derived-address needs to relay
/// a transaction from a client to another program but sign the transaction
/// before the relay. The client cannot mark the field as a signer, and so
/// we have to override the is_signer meta field given by the client.
fn to_account_meta(&self, is_signer: Option<bool>) -> AccountMeta;
}

/// Transformation to
/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html)
/// structs.
Expand Down Expand Up @@ -252,8 +264,8 @@ pub mod prelude {
require, require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq,
require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source,
system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts,
AccountsClose, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, InitSpace, Key, Owner,
ProgramData, Result, Space, ToAccountInfo, ToAccountInfos, ToAccountMetas,
AccountsClose, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner,
ProgramData, Result, ToAccountInfo, ToAccountInfos, ToAccountMeta, ToAccountMetas,
};
pub use anchor_attribute_error::*;
pub use borsh;
Expand Down
10 changes: 7 additions & 3 deletions lang/syn/src/codegen/accounts/to_account_metas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,30 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
if is_optional {
quote! {
if let Some(#name) = &self.#name {
account_metas.extend(#name.to_account_metas(#is_signer));
account_metas.push(#name.to_account_meta(#is_signer));
} else {
account_metas.push(AccountMeta::new_readonly(crate::ID, false));
}
}
} else {
quote! {
account_metas.extend(self.#name.to_account_metas(#is_signer));
account_metas.push(self.#name.to_account_meta(#is_signer));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeh, this basically breaks how composite accounts work. Since you're calling to_account_metas on the another account context type which has itself used the derive(Accounts) macro.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There may be a way to handle this by checking if the type is a composite account.

Alternatively, would be nice if the derive(Accounts) macro also implemented Iterator and iterated through the accounts in order. This could allow you to call to_account_meta on each element of a composite account.

}
}
})
.collect();

let length = to_acc_metas.len();

let (impl_gen, ty_gen, where_clause) = accs.generics.split_for_impl();

quote! {
#[automatically_derived]
impl #impl_gen anchor_lang::ToAccountMetas for #name #ty_gen #where_clause{
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<anchor_lang::solana_program::instruction::AccountMeta> {
let mut account_metas = vec![];
use anchor_lang::ToAccountMeta;

let mut account_metas = Vec::with_capacity(#length);

#(#to_acc_metas)*

Expand Down