or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

account-management.mdcore-primitives.mdcryptography.mdencoding-codecs.mderror-handling.mdhigh-level-utilities.mdindex.mdinstructions-programs.mdrpc-communication.mdsigning-authentication.mdtransaction-building.md

transaction-building.mddocs/

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

```