0

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.

1 Answer 1

0

The Solana runtime does not allow re-entrancy (except a program calling into itself), so you cannot call a program up the stack. For example, this is not allowed:

token-2022 -> transfer-hook -> token-2022

The transfer-hook example program that you linked uses spl-token rather than token-2022 for exactly that reason.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.