Soldev

Sign transactions

Last updated:

In order to be signable, a TransactionMessage must:

On the client, signers abstract the interface to sign transactions.

A signer is an interface that hides the implementation details of a specific signer (like a wallet in the browser).

await mySigner.signTransactions([myTransaction]);

Usually we construct the transaction by building up a bunch of instructions:

import { pipe } from "@solana/functional";
import { createTransactionMessage } from "@solana/transaction-messages";
import { compileTransaction } from "@solana/transactions";

const myTransactionMessage = pipe(
    createTransactionMessage({ version: 0 }),
    // Add instructions, fee payer, lifetime, etc.
);
const myTransaction = compileTransaction(myTransactionMessage);
const [transactionSignatures] = await mySigner.signTransactions([myTransaction]);

There are 3 categories of signers:

Here is a table of the various signer types for both messages and transactions.

Partial signersModifying signersSending signers
TransactionSignerTransactionPartialSignerTransactionModifyingSignerTransactionSendingSigner
MessageSignerMessagePartialSignerMessageModifyingSignerN/A

Signers can be stored inside of the account meta of an instruction. This helps avoid passing them around as plain old addresses.

To sign something we need at least a signable message:

import { createSignableMessage } from "@solana/kit"

const myMessage = createSignableMessage(new Uint8Array([1, 2, 3]));
const myMessageFromText = createSignableMessage('Hello world!');
const myMessageWithSignatures = createSignableMessage('Hello world!', {
    '1234..5678': new Uint8Array([1, 2, 3]),
});

A message is just a recent blockhash, a list of accounts and a corresponding list of instructions.

Usually though you would be signing an actual Transaction which is a message and a list of signers.

Most of the time you will be working with the union types:

type MessageSigner<TAddress extends string = string> =
    | MessagePartialSigner<TAddress>
    | MessageModifyingSigner<TAddress>;

type TransactionSigner<TAddress extends string = string> =
    | TransactionPartialSigner<TAddress>
    | TransactionModifyingSigner<TAddress>
    | TransactionSendingSigner<TAddress>;

Most of time in our tests we are using a KeyPairSigner

import { generateKeyPair } from "@solana/keys";
import { createSignerFromKeyPair, KeyPairSigner } from "@solana/signers";

const myKeyPair: CryptoKeyPair = await generateKeyPair();
const myKeyPairSigner: KeyPairSigner = await createSignerFromKeyPair(myKeyPair);

And even more likely we would use the helper:

import { generateKeyPairSigner } from "@solana/signers";

const myKeyPairSigner = await generateKeyPairSigner();