or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

account-management.mdcryptographic-primitives.mderror-handling.mdindex.mdnetwork-sysvars.mdrpc-functionality.mdsystem-programs.mdtesting-infrastructure.mdtoken-operations.mdtransaction-construction.mdtransaction-status.md

transaction-construction.mddocs/

0

# Transaction Construction

1

2

Transaction and message building with support for both legacy and versioned formats. This includes instruction compilation, message construction, address lookup table integration, and transaction signing for efficient Solana blockchain interactions.

3

4

## Capabilities

5

6

### Transaction Types

7

8

Core transaction structures supporting both legacy and modern versioned formats with address lookup table compression.

9

10

```python { .api }

11

class Transaction:

12

"""

13

Legacy transaction format containing instructions and signatures.

14

"""

15

def __init__(self, instructions: List[Instruction], payer: Optional[Pubkey]):

16

"""

17

Create transaction with instructions and payer.

18

19

Parameters:

20

- instructions: List[Instruction], instructions to execute

21

- payer: Optional[Pubkey], fee payer account

22

"""

23

24

@staticmethod

25

def new_with_payer(instructions: List[Instruction], payer: Optional[Pubkey]) -> 'Transaction':

26

"""

27

Create new transaction with specified payer.

28

29

Parameters:

30

- instructions: List[Instruction], instructions to include

31

- payer: Optional[Pubkey], account that pays transaction fees

32

33

Returns:

34

Transaction object

35

"""

36

37

@staticmethod

38

def new_unsigned(instructions: List[Instruction]) -> 'Transaction':

39

"""

40

Create unsigned transaction without payer.

41

42

Parameters:

43

- instructions: List[Instruction], instructions to include

44

45

Returns:

46

Unsigned Transaction object

47

"""

48

49

@staticmethod

50

def new_with_compiled_instructions(

51

num_required_signatures: int,

52

num_readonly_signed_accounts: int,

53

num_readonly_unsigned_accounts: int,

54

account_keys: List[Pubkey],

55

recent_blockhash: Hash,

56

instructions: List[CompiledInstruction]

57

) -> 'Transaction':

58

"""

59

Create transaction from pre-compiled instructions.

60

61

Parameters:

62

- num_required_signatures: int, number of signatures required

63

- num_readonly_signed_accounts: int, readonly accounts that must sign

64

- num_readonly_unsigned_accounts: int, readonly accounts (no signing)

65

- account_keys: List[Pubkey], all accounts referenced in transaction

66

- recent_blockhash: Hash, recent network blockhash

67

- instructions: List[CompiledInstruction], compiled instructions

68

69

Returns:

70

Transaction object

71

"""

72

73

def sign(self, signers: List[Union[Keypair, Presigner]], recent_blockhash: Hash) -> None:

74

"""

75

Sign transaction with provided signers.

76

77

Parameters:

78

- signers: List of keypairs or presigners

79

- recent_blockhash: Hash, recent network blockhash for replay protection

80

"""

81

82

def partial_sign(self, signers: List[Union[Keypair, Presigner]], recent_blockhash: Hash) -> None:

83

"""

84

Partially sign transaction (allows additional signatures later).

85

86

Parameters:

87

- signers: List of available signers (subset of required)

88

- recent_blockhash: Hash, recent network blockhash

89

"""

90

91

def verify(self) -> bool:

92

"""

93

Verify all signatures in the transaction.

94

95

Returns:

96

bool, True if all signatures are valid

97

"""

98

99

def serialize(self) -> bytes:

100

"""

101

Serialize transaction to bytes for network transmission.

102

103

Returns:

104

bytes, serialized transaction

105

"""

106

107

@classmethod

108

def deserialize(cls, data: bytes) -> 'Transaction':

109

"""

110

Deserialize transaction from bytes.

111

112

Parameters:

113

- data: bytes, serialized transaction data

114

115

Returns:

116

Transaction object

117

"""

118

119

@property

120

def message(self) -> Message:

121

"""Get the transaction message."""

122

123

@property

124

def signatures(self) -> List[Signature]:

125

"""Get transaction signatures."""

126

```

127

128

