use anchor_lang::AnchorSerialize;
use sha2::{Digest, Sha256};
use solana_program::instruction::{AccountMeta, Instruction};
use solana_program::pubkey::Pubkey;
pub fn build_anchor_instruction<T>(
program_id: &Pubkey,
instruction_name: &str,
accounts: Vec<AccountMeta>,
args: T,
) -> Result<Instruction, Box<dyn std::error::Error>>
where
T: AnchorSerialize,
{
let discriminator = calculate_anchor_discriminator(instruction_name);
let mut data = discriminator.to_vec();
args.serialize(&mut data)?;
Ok(Instruction {
program_id: *program_id,
accounts,
data,
})
}
pub fn calculate_anchor_discriminator(instruction_name: &str) -> [u8; 8] {
let mut hasher = Sha256::new();
hasher.update(format!("global:{}", instruction_name));
let hash = hasher.finalize();
let mut discriminator = [0u8; 8];
discriminator.copy_from_slice(&hash[..8]);
discriminator
}
#[cfg(test)]
mod tests {
use super::*;
use borsh::BorshSerialize;
#[test]
fn test_discriminator_calculation() {
let make_discriminator = calculate_anchor_discriminator("make");
let expected_make = [0x8a, 0xe3, 0xe8, 0x4d, 0xdf, 0xa6, 0x60, 0xc5];
assert_eq!(make_discriminator, expected_make);
let test_discriminator = calculate_anchor_discriminator("test");
assert_eq!(test_discriminator.len(), 8);
assert_ne!(make_discriminator, test_discriminator);
}
#[test]
fn test_instruction_building() {
#[derive(BorshSerialize)]
struct TestArgs {
value: u64,
}
impl AnchorSerialize for TestArgs {
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
BorshSerialize::serialize(self, writer)
}
}
let program_id = Pubkey::new_unique();
let accounts = vec![
AccountMeta::new(Pubkey::new_unique(), true),
AccountMeta::new_readonly(Pubkey::new_unique(), false),
];
let args = TestArgs { value: 42 };
let instruction = build_anchor_instruction(
&program_id,
"test",
accounts.clone(),
args,
).unwrap();
assert_eq!(instruction.program_id, program_id);
assert_eq!(instruction.accounts.len(), 2);
assert!(instruction.data.len() >= 8); }
}