pub struct ExtraAccountMetaList;
Expand description
Stateless helper for storing additional accounts required for an instruction.
This struct works with any SplDiscriminate
, and stores the extra accounts
needed for that specific instruction, using the given ArrayDiscriminator
as the type-length-value ArrayDiscriminator
, and then storing all of the
given AccountMeta
s as a zero-copy slice.
Sample usage:
use {
futures_util::TryFutureExt,
solana_client::nonblocking::rpc_client::RpcClient,
solana_program::{
account_info::AccountInfo, instruction::{AccountMeta, Instruction},
pubkey::Pubkey
},
spl_discriminator::{ArrayDiscriminator, SplDiscriminate},
spl_tlv_account_resolution::{
account::ExtraAccountMeta,
seeds::Seed,
state::{AccountDataResult, AccountFetchError, ExtraAccountMetaList}
},
};
struct MyInstruction;
impl SplDiscriminate for MyInstruction {
// Give it a unique discriminator, can also be generated using a hash function
const SPL_DISCRIMINATOR: ArrayDiscriminator = ArrayDiscriminator::new([1; ArrayDiscriminator::LENGTH]);
}
// actually put it in the additional required account keys and signer / writable
let extra_metas = [
AccountMeta::new(Pubkey::new_unique(), false).into(),
AccountMeta::new_readonly(Pubkey::new_unique(), false).into(),
ExtraAccountMeta::new_with_seeds(
&[
Seed::Literal {
bytes: b"some_string".to_vec(),
},
Seed::InstructionData {
index: 1,
length: 1, // u8
},
Seed::AccountKey { index: 1 },
],
false,
true,
).unwrap(),
ExtraAccountMeta::new_external_pda_with_seeds(
0,
&[Seed::AccountKey { index: 2 }],
false,
false,
).unwrap(),
];
// assume that this buffer is actually account data, already allocated to `account_size`
let account_size = ExtraAccountMetaList::size_of(extra_metas.len()).unwrap();
let mut buffer = vec![0; account_size];
// Initialize the structure for your instruction
ExtraAccountMetaList::init::<MyInstruction>(&mut buffer, &extra_metas).unwrap();
// Off-chain, you can add the additional accounts directly from the account data
// You need to provide the resolver a way to fetch account data off-chain
struct MyClient {
client: RpcClient,
}
impl MyClient {
pub fn new() -> Self {
Self {
client: RpcClient::new_mock("succeeds".to_string()),
}
}
pub async fn get_account_data(&self, address: Pubkey) -> AccountDataResult {
self.client.get_account(&address)
.await
.map(|acct| Some(acct.data))
.map_err(|e| Box::new(e) as AccountFetchError)
}
}
let client = MyClient::new();
let program_id = Pubkey::new_unique();
let mut instruction = Instruction::new_with_bytes(program_id, &[0, 1, 2], vec![]);
// Now use the resolver to add the additional accounts off-chain
ExtraAccountMetaList::add_to_instruction::<MyInstruction, _, _>(
&mut instruction,
|address: Pubkey| client.get_account_data(address),
&buffer,
)
.await;
// On-chain, you can add the additional accounts *and* account infos
let mut cpi_instruction = Instruction::new_with_bytes(program_id, &[0, 1, 2], vec![]);
let mut cpi_account_infos = vec![]; // assume the other required account infos are already included
let remaining_account_infos: &[AccountInfo<'_>] = &[]; // these are the account infos provided to the instruction that are *not* part of any other known interface
ExtraAccountMetaList::add_to_cpi_instruction::<MyInstruction>(
&mut cpi_instruction,
&mut cpi_account_infos,
&buffer,
&remaining_account_infos,
);
Implementations§
source§impl ExtraAccountMetaList
impl ExtraAccountMetaList
sourcepub fn init<T: SplDiscriminate>(
data: &mut [u8],
extra_account_metas: &[ExtraAccountMeta],
) -> Result<(), ProgramError>
pub fn init<T: SplDiscriminate>( data: &mut [u8], extra_account_metas: &[ExtraAccountMeta], ) -> Result<(), ProgramError>
Initialize pod slice data for the given instruction and its required
list of ExtraAccountMeta
s
sourcepub fn update<T: SplDiscriminate>(
data: &mut [u8],
extra_account_metas: &[ExtraAccountMeta],
) -> Result<(), ProgramError>
pub fn update<T: SplDiscriminate>( data: &mut [u8], extra_account_metas: &[ExtraAccountMeta], ) -> Result<(), ProgramError>
Update pod slice data for the given instruction and its required
list of ExtraAccountMeta
s
sourcepub fn unpack_with_tlv_state<'a, T: SplDiscriminate>(
tlv_state: &'a TlvStateBorrowed<'_>,
) -> Result<PodSlice<'a, ExtraAccountMeta>, ProgramError>
pub fn unpack_with_tlv_state<'a, T: SplDiscriminate>( tlv_state: &'a TlvStateBorrowed<'_>, ) -> Result<PodSlice<'a, ExtraAccountMeta>, ProgramError>
Get the underlying PodSlice<ExtraAccountMeta>
from an unpacked TLV
Due to lifetime annoyances, this function can’t just take in the bytes,
since then we would be returning a reference to a locally created
TlvStateBorrowed
. I hope there’s a better way to do this!
sourcepub fn size_of(num_items: usize) -> Result<usize, ProgramError>
pub fn size_of(num_items: usize) -> Result<usize, ProgramError>
Get the byte size required to hold num_items
items
sourcepub fn check_account_infos<T: SplDiscriminate>(
account_infos: &[AccountInfo<'_>],
instruction_data: &[u8],
program_id: &Pubkey,
data: &[u8],
) -> Result<(), ProgramError>
pub fn check_account_infos<T: SplDiscriminate>( account_infos: &[AccountInfo<'_>], instruction_data: &[u8], program_id: &Pubkey, data: &[u8], ) -> Result<(), ProgramError>
Checks provided account infos against validation data, using instruction data and program ID to resolve any dynamic PDAs if necessary.
Note: this function will also verify all extra required accounts have been provided in the correct order
sourcepub async fn add_to_instruction<T: SplDiscriminate, F, Fut>(
instruction: &mut Instruction,
fetch_account_data_fn: F,
data: &[u8],
) -> Result<(), ProgramError>
pub async fn add_to_instruction<T: SplDiscriminate, F, Fut>( instruction: &mut Instruction, fetch_account_data_fn: F, data: &[u8], ) -> Result<(), ProgramError>
Add the additional account metas to an existing instruction
sourcepub fn add_to_cpi_instruction<'a, T: SplDiscriminate>(
cpi_instruction: &mut Instruction,
cpi_account_infos: &mut Vec<AccountInfo<'a>>,
data: &[u8],
account_infos: &[AccountInfo<'a>],
) -> Result<(), ProgramError>
pub fn add_to_cpi_instruction<'a, T: SplDiscriminate>( cpi_instruction: &mut Instruction, cpi_account_infos: &mut Vec<AccountInfo<'a>>, data: &[u8], account_infos: &[AccountInfo<'a>], ) -> Result<(), ProgramError>
Add the additional account metas and account infos for a CPI
Auto Trait Implementations§
impl Freeze for ExtraAccountMetaList
impl RefUnwindSafe for ExtraAccountMetaList
impl Send for ExtraAccountMetaList
impl Sync for ExtraAccountMetaList
impl Unpin for ExtraAccountMetaList
impl UnwindSafe for ExtraAccountMetaList
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more