```python { .api }

129

class VersionedTransaction:

130

"""

131

Versioned transaction supporting address lookup tables for compression.

132

"""

133

def __init__(self, message: VersionedMessage, signatures: List[Signature]):

134

"""

135

Create versioned transaction with message and signatures.

136

137

Parameters:

138

- message: VersionedMessage, transaction message (legacy or v0)

139

- signatures: List[Signature], transaction signatures

140

"""

141

142

@staticmethod

143

def new_unsigned(message: VersionedMessage) -> 'VersionedTransaction':

144

"""

145

Create unsigned versioned transaction.

146

147

Parameters:

148

- message: VersionedMessage, transaction message

149

150

Returns:

151

Unsigned VersionedTransaction

152

"""

153

154

def sign(self, signers: List[Union[Keypair, Presigner]]) -> None:

155

"""

156

Sign versioned transaction.

157

158

Parameters:

159

- signers: List of keypairs or presigners

160

"""

161

162

def serialize(self) -> bytes:

163

"""

164

Serialize versioned transaction to bytes.

165

166

Returns:

167

bytes, serialized transaction

168

"""

169

170

@classmethod

171

def deserialize(cls, data: bytes) -> 'VersionedTransaction':

172

"""

173

Deserialize versioned transaction from bytes.

174

175

Parameters:

176

- data: bytes, serialized transaction data

177

178

Returns:

179

VersionedTransaction object

180

"""

181

182

@property

183

def message(self) -> VersionedMessage:

184

"""Get the transaction message."""

185

186

@property

187

def signatures(self) -> List[Signature]:

188

"""Get transaction signatures."""

189

```

190

191

### Message Construction

192

193

Message structures that contain instructions and account metadata for transaction execution.

194

195

```python { .api }

196

class Message:

197

"""

198

Legacy message format containing instructions and account metadata.

199

"""

200

def __init__(

201

self,

202

header: MessageHeader,

203

account_keys: List[Pubkey],

204

recent_blockhash: Hash,

205

instructions: List[CompiledInstruction]

206

):

207

"""

208

Create message with header, accounts, blockhash, and instructions.

209

210

Parameters:

211

- header: MessageHeader, message metadata

212

- account_keys: List[Pubkey], accounts referenced in message

213

- recent_blockhash: Hash, recent network blockhash

214

- instructions: List[CompiledInstruction], compiled instructions

215

"""

216

217

@staticmethod

218

def new_with_compiled_instructions(

219

num_required_signatures: int,

220

num_readonly_signed_accounts: int,

221

num_readonly_unsigned_accounts: int,

222

account_keys: List[Pubkey],

223

recent_blockhash: Hash,

224

instructions: List[CompiledInstruction]

225

) -> 'Message':

226

"""

227

Create message from compiled instructions and metadata.

228

229

Returns:

230

Message object

231

"""

232

233

@staticmethod

234

def new_with_blockhash(instructions: List[Instruction], payer: Optional[Pubkey], blockhash: Hash) -> 'Message':

235

"""

236

Create message from instructions with specified blockhash.

237

238

Parameters:

239

- instructions: List[Instruction], instructions to compile

240

- payer: Optional[Pubkey], fee payer

241

- blockhash: Hash, recent blockhash

242

243

Returns:

244

Message object

245

"""

246

247

def serialize(self) -> bytes:

248

"""Serialize message to bytes."""

249

250

@classmethod

251

def deserialize(cls, data: bytes) -> 'Message':

252

"""Deserialize message from bytes."""

253

254

def is_key_passed_to_program(self, key_index: int, program_id_index: int) -> bool:

255

"""

256

Check if account key is passed to specific program.

257

258

Parameters:

259

- key_index: int, index of account key

260

- program_id_index: int, index of program ID

261

262

Returns:

263

bool, True if key is passed to program

264

"""

265

266

def is_key_called_as_program(self, key_index: int) -> bool:

267

"""

268

Check if account key is called as program.

269

270

Parameters:

271

- key_index: int, index of account key

272

273

Returns:

274

bool, True if key is invoked as program

275

"""

276

277

@property

278

def header(self) -> MessageHeader:

279

"""Get message header."""

280

281

@property

282

def account_keys(self) -> List[Pubkey]:

283

"""Get account keys."""

284

285

@property

286

def recent_blockhash(self) -> Hash:

287

"""Get recent blockhash."""

288

289

@property

290

def instructions(self) -> List[CompiledInstruction]:

291

"""Get compiled instructions."""

292

```

