I am trying to implement an additional transfer of the token that has a transfer hook from within the transfer hook callback. In particular, I am trying to send an additional token from the source account to one of the accounts passed to the transfer hook.
I've put a guard in place to check when i'm in the transfer hook to stop this being an infinite recursion, but the instruction is failing before reaching the transfer hook for the second time, it is already failing at the call to transfer checked, claiming that the source accounts writable status has been escalated, you can find the instruction log here: https://explorer.solana.com/tx/593fyLLsLNhyWcYDekZWPdC393KF8BH3HQbgEKZjp23FZtKS3dnszmn1QdBsU7bXM6oS999PdnnpcJAqF8iYLRiH?cluster=devnet
below is my process execute function, i'm not really sure what i'm doing wrong as that account has writable status:
/// Processes an [Execute](enum.TransferHookInstruction.html) instruction.
pub fn process_execute<'a>(
program_id: &Pubkey,
accounts: &'a [AccountInfo<'a>],
amount: u64,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
msg!("in execute");
let source_account_info = next_account_info(account_info_iter)?;
let mint_info = next_account_info(account_info_iter)?;
let destination_account_info = next_account_info(account_info_iter)?;
let _authority_info = next_account_info(account_info_iter)?;
let extra_account_metas_info = next_account_info(account_info_iter)?;
let launch_data_account_info = next_account_info(account_info_iter)?;
let pda_account_info = next_account_info(account_info_iter)?;
let team_token_account_info = next_account_info(account_info_iter)?;
let transfer_hook_program = next_account_info(account_info_iter)?;
let token_program_2022 = next_account_info(account_info_iter)?;
let launch_data =
state::LaunchData::try_from_slice(&launch_data_account_info.data.borrow()[..])?;
let is_transferring: u8 = u8::try_from_slice(&pda_account_info.data.borrow()[..])?;
msg!("is transferring? {}", is_transferring);
if is_transferring == 1 {
msg!("already in the middle of a hook transfer, don't recurse");
return Ok(());
}
1u8.serialize(&mut &mut pda_account_info.data.borrow_mut()[..])?;
msg!("transfer tokens");
let mut transfer_ix = spl_token_2022::instruction::transfer_checked(
token_program_2022.key,
source_account_info.key,
mint_info.key,
team_token_account_info.key,
pda_account_info.key,
&[],
1,
launch_data.decimals,
)?;
transfer_ix
.accounts
.push(AccountMeta::new_readonly(*program_id, false));
transfer_ix.accounts.push(AccountMeta::new_readonly(
*extra_account_metas_info.key,
false,
));
transfer_ix
.accounts
.push(AccountMeta::new(*launch_data_account_info.key, false));
transfer_ix
.accounts
.push(AccountMeta::new(*pda_account_info.key, false));
transfer_ix
.accounts
.push(AccountMeta::new(*team_token_account_info.key, false));
transfer_ix
.accounts
.push(AccountMeta::new(*transfer_hook_program.key, false));
transfer_ix
.accounts
.push(AccountMeta::new(*token_program_2022.key, false));
let account_infos = vec![
source_account_info.clone(),
mint_info.clone(),
team_token_account_info.clone(),
pda_account_info.clone(),
token_program_2022.clone(),
launch_data_account_info.clone(),
transfer_hook_program.clone(),
];
invoke_signed(
&transfer_ix,
&account_infos,
&[&[&mint_info.key.to_bytes(), b"pda", &[pda_bump_seed]]],
)?;
0u8.serialize(&mut &mut pda_account_info.data.borrow_mut()[..])?;
Ok(())
}
Any suggestions would be most welcome, i was trying to follow the gist of this: https://solana.com/developers/guides/token-extensions/transfer-hook#transfer-hook-with-wsol-transfer-fee-advanced-example
which transfers wrapped sol belonging to the sender to a different account, but just want to do it for the token being transferred.