use solana_sdk::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
};
#[cfg(anchor_lang)]
use anchor_lang::{Id, InstructionData, ToAccountMetas};
pub trait Program {
fn id(&self) -> &Pubkey;
}
impl<P: Program> Program for &P {
fn id(&self) -> &Pubkey {
(**self).id()
}
}
pub trait ProgramExt: Program {
fn instruction(&self, data: Vec<u8>) -> InstructionBuilder<Self>
where
Self: Sized,
{
InstructionBuilder {
program: self,
data,
accounts: vec![],
}
}
#[cfg(anchor_lang)]
fn anchor_instruction(&self, args: impl InstructionData) -> InstructionBuilder<Self>
where
Self: Sized,
{
self.instruction(args.data())
}
#[cfg(anchor_lang)]
fn anchor_accounts(
&self,
accounts: impl ToAccountMetas,
convert_optional: bool,
) -> Vec<AccountMeta>
where
Self: Id,
{
if convert_optional {
fix_optional_account_metas(accounts, &<Self as Id>::id(), self.id())
} else {
accounts.to_account_metas(None)
}
}
}
impl<P: ?Sized + Program> ProgramExt for P {}
#[derive(Debug, Clone)]
pub struct InstructionBuilder<'a, P> {
program: &'a P,
data: Vec<u8>,
accounts: Vec<AccountMeta>,
}
impl<P> InstructionBuilder<'_, P> {
pub fn accounts(mut self, mut accounts: Vec<AccountMeta>) -> Self {
self.accounts.append(&mut accounts);
self
}
}
impl<P: Program> InstructionBuilder<'_, P> {
pub fn build(self) -> Instruction {
Instruction {
program_id: *self.program.id(),
accounts: self.accounts,
data: self.data,
}
}
}
#[cfg(anchor_lang)]
impl<P: Program + Id> InstructionBuilder<'_, P> {
pub fn anchor_accounts(self, accounts: impl ToAccountMetas, convert_optional: bool) -> Self {
let accounts = self.program.anchor_accounts(accounts, convert_optional);
self.accounts(accounts)
}
}
#[cfg(anchor_lang)]
pub fn fix_optional_account_metas(
accounts: impl ToAccountMetas,
original: &Pubkey,
current: &Pubkey,
) -> Vec<AccountMeta> {
let mut metas = accounts.to_account_metas(None);
if *original == *current {
return metas;
}
metas.iter_mut().for_each(|meta| {
if !meta.is_signer && !meta.is_writable && meta.pubkey == *original {
meta.pubkey = *current;
}
});
metas
}