293

294

```python { .api }

295

class MessageV0:

296

"""

297

Version 0 message with address lookup table support for compression.

298

"""

299

def __init__(

300

self,

301

header: MessageHeader,

302

account_keys: List[Pubkey],

303

recent_blockhash: Hash,

304

instructions: List[CompiledInstruction],

305

address_table_lookups: List[MessageAddressTableLookup]

306

):

307

"""

308

Create v0 message with address lookup tables.

309

310

Parameters:

311

- header: MessageHeader, message metadata

312

- account_keys: List[Pubkey], static account keys

313

- recent_blockhash: Hash, recent network blockhash

314

- instructions: List[CompiledInstruction], compiled instructions

315

- address_table_lookups: List[MessageAddressTableLookup], lookup table references

316

"""

317

318

@staticmethod

319

def try_compile(

320

payer: Pubkey,

321

instructions: List[Instruction],

322

address_lookup_table_accounts: List[AddressLookupTableAccount],

323

recent_blockhash: Hash

324

) -> 'MessageV0':

325

"""

326

Compile instructions into v0 message using lookup tables.

327

328

Parameters:

329

- payer: Pubkey, fee payer account

330

- instructions: List[Instruction], instructions to compile

331

- address_lookup_table_accounts: List[AddressLookupTableAccount], available lookup tables

332

- recent_blockhash: Hash, recent blockhash

333

334

Returns:

335

MessageV0 object

336

337

Raises:

338

- CompileError: if compilation fails

339

"""

340

341

def serialize(self) -> bytes:

342

"""Serialize v0 message to bytes."""

343

344

@classmethod

345

def deserialize(cls, data: bytes) -> 'MessageV0':

346

"""Deserialize v0 message from bytes."""

347

348

@property

349

def address_table_lookups(self) -> List[MessageAddressTableLookup]:

350

"""Get address table lookups."""

351

```

352

353

### Message Components

354

355

Supporting structures for message construction and metadata.

356

357

```python { .api }

358

class MessageHeader:

359

"""

360

Message header containing account signature and readonly counts.

361

"""

362

def __init__(

363

self,

364

num_required_signatures: int,

365

num_readonly_signed_accounts: int,

366

num_readonly_unsigned_accounts: int

367

):

368

"""

369

Create message header with account counts.

370

371

Parameters:

372

- num_required_signatures: int, total signatures required

373

- num_readonly_signed_accounts: int, readonly accounts that must sign

374

- num_readonly_unsigned_accounts: int, readonly accounts (no signing)

375

"""

376

377

@property

378

def num_required_signatures(self) -> int:

379

"""Number of signatures required."""

380

381

@property

382

def num_readonly_signed_accounts(self) -> int:

383

"""Number of readonly accounts that must sign."""

384

385

@property

386

def num_readonly_unsigned_accounts(self) -> int:

387

"""Number of readonly unsigned accounts."""

388

```

389

390

```python { .api }

391

class MessageAddressTableLookup:

392

"""

393

Reference to addresses from an address lookup table.

394

"""

395

def __init__(

396

self,

397

account_key: Pubkey,

398

writable_indexes: List[int],

399

readonly_indexes: List[int]

400

):

401

"""

402

Create address table lookup.

403

404

Parameters:

405

- account_key: Pubkey, address of lookup table account

406

- writable_indexes: List[int], indexes of writable addresses

407

- readonly_indexes: List[int], indexes of readonly addresses

408

"""

409

410

@property

411

def account_key(self) -> Pubkey:

412

"""Lookup table account address."""

413

414

@property

415

def writable_indexes(self) -> List[int]:

416

"""Writable address indexes."""

417

418

@property

419

def readonly_indexes(self) -> List[int]:

420

"""Readonly address indexes."""

421

```

422

423

### Instruction Building

424

425

Individual instruction construction with program invocation and account specification.

426

427

