I'm attempting to send a transaction with an instruction that requires the signature of an admin key. My idea was to have this admin keypair on a hosted server, and have an endpoint where the tx containing the instruction would be sent to. From there, the server runs some checks, and partial signs the transaction if the checks pass. The server logic looks like the following:

export async function checkAndSign(check: CheckType, tx: Transaction) {
  // Get admin key
  const buffer = Uint8Array.from(<some array of bytes>);
  // Create keypair
  const pk = Keypair.fromSecretKey(buffer);

  if (check) {
    let verif = true; // unrelated calculations made here
    if (verif) {
      //Slap a blockhash on it
      tx.recentBlockhash = (await connection.getLatestBlockhash("finalized")).blockhash;
      //If true, partial sign, and return
      logger("Transaction partial signed.");
      return tx;
    } else {
      logger("Verification failed, returning transaction");
      return tx;
  } else {
    return null;

This function fails with the error unknown signer: AHexUL9Q7pXunCZFXZ9TniqT4YEihqGXPiRrUqotAvho. I can't seem to find much on this specific error, and why it's happening. The transaction is created on the client-side, and looks like the following:

let instruction = await program.methods
    .checkTx(<some arguments>)
      <...some accounts>
      payer: wallet.publicKey
      relayer: new PublicKey("AHexUL9Q7pXunCZFXZ9TniqT4YEihqGXPiRrUqotAvho") //In the program this account is Option<Signer<'info>>, so it has the possibility of being null in some cases

Wondering if this method would feasibly work, or if I should be creating the tx on the backend, and sending it for the user to sign instead.

1 Answer 1


The error you're seeing is from web3js. It happens because AHexUL9Q7pXunCZFXZ9TniqT4YEihqGXPiRrUqotAvho isn't a signer on the transaction - so no transaction instruction has it as a signer. I'm not sure why that would be happening since you said relayer is an Option<Signer> though.

Something that might help debug a bit is to log out your transaction instructions and inspect the keys and see where AHex... is being used and in what role.

Some debug code I've used before for a similar issue that might help too:

const currentSigners = [... new Set(transaction.signatures.filter(k => k.signature !== null).map(k => k.publicKey.toBase58()))];
const expectedSigners = [... new Set(transaction.instructions.flatMap(i => i.keys.filter(k => k.isSigner).map(k => k.pubkey.toBase58())))];

That'll give you the addresses that have already signed the transaction, and those that are expected to.

In general this method of creating a transaction on the client, partially signing it on the server, and then signing and sending it on the client, is definitely fine. Transactions are serializable, including with signatures.

  • Thanks for the help! Was able to identify the fix
    – Joey Meere
    Feb 8 at 16:32

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.