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