```python { .api }

428

class Instruction:

429

"""

430

Transaction instruction containing program ID, accounts, and data.

431

"""

432

def __init__(self, program_id: Pubkey, accounts: List[AccountMeta], data: bytes):

433

"""

434

Create instruction with program, accounts, and data.

435

436

Parameters:

437

- program_id: Pubkey, program to invoke

438

- accounts: List[AccountMeta], accounts required by instruction

439

- data: bytes, instruction-specific data

440

"""

441

442

@property

443

def program_id(self) -> Pubkey:

444

"""Program to invoke."""

445

446

@property

447

def accounts(self) -> List[AccountMeta]:

448

"""Accounts required by instruction."""

449

450

@property

451

def data(self) -> bytes:

452

"""Instruction-specific data."""

453

```

454

455

```python { .api }

456

class AccountMeta:

457

"""

458

Account metadata specifying permissions and signing requirements.

459

"""

460

def __init__(self, pubkey: Pubkey, is_signer: bool, is_writable: bool):

461

"""

462

Create account metadata.

463

464

Parameters:

465

- pubkey: Pubkey, account address

466

- is_signer: bool, whether account must sign transaction

467

- is_writable: bool, whether account data may be modified

468

"""

469

470

@staticmethod

471

def new(pubkey: Pubkey, is_signer: bool) -> 'AccountMeta':

472

"""

473

Create writable account metadata.

474

475

Parameters:

476

- pubkey: Pubkey, account address

477

- is_signer: bool, whether account must sign

478

479

Returns:

480

Writable AccountMeta

481

"""

482

483

@staticmethod

484

def new_readonly(pubkey: Pubkey, is_signer: bool) -> 'AccountMeta':

485

"""

486

Create readonly account metadata.

487

488

Parameters:

489

- pubkey: Pubkey, account address

490

- is_signer: bool, whether account must sign

491

492

Returns:

493

Readonly AccountMeta

494

"""

495

496

@property

497

def pubkey(self) -> Pubkey:

498

"""Account address."""

499

500

@property

501

def is_signer(self) -> bool:

502

"""Whether account must sign."""

503

504

@property

505

def is_writable(self) -> bool:

506

"""Whether account may be modified."""

507

```

508

509

```python { .api }

510

class CompiledInstruction:

511

"""

512

Compiled instruction with account indexes instead of pubkeys.

513

"""

514

def __init__(self, program_id_index: int, accounts: List[int], data: bytes):

515

"""

516

Create compiled instruction.

517

518

Parameters:

519

- program_id_index: int, index of program ID in account keys

520

- accounts: List[int], indexes of accounts in account keys

521

- data: bytes, instruction data

522

"""

523

524

@property

525

def program_id_index(self) -> int:

526

"""Program ID index in account keys."""

527

528

@property

529

def accounts(self) -> List[int]:

530

"""Account indexes."""

531

532

@property

533

def data(self) -> bytes:

534

"""Instruction data."""

535

```

536

537

### Version Management

538

539

Transaction version handling and type definitions.

540

541

```python { .api }

542

class Legacy:

543

"""Transaction version marker for legacy format."""

544

545

# Type aliases for version handling

546

TransactionVersion = Union[Legacy, int]

547

VersionedMessage = Union[Message, MessageV0]

548

```

549

550

## Usage Examples

551

552

### Basic Transaction Creation

553

554

```python

555

from solders.keypair import Keypair

556

from solders.pubkey import Pubkey

557

from solders.transaction import Transaction

558

from solders.instruction import Instruction, AccountMeta

559

from solders.hash import Hash

560

561

# Create keypairs

562

payer = Keypair()

563

recipient = Keypair()

564

program_id = Pubkey.from_string("11111111111111111111111111111112")

565

566

# Create instruction

567

accounts = [

568

AccountMeta(payer.pubkey(), is_signer=True, is_writable=True),

569

AccountMeta(recipient.pubkey(), is_signer=False, is_writable=True)

570

]

571

instruction = Instruction(program_id, accounts, b"instruction_data")

572

573

# Create and sign transaction

574

transaction = Transaction.new_with_payer([instruction], payer.pubkey())

575

recent_blockhash = Hash.from_string("11111111111111111111111111111112")

576

transaction.sign([payer], recent_blockhash)

577

578

# Serialize for transmission

579

serialized = transaction.serialize()

580

```

581

582

### Versioned Transaction with Lookup Tables

583

584

