0

I have this code to transfer sol via a contract and via web3 in ReactJS -

const transferSol = async () => {

        if (!publicKey) throw new WalletNotConnectedError();

             const programId = new PublicKey('7XyzLTMw7nBzoZnYoxauCzv11ruJee3HqjbxCyDfYb52'); //transfer sol
        const {
           context: { slot: minContextSlot },
            value: { blockhash, lastValidBlockHeight }
        } = await connection.getLatestBlockhashAndContext();

        const fromPublicKey = new PublicKey(publicKey);
            const toPublicKey = new PublicKey(solAddr);

    const amount = new BN(solAmount * LAMPORTS_PER_SOL);
    const tx = new Transaction({
      recentBlockhash: blockhash,
      feePayer: publicKey,
    })
 //const blockhashInfo = await connection.getLatestBlockhash();
// const tx = new Transaction({
//     ...blockhashInfo, feePayer: publicKey,
// });  

console.log("from pub key ", fromPublicKey.toString());
console.log("from pub key ", publicKey.toString());
console.log("to pub key ", toPublicKey.toString());
console.log("system programid ", SystemProgram.programId.toString());

      const transactionInstruction = 
         new TransactionInstruction({
        programId,
        keys: [
          {pubkey: publicKey, isSigner: false, isWritable: true},
          {pubkey: toPublicKey, isSigner: false, isWritable: true},
          { pubkey: SystemProgram.programId, isSigner: false, isWritable: true},
          // add any additional keys here
        ],
        data: encodeIxData({ discriminator: 0, amount }), 
      }); 

      tx.add(transactionInstruction);
      const simResult = await connection.simulateTransaction(tx);
      console.log(simResult);     
console.log("tx === ", tx);
            const signature = await sendTransaction(tx, connection, {minContextSlot});
            const signatureResult = await connection.confirmTransaction({blockhash, lastValidBlockHeight, signature});
            console.log(" signature ", signature);
            setTxnSignature(signature);
}


This used via the browser with Phantom (chrome plugin). There is an error "NotEnoughKeys" -

from pub key  5EH28q5BE8GuYzUTyHuLEqzTJATxoshvJZkNWzxuLEsk
App.tsx:241 from pub key  5EH28q5BE8GuYzUTyHuLEqzTJATxoshvJZkNWzxuLEsk
App.tsx:242 to pub key  4dGDp3BuTaXiqJwwJhh9abUBBm6hMhRkidttr5N4Cemm
App.tsx:243 system programid  11111111111111111111111111111111
App.tsx:259 
{context: {…}, value: {…}}
context: {apiVersion: '1.14.13', slot: 198071637}
value: accounts: 
null err: {InstructionError: Array(2)}
logs: Array(3)
0
: 
"Program 7XyzLTMw7nBzoZnYoxauCzv11ruJee3HqjbxCyDfYb52 invoke [1]"
1
: 
"Program 7XyzLTMw7nBzoZnYoxauCzv11ruJee3HqjbxCyDfYb52 consumed 771 of 200000 compute units"
2
: 
"Program 7XyzLTMw7nBzoZnYoxauCzv11ruJee3HqjbxCyDfYb52 failed: insufficient account keys for instruction"
 

I checked the keys and the addr for the contract and they look to be OK. The Rust code for the contract -

use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    pubkey::Pubkey,
    system_instruction,
};

#[derive(BorshSerialize, BorshDeserialize)]
pub enum MyInstruction {
    Transfer { amount: u64 },
}

entrypoint!(process_instruction);

pub fn process_instruction(
    _program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    match MyInstruction::try_from_slice(instruction_data)? {
        MyInstruction::Transfer { amount } => process_transfer(accounts, amount),
    }
}

fn process_transfer(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();
    let from = next_account_info(accounts_iter)?.key;
    let to = next_account_info(accounts_iter)?.key;

    msg!("Transfering {} lamports from {} to {}", amount, from, to);

    let transfer_ix = system_instruction::transfer(from, to, amount);
    invoke(&transfer_ix, accounts)
}

Repo reference - https://github.com/zillerium/grizzlythonhackathon/blob/trevor/src/App.tsx Deployed contract - https://explorer.solana.com/address/7XyzLTMw7nBzoZnYoxauCzv11ruJee3HqjbxCyDfYb52?cluster=devnet

It looks like the ReactJS using the Phantom wallet cannot execute the transaction. But the txn instruction has three keys coded and they all look ok.

1 Answer 1

0

There's a few things to change that might fix this. First off, you'll need to declare publicKey as a signer in your transaction:

const transactionInstruction = 
         new TransactionInstruction({
        programId,
        keys: [
          {pubkey: publicKey, isSigner: true, isWritable: true},
          {pubkey: toPublicKey, isSigner: false, isWritable: true},
          { pubkey: SystemProgram.programId, isSigner: false, isWritable: true},
          // add any additional keys here
        ],
        data: encodeIxData({ discriminator: 0, amount }), 
      }); 

Next, when sending the accounts through invoke, be sure to not send in too much, just what's needed, ie:

fn process_transfer(accounts: &[AccountInfo], amount: u64) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();
    let from = next_account_info(accounts_iter)?;
    let to = next_account_info(accounts_iter)?;

    msg!("Transfering {} lamports from {} to {}", amount, from.key, to.key);

    let transfer_ix = system_instruction::transfer(from.key, to.key, amount);
    invoke(&transfer_ix, &[from.clone(), to.clone()])
}

I hope that'll start to give a more helpful error message!

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.