Class: Solace::TransactionComposer

Inherits:
Object
  • Object
show all
Defined in:
lib/solace/transaction_composer.rb

Overview

Composes transactions with automatic account management and instruction building.

This class allows you to add multiple instruction composers, manage account contexts, and build a complete transaction in a flexible way. It is a high-level abstraction over the process of creating Solana transactions, making it easier to work with complex transaction scenarios.

For most use cases, you will create an instance of this class, add instruction composers, and then call the compose_transaction method to build the final transaction. That said, all of the individual pieces are also accessible for more advanced use cases.

Examples:

# Initialize a transaction composer
composer = Solace::TransactionComposer.new(connection: connection)

# Add an instruction composer
composer.add_instruction(
  Solace::Composers::SystemProgramTransferComposer.new(
    to: 'pubkey1',
    from: 'pubkey2',
    lamports: 100
  )
)

# Add another instruction composer
composer.add_instruction(
  Solace::Composers::SplTokenProgramTransferCheckedComposer.new(
    from: 'pubkey4',
    to: 'pubkey5',
    mint: 'pubkey6',
    authority: 'pubkey7',
    amount: 1_000_000,
    decimals: 6
  )
)

# Set the fee payer
composer.set_fee_payer('pubkey8')

# Compose the transaction
tx = composer.compose_transaction

# Sign the transaction with all required signers
tx.sign(*required_signers)
# Chaining methods will return the composer itself for further modifications. The add, prepend,
# and insert methods allow for dynamic insertion of instruction composers at required positions.
transaction_composer = Solace::TransactionComposer
  .new(connection: connection)
  .add_instruction(instruction_composer_1)
  .prepend_instruction(instruction_composer_2)
  .insert_instruction(1, instruction_composer_3)
  .set_fee_payer(fee_payer_pubkey)
  .compose_transaction

See Also:

Since:

  • 0.0.6

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection:) ⇒ TransactionComposer

Initialize the composer

Parameters:

Since:

  • 0.0.6



87
88
89
90
91
92
93
# File 'lib/solace/transaction_composer.rb', line 87

def initialize(connection:)
  @connection            = connection
  @instruction_composers = []
  @context               = Utils::AccountContext.new
  @address_lookup_tables = []
  @version               = nil
end

Instance Attribute Details

#address_lookup_tablesObject

Since:

  • 0.0.6



78
79
80
# File 'lib/solace/transaction_composer.rb', line 78

def address_lookup_tables
  @address_lookup_tables
end

#connectionObject

Since:

  • 0.0.6



66
67
68
# File 'lib/solace/transaction_composer.rb', line 66

def connection
  @connection
end

#contextObject

Since:

  • 0.0.6



70
71
72
# File 'lib/solace/transaction_composer.rb', line 70

def context
  @context
end

#instruction_composersObject

Since:

  • 0.0.6



74
75
76
# File 'lib/solace/transaction_composer.rb', line 74

def instruction_composers
  @instruction_composers
end

#versionObject

Since:

  • 0.0.6



82
83
84
# File 'lib/solace/transaction_composer.rb', line 82

def version
  @version
end

Instance Method Details

#add_address_lookup_table(account:, addresses: nil) ⇒ TransactionComposer

Make an address lookup table available to the transaction

Registering a table opts the transaction into the v0 format: every compiled account found in a table that is allowed to load (a non-signer that is not the fee payer and not an invoked program id) is referenced by table index instead of occupying a static account slot. Adding the same table (by account) twice is a no-op.

Examples:

composer.add_address_lookup_table(account: table_address, addresses: on_chain_addresses)

Parameters:

  • account (#to_s, PublicKey)

    The lookup table's on-chain address

  • addresses (Array<#to_s>, nil) (defaults to: nil)

    The full, ordered list of addresses stored in the table

Returns:

Since:

  • 0.1.8



184
185
186
187
188
189
190
191
192
193
# File 'lib/solace/transaction_composer.rb', line 184

def add_address_lookup_table(account:, addresses: nil)
     = .to_s
  @version  = 0 # Lookup tables require a v0 transaction

  unless address_lookup_tables.any? { |table| table. ==  }
    address_lookup_tables << Solace::Accounts::AddressLookupTable.new(account: , addresses: addresses)
  end

  self
end

#add_instruction(composer) ⇒ TransactionComposer

Add an instruction composer to the transaction

Parameters:

Returns:

Since:

  • 0.0.6



99
100
101
102
103
# File 'lib/solace/transaction_composer.rb', line 99

def add_instruction(composer)
  merge_accounts(composer.)
  instruction_composers << composer
  self
end

#compose_transactionTransaction

Compose the final transaction

Emits a message at the composer's #version — legacy by default, or v0 once a lookup table has been added — loading eligible accounts through any registered tables.

Returns:

  • (Transaction)

    The composed transaction (unsigned)

Since:

  • 0.0.6



202
203
204
205
206
207
208
209
# File 'lib/solace/transaction_composer.rb', line 202

def compose_transaction
  context.compile

  writable, readonly, references = resolve_address_lookup_tables
  context.compile(loaded_accounts: writable + readonly)

  Solace::Transaction.new(message: build_message(references))
end

#insert_instruction(index, composer) ⇒ TransactionComposer

Insert an instruction composer at a specific index

Parameters:

  • index (Integer)

    The index to insert at

  • composer (Composers::Base)

    The instruction composer

Returns:

Since:

  • 0.1.0



124
125
126
127
128
# File 'lib/solace/transaction_composer.rb', line 124

def insert_instruction(index, composer)
  merge_accounts(composer.)
  instruction_composers.insert(index, composer)
  self
end

#merge(other, placement: :add, index: nil) ⇒ TransactionComposer

Merge another TransactionComposer into this one

Parameters:

  • other (TransactionComposer)

    The other composer to merge

  • placement (Symbol) (defaults to: :add)

    :add to append, :prepend to prepend

  • index (Integer, nil) (defaults to: nil)

    The index to insert at if placement is :insert

Returns:

Since:

  • 0.1.0



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/solace/transaction_composer.rb', line 138

def merge(other, placement: :add, index: nil)
  merge_accounts(other.context)
  merge_address_lookup_tables(other.address_lookup_tables)

  case placement
  when :add
    # Appends the other's instruction composers to this one's list
    instruction_composers.concat(other.instruction_composers)
  when :insert
    # Inserts the other's instruction composers at the specified index
    instruction_composers.insert(index, *other.instruction_composers)
  when :prepend
    # Prepends the other's instruction composers to this one's list
    instruction_composers.unshift(*other.instruction_composers)
  else
    raise ArgumentError, "Invalid placement option: #{placement}"
  end

  self
end

#prepend_instruction(composer) ⇒ TransactionComposer

Prepend an instruction composer to the transaction

Parameters:

Returns:

Since:

  • 0.1.0



111
112
113
114
115
# File 'lib/solace/transaction_composer.rb', line 111

def prepend_instruction(composer)
  merge_accounts(composer.)
  instruction_composers.unshift(composer)
  self
end

#set_fee_payer(pubkey) ⇒ TransactionComposer

Set the fee payer for the transaction

Parameters:

  • pubkey (#to_s, PublicKey)

    The fee payer pubkey

Returns:

Since:

  • 0.0.6



163
164
165
166
# File 'lib/solace/transaction_composer.rb', line 163

def set_fee_payer(pubkey)
  context.set_fee_payer(pubkey.to_s)
  self
end