1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//! On-chain program invoke helper to perform on-chain `execute` with correct
//! accounts

use {
    crate::{error::TransferHookError, get_extra_account_metas_address, instruction},
    solana_program::{
        account_info::AccountInfo,
        entrypoint::ProgramResult,
        instruction::{AccountMeta, Instruction},
        program::invoke,
        pubkey::Pubkey,
    },
    spl_tlv_account_resolution::state::ExtraAccountMetaList,
};
/// Helper to CPI into a transfer-hook program on-chain, looking through the
/// additional account infos to create the proper instruction
pub fn invoke_execute<'a>(
    program_id: &Pubkey,
    source_info: AccountInfo<'a>,
    mint_info: AccountInfo<'a>,
    destination_info: AccountInfo<'a>,
    authority_info: AccountInfo<'a>,
    additional_accounts: &[AccountInfo<'a>],
    amount: u64,
) -> ProgramResult {
    let validation_pubkey = get_extra_account_metas_address(mint_info.key, program_id);
    let validation_info = additional_accounts
        .iter()
        .find(|&x| *x.key == validation_pubkey)
        .ok_or(TransferHookError::IncorrectAccount)?;
    let mut cpi_instruction = instruction::execute(
        program_id,
        source_info.key,
        mint_info.key,
        destination_info.key,
        authority_info.key,
        &validation_pubkey,
        amount,
    );

    let mut cpi_account_infos = vec![
        source_info,
        mint_info,
        destination_info,
        authority_info,
        validation_info.clone(),
    ];
    ExtraAccountMetaList::add_to_cpi_instruction::<instruction::ExecuteInstruction>(
        &mut cpi_instruction,
        &mut cpi_account_infos,
        &validation_info.try_borrow_data()?,
        additional_accounts,
    )?;
    invoke(&cpi_instruction, &cpi_account_infos)
}

/// Helper to add accounts required for the transfer-hook program on-chain,
/// looking through the additional account infos to add the proper accounts
pub fn add_cpi_accounts_for_execute<'a>(
    cpi_instruction: &mut Instruction,
    cpi_account_infos: &mut Vec<AccountInfo<'a>>,
    mint_pubkey: &Pubkey,
    program_id: &Pubkey,
    additional_accounts: &[AccountInfo<'a>],
) -> ProgramResult {
    let validation_pubkey = get_extra_account_metas_address(mint_pubkey, program_id);
    let validation_info = additional_accounts
        .iter()
        .find(|&x| *x.key == validation_pubkey)
        .ok_or(TransferHookError::IncorrectAccount)?;

    let program_info = additional_accounts
        .iter()
        .find(|&x| x.key == program_id)
        .ok_or(TransferHookError::IncorrectAccount)?;

    ExtraAccountMetaList::add_to_cpi_instruction::<instruction::ExecuteInstruction>(
        cpi_instruction,
        cpi_account_infos,
        &validation_info.try_borrow_data()?,
        additional_accounts,
    )?;
    // The onchain helpers pull out the required accounts from an opaque
    // slice by pubkey, so the order doesn't matter here!
    cpi_account_infos.push(validation_info.clone());
    cpi_account_infos.push(program_info.clone());
    cpi_instruction
        .accounts
        .push(AccountMeta::new_readonly(validation_pubkey, false));
    cpi_instruction
        .accounts
        .push(AccountMeta::new_readonly(*program_id, false));
    Ok(())
}