use anchor_lang::{InstructionData, ToAccountMetas};
use solana_program::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
};
#[derive(Copy, Clone)]
pub struct Program {
program_id: Pubkey,
}
impl Program {
pub fn new(program_id: Pubkey) -> Self {
Self { program_id }
}
pub fn accounts<T: ToAccountMetas>(self, accounts: T) -> InstructionBuilder {
InstructionBuilder {
program_id: self.program_id,
accounts: accounts.to_account_metas(None),
data: Vec::new(),
}
}
pub fn id(&self) -> Pubkey {
self.program_id
}
}
pub struct InstructionBuilder {
program_id: Pubkey,
accounts: Vec<AccountMeta>,
data: Vec<u8>,
}
impl InstructionBuilder {
pub fn args<T: InstructionData>(mut self, args: T) -> Self {
self.data = args.data();
self
}
pub fn instruction(self) -> Result<Instruction, Box<dyn std::error::Error>> {
if self.data.is_empty() {
return Err("No instruction data provided. Call .args() before .instruction()".into());
}
Ok(Instruction {
program_id: self.program_id,
accounts: self.accounts,
data: self.data,
})
}
}
#[cfg(test)]
mod tests {
use super::Program;
use anchor_lang::{prelude::*, InstructionData, ToAccountMetas};
use solana_program::{instruction::AccountMeta, pubkey::Pubkey};
struct TestAccounts {
user: Pubkey,
account: Pubkey,
}
impl ToAccountMetas for TestAccounts {
fn to_account_metas(&self, _is_signer: Option<bool>) -> Vec<AccountMeta> {
vec![
AccountMeta::new(self.user, true),
AccountMeta::new(self.account, false),
]
}
}
#[derive(AnchorSerialize, AnchorDeserialize)]
struct TestArgs {
amount: u64,
}
impl anchor_lang::Discriminator for TestArgs {
const DISCRIMINATOR: &'static [u8] = &[1, 2, 3, 4, 5, 6, 7, 8];
}
impl InstructionData for TestArgs {
fn data(&self) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(Self::DISCRIMINATOR);
self.serialize(&mut data).unwrap();
data
}
}
#[test]
fn test_simplified_syntax() {
let program_id = Pubkey::new_unique();
let user = Pubkey::new_unique();
let account = Pubkey::new_unique();
let program = Program::new(program_id);
let ix = program
.accounts(TestAccounts { user, account })
.args(TestArgs { amount: 100 })
.instruction()
.unwrap();
assert_eq!(ix.program_id, program_id);
assert_eq!(ix.accounts.len(), 2);
assert!(ix.data.len() > 8);
}
}