Transaction and message signing with support for multiple signers, partial signing workflows, and authentication patterns.
Core signer abstractions for different signing scenarios.
/**
* Can sign complete transactions
*/
interface TransactionSigner<TAddress = Address> {
address: TAddress;
signTransaction<TTransaction extends TransactionWithSignatures>(
transaction: TTransaction
): Promise<TTransaction>;
}
/**
* Can modify and sign transactions (e.g., add instructions)
*/
interface TransactionModifyingSigner<TAddress = Address> {
address: TAddress;
modifyAndSignTransaction<TTransaction extends TransactionWithSignatures>(
transaction: TTransaction,
config?: TransactionModificationConfig
): Promise<TTransaction>;
}
/**
* Can partially sign transactions in multi-sig scenarios
*/
interface TransactionPartialSigner<TAddress = Address> {
address: TAddress;
signTransaction<TTransaction extends TransactionWithSignatures>(
transaction: TTransaction
): Promise<TTransaction>;
}
/**
* Can send transactions directly
*/
interface TransactionSendingSigner<TAddress = Address> extends TransactionSigner<TAddress> {
sendTransaction<TTransaction extends TransactionWithSignatures>(
transaction: TTransaction,
config?: SendTransactionConfig
): Promise<Signature>;
}
/**
* Can sign arbitrary messages
*/
interface MessageSigner<TAddress = Address> {
address: TAddress;
signMessage(message: Uint8Array): Promise<SignatureBytes>;
}
/**
* Can modify and sign messages
*/
interface MessageModifyingSigner<TAddress = Address> extends MessageSigner<TAddress> {
modifyAndSignMessage(
message: Uint8Array,
config?: MessageModificationConfig
): Promise<{ message: Uint8Array; signature: SignatureBytes }>;
}
/**
* Can partially sign messages
*/
interface MessagePartialSigner<TAddress = Address> extends MessageSigner<TAddress> {
signMessage(message: Uint8Array): Promise<SignatureBytes>;
}Concrete signer implementations for different key sources.
/**
* Signer using CryptoKey pair
*/
interface KeypairSigner extends TransactionSigner, MessageSigner {
address: Address;
privateKey: CryptoKey;
publicKey: CryptoKey;
}
/**
* Create signer from CryptoKey pair
* @param keyPair - Ed25519 key pair
* @returns Signer implementation
*/
function createSignerFromKeyPair(keyPair: CryptoKeyPair): Promise<KeypairSigner>;
/**
* No-operation signer for testing
*/
interface NoopSigner extends TransactionSigner, MessageSigner {
address: Address;
}
/**
* Create no-op signer that doesn't actually sign
* @param address - Signer address
* @returns No-op signer for testing
*/
function createNoopSigner(address: Address): NoopSigner;Usage Examples:
import {
generateKeyPair,
createSignerFromKeyPair,
getAddressFromPublicKey
} from "@solana/web3.js";
// Create signer from generated key pair
const keyPair = await generateKeyPair();
const signer = await createSignerFromKeyPair(keyPair);
console.log("Signer address:", signer.address);
// Sign a message
const message = new TextEncoder().encode("Hello, Solana!");
const signature = await signer.signMessage(message);
console.log("Message signature:", signature);Add signatures to transactions and manage signing workflows.
/**
* Add signers to a transaction or message
* @param transaction - Transaction to add signers to
* @param signers - Array of signers to add
* @returns Transaction with signers added
*/
function addSigners<T extends TransactionMessage | Transaction>(
transaction: T,
signers: TransactionSigner[]
): T;
/**
* Sign transaction with provided signers
* @param transaction - Transaction to sign
* @param signers - Signers to use
* @returns Promise resolving to signed transaction
*/
function signTransaction<TTransaction extends TransactionWithSignatures>(
transaction: TTransaction,
signers: TransactionSigner[]
): Promise<TTransaction>;
/**
* Partially sign transaction (for multi-sig workflows)
* @param transaction - Transaction to partially sign
* @param signer - Signer to add signature
* @returns Promise resolving to transaction with additional signature
*/
function partiallySignTransaction<TTransaction extends TransactionWithSignatures>(
transaction: TTransaction,
signer: TransactionPartialSigner
): Promise<TTransaction>;
/**
* Check if transaction is fully signed
* @param transaction - Transaction to check
* @returns True if all required signatures are present
*/
function isTransactionFullySigned(transaction: TransactionWithSignatures): boolean;
/**
* Get missing signers from transaction
* @param transaction - Transaction to analyze
* @returns Array of addresses that still need to sign
*/
function getMissingSigners(transaction: TransactionWithSignatures): Address[];Usage Examples:
import {
createTransactionMessage,
compileTransaction,
signTransaction,
addSigners,
isTransactionFullySigned
} from "@solana/web3.js";
// Create transaction
const message = createTransactionMessage({
version: 0,
feePayer: signer.address,
blockhash,
instructions: [/* instructions */]
});
let transaction = compileTransaction(message);
// Add signers
transaction = addSigners(transaction, [signer]);
// Sign transaction
const signedTransaction = await signTransaction(transaction, [signer]);
// Check if fully signed
console.log("Fully signed:", isTransactionFullySigned(signedTransaction));Handle transactions requiring multiple signatures.
/**
* Multi-signature transaction builder
*/
interface MultiSigTransaction {
/** Transaction being signed */
transaction: TransactionWithSignatures;
/** Required signers */
requiredSigners: Address[];
/** Current signatures */
signatures: Map<Address, SignatureBytes>;
/** Whether transaction is ready to send */
isComplete: boolean;
}
/**
* Create multi-signature transaction
* @param transaction - Base transaction
* @param requiredSigners - Addresses that must sign
* @returns Multi-sig transaction builder
*/
function createMultiSigTransaction(
transaction: TransactionWithSignatures,
requiredSigners: Address[]
): MultiSigTransaction;
/**
* Add signature to multi-sig transaction
* @param multiSigTx - Multi-sig transaction
* @param signer - Signer to add
* @returns Updated multi-sig transaction
*/
function addSignatureToMultiSig(
multiSigTx: MultiSigTransaction,
signer: TransactionSigner
): Promise<MultiSigTransaction>;
/**
* Finalize multi-sig transaction when all signatures collected
* @param multiSigTx - Completed multi-sig transaction
* @returns Fully signed transaction ready to send
*/
function finalizeMultiSigTransaction(
multiSigTx: MultiSigTransaction
): TransactionWithSignatures;Handle fee payer designation and signing requirements.
/**
* Fee payer signer interface
*/
interface FeePayerSigner extends TransactionSigner {
/** Indicates this signer can pay fees */
canPayFees: boolean;
}
/**
* Set fee payer for transaction
* @param transaction - Transaction to modify
* @param feePayer - Fee payer signer
* @returns Transaction with fee payer set
*/
function setFeePayer<T extends TransactionMessage | Transaction>(
transaction: T,
feePayer: FeePayerSigner
): T;
/**
* Get fee payer from transaction
* @param transaction - Transaction to analyze
* @returns Fee payer address
*/
function getFeePayer(transaction: TransactionMessage | Transaction): Address;
/**
* Estimate transaction fee
* @param transaction - Transaction to estimate
* @param rpc - RPC client for fee calculation
* @returns Estimated fee in lamports
*/
function estimateTransactionFee(
transaction: TransactionWithSignatures,
rpc: Rpc<GetFeeForMessageApi>
): Promise<Lamports>;Verify signatures and validate signing requirements.
/**
* Verify transaction signatures
* @param transaction - Signed transaction
* @returns Promise resolving to verification result
*/
function verifyTransactionSignatures(
transaction: TransactionWithSignatures
): Promise<{
valid: boolean;
invalidSignatures: Address[];
}>;
/**
* Verify message signature
* @param message - Original message
* @param signature - Signature to verify
* @param publicKey - Public key of signer
* @returns Promise resolving to true if valid
*/
function verifyMessageSignature(
message: Uint8Array,
signature: SignatureBytes,
publicKey: CryptoKey
): Promise<boolean>;
/**
* Extract signer addresses from transaction
* @param transaction - Transaction to analyze
* @returns Array of required signer addresses
*/
function extractSigners(transaction: TransactionMessage | Transaction): Address[];/**
* Transaction with signature storage
*/
interface TransactionWithSignatures {
/** Transaction signatures */
signatures: Signature[];
/** Other transaction properties */
[key: string]: unknown;
}
/**
* Configuration for transaction modification
*/
interface TransactionModificationConfig {
/** Additional instructions to add */
additionalInstructions?: IInstruction[];
/** Whether to replace existing instructions */
replaceInstructions?: boolean;
/** Custom fee payer */
feePayer?: Address;
}
/**
* Configuration for message modification
*/
interface MessageModificationConfig {
/** Additional data to append */
additionalData?: Uint8Array;
/** Custom prefix */
prefix?: string;
}
/**
* Configuration for sending transactions
*/
interface SendTransactionConfig {
/** Skip preflight checks */
skipPreflight?: boolean;
/** Commitment level for confirmation */
commitment?: Commitment;
/** Maximum retries */
maxRetries?: number;
}
/**
* Signer metadata
*/
interface SignerMeta {
/** Signer address */
address: Address;
/** Signer capabilities */
capabilities: {
canSignTransactions: boolean;
canSignMessages: boolean;
canModifyTransactions: boolean;
canSendTransactions: boolean;
};
}Advanced Usage Examples:
import {
createMultiSigTransaction,
addSignatureToMultiSig,
finalizeMultiSigTransaction,
verifyTransactionSignatures
} from "@solana/web3.js";
// Multi-signature workflow
const requiredSigners = [signer1.address, signer2.address, signer3.address];
let multiSigTx = createMultiSigTransaction(transaction, requiredSigners);
// Collect signatures from different signers
multiSigTx = await addSignatureToMultiSig(multiSigTx, signer1);
multiSigTx = await addSignatureToMultiSig(multiSigTx, signer2);
multiSigTx = await addSignatureToMultiSig(multiSigTx, signer3);
// Finalize when all signatures collected
if (multiSigTx.isComplete) {
const finalTransaction = finalizeMultiSigTransaction(multiSigTx);
// Verify signatures before sending
const verification = await verifyTransactionSignatures(finalTransaction);
if (verification.valid) {
// Send transaction
const signature = await rpc.sendTransaction(finalTransaction).send();
console.log("Multi-sig transaction sent:", signature);
}
}