0
# Transaction Building
1
2
Comprehensive transaction message creation, compilation, and optimization with functional composition patterns and support for versioned transactions and address lookup tables.
3
4
## Capabilities
5
6
### Transaction Message Creation
7
8
Build transaction messages using a functional, step-by-step approach.
9
10
```typescript { .api }
11
/**
12
* Base transaction message structure
13
*/
14
type BaseTransactionMessage = {
15
readonly instructions: readonly IInstruction[];
16
readonly version: TransactionVersion;
17
};
18
19
/**
20
* Create an empty transaction message with specified version
21
* @param config - Version configuration
22
* @returns Empty transaction message
23
*/
24
function createTransactionMessage<TVersion extends TransactionVersion>(
25
config: { version: TVersion }
26
): Extract<TransactionMessage, { version: TVersion }>;
27
28
/**
29
* Add instructions to a transaction message
30
* @param instructions - Instructions to append
31
* @param transactionMessage - Existing transaction message
32
* @returns New transaction message with added instructions
33
*/
34
function appendTransactionMessageInstructions<TTransactionMessage>(
35
instructions: readonly IInstruction[],
36
transactionMessage: TTransactionMessage
37
): TTransactionMessage;
38
39
/**
40
* Add a single instruction to a transaction message
41
* @param instruction - Instruction to append
42
* @param transactionMessage - Existing transaction message
43
* @returns New transaction message with added instruction
44
*/
45
function appendTransactionMessageInstruction<TTransactionMessage>(
46
instruction: IInstruction,
47
transactionMessage: TTransactionMessage
48
): TTransactionMessage;
49
50
/**
51
* Add instructions to the beginning of a transaction message
52
* @param instructions - Instructions to prepend
53
* @param transactionMessage - Existing transaction message
54
* @returns New transaction message with prepended instructions
55
*/
56
function prependTransactionMessageInstructions<TTransactionMessage>(
57
instructions: readonly IInstruction[],
58
transactionMessage: TTransactionMessage
59
): TTransactionMessage;
60
61
/**
62
* Add a single instruction to the beginning of a transaction message
63
* @param instruction - Instruction to prepend
64
* @param transactionMessage - Existing transaction message
65
* @returns New transaction message with prepended instruction
66
*/
67
function prependTransactionMessageInstruction<TTransactionMessage>(
68
instruction: IInstruction,
69
transactionMessage: TTransactionMessage
70
): TTransactionMessage;
71
```
72
73
**Usage Examples:**
74
75
```typescript
76
import {
77
createTransactionMessage,
78
appendTransactionMessageInstructions,
79
setTransactionMessageFeePayer,
80
address
81
} from "@solana/web3.js";
82
83
// Start with empty transaction
84
const baseMessage = createTransactionMessage({ version: 0 });
85
86
// Add instructions
87
const withInstructions = appendTransactionMessageInstructions(
88
[
89
// Your instructions here
90
{
91
programId: address("11111111111111111111111111111112"),
92
accounts: [],
93
data: new Uint8Array()
94
}
95
],
96
baseMessage
97
);
98
99
// Set fee payer (required for compilation)
100
const withFeePayer = setTransactionMessageFeePayer(
101
address("your-fee-payer-address"),
102
withInstructions
103
);
104
```
105
106
### Transaction Message Configuration
107
108
Set fee payer and lifetime constraints on transaction messages.
109
110
```typescript { .api }
111
/**
112
* Set the fee payer for a transaction message
113
* @param feePayer - Address that will pay transaction fees
114
* @param transactionMessage - Transaction message to modify
115
* @returns Transaction message with fee payer set
116
*/
117
function setTransactionMessageFeePayer<TTransactionMessage>(
118
feePayer: Address,
119
transactionMessage: TTransactionMessage
120
): TTransactionMessage & ITransactionMessageWithFeePayer;
121
122
/**
123
* Set blockhash-based lifetime constraint
124
* @param lifetimeConstraint - Blockhash and last valid block height
125
* @param transactionMessage - Transaction message to modify
126
* @returns Transaction message with lifetime constraint
127
*/
128
function setTransactionMessageLifetimeUsingBlockhash<TTransactionMessage>(
129
lifetimeConstraint: {
130
blockhash: Blockhash;
131
lastValidBlockHeight: bigint;
132
},
133
transactionMessage: TTransactionMessage
134
): TTransactionMessage & ITransactionMessageWithBlockhashLifetime;
135
136
/**
137
* Set durable nonce-based lifetime constraint
138
* @param durableNonceConfig - Nonce configuration
139
* @param transactionMessage - Transaction message to modify
140
* @returns Transaction message with durable nonce lifetime
141
*/
142
function setTransactionMessageLifetimeUsingDurableNonce<TTransactionMessage>(
143
durableNonceConfig: {
144
nonce: Nonce;
145
nonceAccountAddress: Address;
146
nonceAuthorityAddress: Address;
147
},
148
transactionMessage: TTransactionMessage
149
): TTransactionMessage & ITransactionMessageWithDurableNonceLifetime;
150
```
151
152
**Usage Examples:**
153
154
```typescript
155
import {
156
createTransactionMessage,
157
setTransactionMessageFeePayer,
158
setTransactionMessageLifetimeUsingBlockhash,
159
createSolanaRpc,
160
address
161
} from "@solana/web3.js";
162
163
const rpc = createSolanaRpc("https://api.devnet.solana.com");
164
165
// Get recent blockhash
166
const { value: { blockhash, lastValidBlockHeight } } = await rpc
167
.getLatestBlockhash()
168
.send();
169
170
// Build transaction step by step
171
const message = createTransactionMessage({ version: 0 });
172
173
const withFeePayer = setTransactionMessageFeePayer(
174
address("your-fee-payer-address"),
175
message
176
);
177
178
const readyToCompile = setTransactionMessageLifetimeUsingBlockhash(
179
{ blockhash, lastValidBlockHeight },
180
withFeePayer
181
);
182
```
183
184
### Transaction Message Compilation
185
186
Convert transaction messages to compiled format for network transmission.
187
188
```typescript { .api }
189
/**
190
* Compile a transaction message to bytes
191
* @param transactionMessage - Compilable transaction message
192
* @returns Compiled transaction message bytes
193
*/
194
function compileTransactionMessage<TTransactionMessage extends CompilableTransactionMessage>(
195
transactionMessage: TTransactionMessage
196
): CompiledTransactionMessage;
197
198
/**
199
* Decompile transaction message bytes back to readable format
200
* @param compiledTransactionMessage - Compiled message bytes
201
* @param config - Optional decompilation configuration
202
* @returns Decompiled transaction message
203
*/
204
function decompileTransactionMessage(
205
compiledTransactionMessage: CompiledTransactionMessage,
206
config?: {
207
addressesByLookupTableAddress?: ReadonlyMap<Address, readonly Address[]>;
208
}
209
): TransactionMessage;
210
```
211
212
**Usage Examples:**
213
214
```typescript
215
import {
216
compileTransactionMessage,
217
createTransactionMessage,
218
appendTransactionMessageInstructions,
219
setTransactionMessageFeePayer,
220
setTransactionMessageLifetimeUsingBlockhash
221
} from "@solana/web3.js";
222
223
// Build complete transaction message
224
const message = createTransactionMessage({ version: 0 });
225
const withInstructions = appendTransactionMessageInstructions(myInstructions, message);
226
const withFeePayer = setTransactionMessageFeePayer(feePayerAddress, withInstructions);
227
const compilableMessage = setTransactionMessageLifetimeUsingBlockhash(
228
{ blockhash, lastValidBlockHeight },
229
withFeePayer
230
);
231
232
// Compile to bytes
233
const compiledMessage = compileTransactionMessage(compilableMessage);
234
```
235
236
### Transaction Compilation and Signing
237
238
Convert transaction messages to final transaction format ready for network submission.
239
240
```typescript { .api }
241
/**
242
* Compile a transaction message to a transaction
243
* @param transactionMessage - Compilable transaction message
244
* @returns Transaction with empty signatures
245
*/
246
function compileTransaction<TTransactionMessage extends CompilableTransactionMessage>(
247
transactionMessage: TTransactionMessage
248
): Transaction;
249
250
/**
251
* Sign a transaction with keypairs
252
* @param keyPairs - Array of key pairs to sign with
253
* @param transaction - Transaction to sign
254
* @returns Fully signed transaction
255
*/
256
function signTransaction<TTransaction extends Transaction>(
257
keyPairs: readonly CryptoKeyPair[],
258
transaction: TTransaction
259
): Promise<TTransaction & ITransactionWithSignatures>;
260
261
/**
262
* Partially sign a transaction
263
* @param keyPairs - Array of key pairs to sign with
264
* @param transaction - Transaction to sign
265
* @returns Partially signed transaction
266
*/
267
function partiallySignTransaction<TTransaction extends Transaction>(
268
keyPairs: readonly CryptoKeyPair[],
269
transaction: TTransaction
270
): Promise<TTransaction>;
271
272
/**
273
* Get wire-format transaction bytes
274
* @param transaction - Signed transaction
275
* @returns Base64-encoded transaction bytes for network submission
276
*/
277
function getBase64EncodedWireTransaction(transaction: Transaction): Base64EncodedWireTransaction;
278
```
279
280
**Usage Examples:**
281
282
```typescript
283
import {
284
compileTransaction,
285
signTransaction,
286
getBase64EncodedWireTransaction,
287
generateKeyPair
288
} from "@solana/web3.js";
289
290
// Compile transaction message to transaction
291
const transaction = compileTransaction(compilableMessage);
292
293
// Sign transaction
294
const keyPair = await generateKeyPair();
295
const signedTransaction = await signTransaction([keyPair], transaction);
296
297
// Get wire format for network submission
298
const wireTransaction = getBase64EncodedWireTransaction(signedTransaction);
299
300
// Now ready to send via RPC
301
const signature = await rpc.sendTransaction(wireTransaction).send();
302
```
303
304
### Address Lookup Tables
305
306
Optimize transaction size using address lookup tables for frequently used addresses.
307
308
```typescript { .api }
309
/**
310
* Compress transaction message using address lookup tables
311
* @param transactionMessage - Transaction message to compress
312
* @param addressesByLookupTableAddress - Map of lookup table addresses to their address lists
313
* @returns Compressed transaction message
314
*/
315
function compressTransactionMessageUsingAddressLookupTables<
316
TTransactionMessage extends BaseTransactionMessage
317
>(
318
transactionMessage: TTransactionMessage,
319
addressesByLookupTableAddress: ReadonlyMap<Address, readonly Address[]>
320
): TTransactionMessage;
321
```
322
323
**Usage Examples:**
324
325
```typescript
326
import {
327
compressTransactionMessageUsingAddressLookupTables,
328
address
329
} from "@solana/web3.js";
330
331
// Define address lookup table
332
const lookupTableAddress = address("your-lookup-table-address");
333
const lookupTableAddresses = [
334
address("frequently-used-address-1"),
335
address("frequently-used-address-2"),
336
// ... more addresses
337
];
338
339
const addressLookupMap = new Map([
340
[lookupTableAddress, lookupTableAddresses]
341
]);
342
343
// Compress transaction using lookup tables
344
const compressedMessage = compressTransactionMessageUsingAddressLookupTables(
345
transactionMessage,
346
addressLookupMap
347
);
348
```
349
350
### Transaction Validation
351
352
Utilities for validating transaction messages and their properties.
353
354
```typescript { .api }
355
/**
356
* Check if transaction message has blockhash lifetime constraint
357
* @param transactionMessage - Transaction message to check
358
* @returns True if message uses blockhash lifetime
359
*/
360
function isTransactionMessageWithBlockhashLifetime(
361
transactionMessage: BaseTransactionMessage
362
): transactionMessage is TransactionMessageWithBlockhashLifetime;
363
364
/**
365
* Check if transaction is a durable nonce transaction
366
* @param transaction - Transaction to check
367
* @returns True if transaction uses durable nonce
368
*/
369
function isDurableNonceTransaction(
370
transaction: BaseTransactionMessage
371
): transaction is DurableNonceTransactionMessage;
372
373
/**
374
* Assert that transaction is fully signed
375
* @param transaction - Transaction to validate
376
* @throws Error if transaction is not fully signed
377
*/
378
function assertTransactionIsFullySigned(
379
transaction: Transaction
380
): asserts transaction is FullySignedTransaction;
381
```
382
383
## Complete Transaction Building Example
384
385
```typescript
386
import {
387
createTransactionMessage,
388
appendTransactionMessageInstructions,
389
setTransactionMessageFeePayer,
390
setTransactionMessageLifetimeUsingBlockhash,
391
compileTransaction,
392
signTransaction,
393
createSolanaRpc,
394
generateKeyPair,
395
address
396
} from "@solana/web3.js";
397
398
async function buildAndSendTransaction() {
399
const rpc = createSolanaRpc("https://api.devnet.solana.com");
400
401
// 1. Get blockhash
402
const { value: { blockhash, lastValidBlockHeight } } = await rpc
403
.getLatestBlockhash()
404
.send();
405
406
// 2. Create base transaction
407
const message = createTransactionMessage({ version: 0 });
408
409
// 3. Add instructions
410
const withInstructions = appendTransactionMessageInstructions([
411
{
412
programId: address("11111111111111111111111111111112"),
413
accounts: [],
414
data: new Uint8Array()
415
}
416
], message);
417
418
// 4. Set fee payer
419
const feePayerKeyPair = await generateKeyPair();
420
const feePayerAddress = await getAddressFromPublicKey(feePayerKeyPair.publicKey);
421
const withFeePayer = setTransactionMessageFeePayer(feePayerAddress, withInstructions);
422
423
// 5. Set lifetime constraint
424
const compilableMessage = setTransactionMessageLifetimeUsingBlockhash(
425
{ blockhash, lastValidBlockHeight },
426
withFeePayer
427
);
428
429
// 6. Compile to transaction
430
const transaction = compileTransaction(compilableMessage);
431
432
// 7. Sign transaction
433
const signedTransaction = await signTransaction([feePayerKeyPair], transaction);
434
435
// 8. Send transaction
436
const wireTransaction = getBase64EncodedWireTransaction(signedTransaction);
437
const signature = await rpc.sendTransaction(wireTransaction).send();
438
439
console.log("Transaction sent:", signature);
440
return signature;
441
}
442
```
443
444
## Types
445
446
```typescript { .api }
447
/**
448
* Base transaction message
449
*/
450
type BaseTransactionMessage = {
451
readonly instructions: readonly IInstruction[];
452
readonly version: TransactionVersion;
453
};
454
455
/**
456
* Transaction message with fee payer
457
*/
458
interface ITransactionMessageWithFeePayer {
459
readonly feePayer: Address;
460
}
461
462
/**
463
* Transaction message with blockhash lifetime
464
*/
465
interface ITransactionMessageWithBlockhashLifetime {
466
readonly lifetimeConstraint: {
467
readonly blockhash: Blockhash;
468
readonly lastValidBlockHeight: bigint;
469
};
470
}
471
472
/**
473
* Transaction message with durable nonce lifetime
474
*/
475
interface ITransactionMessageWithDurableNonceLifetime {
476
readonly lifetimeConstraint: {
477
readonly nonce: Nonce;
478
};
479
}
480
481
/**
482
* Transaction message ready for compilation
483
*/
484
type CompilableTransactionMessage = BaseTransactionMessage &
485
ITransactionMessageWithFeePayer &
486
(ITransactionMessageWithBlockhashLifetime | ITransactionMessageWithDurableNonceLifetime);
487
488
/**
489
* Compiled transaction
490
*/
491
interface Transaction {
492
readonly messageBytes: TransactionMessageBytes;
493
readonly signatures: SignaturesMap;
494
readonly lifetimeConstraint: BlockhashLifetimeConstraint | NonceLifetimeConstraint;
495
}
496
497
/**
498
* Signature map for transaction
499
*/
500
type SignaturesMap = Record<Address, SignatureBytes | null>;
501
502
/**
503
* Wire-encoded transaction for network submission
504
*/
505
type Base64EncodedWireTransaction = string & { readonly __brand: unique symbol };
506
```