solanasolana-web3jssolana-program-librarysolana-transaction-instruction

Sign the payer of the transaction through an API


What I would like to achieve is to make the user use the solana program for "free" and make the company pay for the transaction, what I have in mind is:

  1. Extrapolate the transaction in the frontend
  2. Send it to my backend server through an api
  3. Use the wallet that I have on my BE to sing and set this wallet as payer
  4. Send back the transaction
  5. Sign the transaction with the user that is interacting with the FE
  6. Send the transaction from the FE to the solana program.

Let's consider the hello world example https://github.com/solana-labs/example-helloworld

export async function sayHello(): Promise<void> {
  console.log('Saying hello to', greetedPubkey.toBase58());
  const instruction = new TransactionInstruction({
    keys: [{pubkey: greetedPubkey, isSigner: false, isWritable: true}],
    programId,
    data: createSetInstruction()
  });
  console.log(instruction)
  await sendAndConfirmTransaction(
    connection,
    new Transaction().add(instruction),
    [payer],
  );
}

I guess that in some way I could extrapolate the transaction before the sendAndConfirmTransaction

How can I achieve that and the sign it with my Backend wallet?


Update

In order to manage this problem, I started developing this service: cowsigner.com


Solution

  • You have the entire flow correct, so what you would do is:

    const transaction = new Transaction(... your instructions here ...);
    const wireTransaction = transaction.serialize();
    
    // send wireTransaction to backend to be signed
    // on backend:
    const latestBlockhash = await connection.getLatestBlockhash();
    transaction.lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
    transaction.recentBlockhash = latestBlockhash.blockhash;
    transaction.feePayer = backendKey.publicKey;
    transaction.partialSign(backendKey);
    const wireTransactionToSendBack = transaction.serialize();
    
    // send back wireTransactionToSendBack to frontend to be signed by user
    // back on frontend:
    transaction.partialSign(userKey); // or use a wallet adapter, most likely
    const finalWireTransaction = transaction.serialize();
    const signature = await connection.sendRawTransaction(finalWireTransaction);