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 AccountMetas 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

source

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 ExtraAccountMetas

source

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 ExtraAccountMetas

source

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!

source

pub fn size_of(num_items: usize) -> Result<usize, ProgramError>

Get the byte size required to hold num_items items

source

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

source

pub async fn add_to_instruction<T: SplDiscriminate, F, Fut>( instruction: &mut Instruction, fetch_account_data_fn: F, data: &[u8], ) -> Result<(), ProgramError>
where F: Fn(Pubkey) -> Fut, Fut: Future<Output = AccountDataResult>,

Add the additional account metas to an existing instruction

source

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§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
source§

impl<T> Same for T

§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

source§

fn vzip(self) -> V