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.