At this moment whirlpools SDK have building/signing/sending tx steps meshed together. Trying to separate transaction signing into separate secure environment.

  1. generate transaction without private key, send to signer as string


// walletAddr is public key, same as signer. private key is not available 
const whirlpool = await client.getPool(whirlpoolAddress);
const tx = await whirlpool.swap(quote, walletAddr);
const result = await tx.build();
const txs = result.transaction.serialize({ requireAllSignatures: false, verifySignatures: true });
return Buffer.from(txs).toString('base64');
  1. sign transaction and send to network
const keypair = Keypair.fromSecretKey(new Uint8Array(secret));
const signers = [keypair];

let vt = VersionedTransaction.deserialize(Buffer.from(txB64string, 'base64'));

const connection = new Connection("https://api.mainnet-beta.solana.com", 'confirmed');
await connection.sendTransaction(vt); 

except it doesn't work, failed to send transaction: Transaction signature verification failure

P.S. key line is Whirlpools-specific tx.build(), and for some reason it generates two empty (all zeroes) signatures, adding real one makes it 3 total. removing empty ones by hand gives various errors depending on transaction header and remaining signatures

P.S. The solution was:

  • replace wallet manually
const tx = await whirlpoolPool.swap(quote, walletAddr);
let w = new ReadOnlyWallet(walletAddr);
(tx.wallet as any) = w;
  • do not use fresh wallet, create associated token accounts before using them

2 Answers 2


The tx.build() function optionally takes an argument of this type which is used to determine the kind of transaction it returns(could be either a legacy Transaction or a VersionedTransaction). The transaction can be specified with:

const txPayload = await (
  await whirlpool.swap(inputTokenQuote)
  maxSupportedTransactionVersion: "legacy",

but if unspecified will default to a maxSupportedTransactionVersion of 0 which will result in a VersionedTransaction being returned(cc here).

Hence for the above we can do:

const transaction = tx.build() as any as VersionedTransaction; // we know it's this type
transaction.sign[keypair as Signer];

and then send and confirm our transaction as usual.


Best to ask protocol specific questions in their discord or other help forums. It might have something to do with initializing new tick arrays and needing to pass them into the instruction but you should probably talk to the Orca team.

  • Talking on discord did help. Commented Dec 29, 2023 at 8:45

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.