```python

585

from solders.transaction import VersionedTransaction

586

from solders.message import MessageV0

587

from solders.address_lookup_table_account import AddressLookupTableAccount

588

589

# Assume we have lookup table accounts loaded

590

lookup_tables = [] # List of AddressLookupTableAccount objects

591

592

# Compile v0 message

593

message_v0 = MessageV0.try_compile(

594

payer=payer.pubkey(),

595

instructions=[instruction],

596

address_lookup_table_accounts=lookup_tables,

597

recent_blockhash=recent_blockhash

598

)

599

600

# Create versioned transaction

601

versioned_tx = VersionedTransaction.new_unsigned(message_v0)

602

versioned_tx.sign([payer])

603

```

604

605

### Multi-Instruction Transaction

606

607

```python

608

from solders.system_program import transfer, TransferParams

609

from solders.compute_budget import set_compute_unit_limit

610

611

# Create multiple instructions

612

transfer_ix = transfer(TransferParams(

613

from_pubkey=payer.pubkey(),

614

to_pubkey=recipient.pubkey(),

615

lamports=1000000

616

))

617

618

compute_budget_ix = set_compute_unit_limit(200000)

619

620

# Combine in transaction

621

multi_instruction_tx = Transaction.new_with_payer([

622

compute_budget_ix, # Always include compute budget first

623

transfer_ix

624

], payer.pubkey())

625

626

multi_instruction_tx.sign([payer], recent_blockhash)

627

```

628

629

### Partial Signing Workflow

630

631

```python

632

# Create transaction requiring multiple signers

633

multi_signer_accounts = [

634

AccountMeta(payer.pubkey(), is_signer=True, is_writable=True),

635

AccountMeta(recipient.pubkey(), is_signer=True, is_writable=True), # Also must sign

636

AccountMeta(Pubkey.from_string("11111111111111111111111111111112"), is_signer=False, is_writable=False)

637

]

638

639

multi_signer_ix = Instruction(program_id, multi_signer_accounts, b"multi_signer_data")

640

multi_signer_tx = Transaction.new_with_payer([multi_signer_ix], payer.pubkey())

641

642

# Partial sign with available signers

643

multi_signer_tx.partial_sign([payer], recent_blockhash)

644

645

# Later, add remaining signatures

646

multi_signer_tx.partial_sign([recipient], recent_blockhash)

647

648

# Verify all signatures are present

649

assert multi_signer_tx.verify()

650

```

651

652

### Message Serialization

653

654

```python

655

from solders.message import Message, to_bytes_versioned, from_bytes_versioned

656

657

# Create message directly

658

message = Message.new_with_blockhash([instruction], payer.pubkey(), recent_blockhash)

659

660

# Serialize legacy message

661

message_bytes = message.serialize()

662

663

# Serialize versioned message

664

versioned_bytes = to_bytes_versioned(message)

665

666

# Deserialize versioned message

667

restored_message = from_bytes_versioned(versioned_bytes)

668

```

669

670

## Signer Types

671

672

```python { .api }

673

# Union type for all signer implementations

674

Signer = Union[Keypair, Presigner, NullSigner]

675

```

676

677

Different signer types provide flexibility for various signing scenarios:

678

679

- **Keypair**: Standard Ed25519 keypair for direct signing

680

- **Presigner**: Container for pre-computed signatures

681

- **NullSigner**: No-op signer for simulation and readonly operations

682

683

## Error Handling

684

685

### Transaction Errors

686

687

```python { .api }

688

class SanitizeError(Exception):

689

"""Transaction sanitization errors during construction or validation."""

690

691

class TransactionError(Exception):

692

"""General transaction processing errors."""

693

694

class CompileError(Exception):

695

"""Message compilation errors, especially with address lookup tables."""

696

```

697

698

Common error scenarios:

699

700

```python

701

from solders.transaction import Transaction, SanitizeError

702

from solders.message import MessageV0, CompileError

703

704

try:

705

# Invalid transaction construction

706

invalid_tx = Transaction.new_with_payer([], None) # No payer

707

except SanitizeError as e:

708

print(f"Transaction sanitization failed: {e}")

709

710

try:

711

# Message compilation failure

712

message_v0 = MessageV0.try_compile(

713

payer=payer.pubkey(),

714

instructions=[], # Empty instructions

715

address_lookup_table_accounts=[],

716

recent_blockhash=recent_blockhash

717

)

718

except CompileError as e:

719

print(f"Message compilation failed: {e}")

720

```