web3.js v2 sendTransaction fails 'invalid base58 encoding: InvalidCharacter { character: '+' }' — use sendAndConfirmTransactionFactory or encoding base64
Symptom: with @solana/web3.js v2 (now @solana/kit) you compile and sign a transaction, call rpc.sendTransaction(getBase64EncodedWireTransaction(tx), { skipPreflight: true }).send(), and get SolanaError: JSON-RPC error: Invalid method parameter(s) (invalid base58 encoding: InvalidCharacter { character: '+', index: 12 }). You never asked for base58 anywhere.
Cause: the sendTransaction JSON-RPC method encodes its payload as base58 unless you say otherwise. You handed it base64, whose alphabet includes +, and the server rejected the first non-base58 character. The error is about the *server's* default encoding, not about your keys, signatures or blockhash.
Fix — do not hand-roll the send. Build the factory that sets the encoding and waits for confirmation:
const rpc = createSolanaRpc('http://127.0.0.1:8899');
const rpcSubscriptions = createSolanaRpcSubscriptions('ws://127.0.0.1:8900');
const sendAndConfirm = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const message = pipe(
createTransactionMessage({ version: 0 }),
m => setTransactionMessageFeePayerSigner(feePayer, m),
m => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
m => appendTransactionMessageInstructions(instructions, m),
);
const signedTransaction = await signTransactionMessageWithSigners(message);
const signature = getSignatureFromTransaction(signedTransaction); // for explorers
await sendAndConfirm(signedTransaction, { commitment: 'confirmed' });If you must call the raw method, pass { encoding: 'base64' } in the config object alongside skipPreflight.
Other non-obvious bits: signTransactionMessageWithSigners replaces manually collecting keypairs and calling signTransaction; sendAndConfirmTransactionFactory requires a websocket subscriptions client (port 8900 locally), so an RPC-only endpoint will not work; and the signature is derived from the signed transaction locally rather than returned by the send.
Source: adapted from a Stack Exchange answer by ngundotra (question by ngundotra), https://solana.stackexchange.com/questions/17530/how-do-i-send-transactions-with-new-web3-js — CC BY-SA 4.0