Module: Solana::Ruby::Kit::TransactionMessages

Extended by:
T::Sig
Defined in:
lib/solana/ruby/kit/transaction_messages/compute_budget.rb,
lib/solana/ruby/kit/transaction_messages/transaction_message.rb

Defined Under Namespace

Classes: BlockhashLifetimeConstraint, DurableNonceLifetimeConstraint, TransactionMessage

Constant Summary collapse

COMPUTE_BUDGET_PROGRAM_ADDRESS =

Address of the Compute Budget program.

T.let(
  Addresses::Address.new('ComputeBudget111111111111111111111111111111'),
  Addresses::Address
)
MAX_COMPUTE_UNIT_LIMIT =

Maximum compute unit limit per transaction (from Agave execution_budget.rs).

T.let(1_400_000, Integer)
MAX_LOADED_ACCOUNTS_DATA_SIZE_LIMIT =

Maximum loaded accounts data size limit per transaction (64 MiB, from Agave).

T.let(64 * 1024 * 1024, Integer)
SET_COMPUTE_UNIT_LIMIT_DISCRIMINATOR =
T.let(2, Integer)
SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_DISCRIMINATOR =
T.let(4, Integer)
TransactionVersion =

Supported transaction versions. Mirrors TypeScript’s ‘TransactionVersion = ’legacy’ | 0 | 1`. Version 1 is defined but not yet supported by most tooling.

T.type_alias { T.any(Symbol, Integer) }
LEGACY_VERSION =
T.let(:legacy, Symbol)
V0_VERSION =
T.let(0, Integer)
V1_VERSION =
T.let(1, Integer)
MAX_SUPPORTED_TRANSACTION_VERSION =
T.let(1, Integer)
Lifetime =

Lifetime can be a blockhash constraint, a durable-nonce constraint, or nil.

T.type_alias { T.nilable(T.any(BlockhashLifetimeConstraint, DurableNonceLifetimeConstraint)) }

Class Method Summary collapse

Class Method Details

.append_instructions(message, instructions) ⇒ Object



127
128
129
130
131
132
133
134
135
# File 'lib/solana/ruby/kit/transaction_messages/transaction_message.rb', line 127

def append_instructions(message, instructions)
  TransactionMessage.new(
    version:               message.version,
    instructions:          message.instructions + instructions,
    fee_payer:             message.fee_payer,
    lifetime_constraint:   message.lifetime_constraint,
    address_table_lookups: message.address_table_lookups
  )
end

.assert_blockhash_lifetime!(message) ⇒ Object



164
165
166
# File 'lib/solana/ruby/kit/transaction_messages/transaction_message.rb', line 164

def assert_blockhash_lifetime!(message)
  Kernel.raise SolanaError.new(:SOLANA_ERROR__TRANSACTION__EXPECTED_BLOCKHASH_LIFETIME) unless blockhash_lifetime?(message)
end

.blockhash_lifetime?(message) ⇒ Boolean

Returns:

  • (Boolean)


152
153
154
# File 'lib/solana/ruby/kit/transaction_messages/transaction_message.rb', line 152

def blockhash_lifetime?(message)
  message.lifetime_constraint.is_a?(BlockhashLifetimeConstraint)
end

.compute_unit_limit_from_instruction_data(data) ⇒ Object



50
51
52
# File 'lib/solana/ruby/kit/transaction_messages/compute_budget.rb', line 50

def compute_unit_limit_from_instruction_data(data)
  T.must(data[1, 4]).unpack1('V')
end

.create_transaction_message(version:) ⇒ Object



65
66
67
68
69
70
71
72
73
# File 'lib/solana/ruby/kit/transaction_messages/transaction_message.rb', line 65

def create_transaction_message(version:)
  TransactionMessage.new(
    version:               version,
    instructions:          [],
    fee_payer:             nil,
    lifetime_constraint:   nil,
    address_table_lookups: nil
  )
end

.durable_nonce_lifetime?(message) ⇒ Boolean

Returns:

  • (Boolean)


158
159
160
# File 'lib/solana/ruby/kit/transaction_messages/transaction_message.rb', line 158

def durable_nonce_lifetime?(message)
  message.lifetime_constraint.is_a?(DurableNonceLifetimeConstraint)
end

.get_set_compute_unit_limit_instruction(units) ⇒ Object



32
33
34
35
36
37
38
39
# File 'lib/solana/ruby/kit/transaction_messages/compute_budget.rb', line 32

def get_set_compute_unit_limit_instruction(units)
  data = ([SET_COMPUTE_UNIT_LIMIT_DISCRIMINATOR].pack('C') + [units].pack('V')).b
  Instructions::Instruction.new(
    program_address: COMPUTE_BUDGET_PROGRAM_ADDRESS,
    accounts:        nil,
    data:            data
  )
end

.get_set_loaded_accounts_data_size_limit_instruction(limit) ⇒ Object



91
92
93
94
95
96
97
98
# File 'lib/solana/ruby/kit/transaction_messages/compute_budget.rb', line 91

def get_set_loaded_accounts_data_size_limit_instruction(limit)
  data = ([SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_DISCRIMINATOR].pack('C') + [limit].pack('V')).b
  Instructions::Instruction.new(
    program_address: COMPUTE_BUDGET_PROGRAM_ADDRESS,
    accounts:        nil,
    data:            data
  )
end

.get_transaction_message_compute_unit_limit(message) ⇒ Object



57
58
59
60
# File 'lib/solana/ruby/kit/transaction_messages/compute_budget.rb', line 57

def get_transaction_message_compute_unit_limit(message)
  ix = message.instructions.find { |i| set_compute_unit_limit_instruction?(i) }
  ix ? compute_unit_limit_from_instruction_data(T.must(ix.data)) : nil
end

.get_transaction_message_loaded_accounts_data_size_limit(message) ⇒ Object



116
117
118
119
# File 'lib/solana/ruby/kit/transaction_messages/compute_budget.rb', line 116

def get_transaction_message_loaded_accounts_data_size_limit(message)
  ix = message.instructions.find { |i| set_loaded_accounts_data_size_limit_instruction?(i) }
  ix ? loaded_accounts_data_size_limit_from_instruction_data(T.must(ix.data)) : nil
end

.loaded_accounts_data_size_limit_from_instruction_data(data) ⇒ Object



109
110
111
# File 'lib/solana/ruby/kit/transaction_messages/compute_budget.rb', line 109

def loaded_accounts_data_size_limit_from_instruction_data(data)
  T.must(data[1, 4]).unpack1('V')
end

.prepend_instructions(message, instructions) ⇒ Object



140
141
142
143
144
145
146
147
148
# File 'lib/solana/ruby/kit/transaction_messages/transaction_message.rb', line 140

def prepend_instructions(message, instructions)
  TransactionMessage.new(
    version:               message.version,
    instructions:          instructions + message.instructions,
    fee_payer:             message.fee_payer,
    lifetime_constraint:   message.lifetime_constraint,
    address_table_lookups: message.address_table_lookups
  )
end

.set_blockhash_lifetime(constraint, message) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/solana/ruby/kit/transaction_messages/transaction_message.rb', line 93

def set_blockhash_lifetime(constraint, message)
  existing = message.lifetime_constraint
  if existing.is_a?(BlockhashLifetimeConstraint) &&
     existing.blockhash == constraint.blockhash &&
     existing.last_valid_block_height == constraint.last_valid_block_height
    return message
  end

  TransactionMessage.new(
    version:               message.version,
    instructions:          message.instructions,
    fee_payer:             message.fee_payer,
    lifetime_constraint:   constraint,
    address_table_lookups: message.address_table_lookups
  )
end

.set_compute_unit_limit_instruction?(instruction) ⇒ Boolean

Returns:

  • (Boolean)


42
43
44
45
46
47
# File 'lib/solana/ruby/kit/transaction_messages/compute_budget.rb', line 42

def set_compute_unit_limit_instruction?(instruction)
  instruction.program_address == COMPUTE_BUDGET_PROGRAM_ADDRESS &&
    !instruction.data.nil? &&
    T.must(instruction.data).bytesize == 5 &&
    T.must(instruction.data).getbyte(0) == SET_COMPUTE_UNIT_LIMIT_DISCRIMINATOR
end

.set_durable_nonce_lifetime(constraint, message) ⇒ Object



113
114
115
116
117
118
119
120
121
# File 'lib/solana/ruby/kit/transaction_messages/transaction_message.rb', line 113

def set_durable_nonce_lifetime(constraint, message)
  TransactionMessage.new(
    version:               message.version,
    instructions:          message.instructions,
    fee_payer:             message.fee_payer,
    lifetime_constraint:   constraint,
    address_table_lookups: message.address_table_lookups
  )
end

.set_fee_payer(fee_payer, message) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
# File 'lib/solana/ruby/kit/transaction_messages/transaction_message.rb', line 78

def set_fee_payer(fee_payer, message)
  return message if message.fee_payer == fee_payer

  TransactionMessage.new(
    version:               message.version,
    instructions:          message.instructions,
    fee_payer:             fee_payer,
    lifetime_constraint:   message.lifetime_constraint,
    address_table_lookups: message.address_table_lookups
  )
end

.set_loaded_accounts_data_size_limit_instruction?(instruction) ⇒ Boolean

Returns:

  • (Boolean)


101
102
103
104
105
106
# File 'lib/solana/ruby/kit/transaction_messages/compute_budget.rb', line 101

def set_loaded_accounts_data_size_limit_instruction?(instruction)
  instruction.program_address == COMPUTE_BUDGET_PROGRAM_ADDRESS &&
    !instruction.data.nil? &&
    T.must(instruction.data).bytesize == 5 &&
    T.must(instruction.data).getbyte(0) == SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_DISCRIMINATOR
end

.set_transaction_message_compute_unit_limit(limit, message) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/solana/ruby/kit/transaction_messages/compute_budget.rb', line 65

def set_transaction_message_compute_unit_limit(limit, message)
  existing_idx = message.instructions.index { |i| set_compute_unit_limit_instruction?(i) }
  new_ix = get_set_compute_unit_limit_instruction(limit)

  if existing_idx.nil?
    append_instructions(message, [new_ix])
  elsif compute_unit_limit_from_instruction_data(T.must(message.instructions[existing_idx].data)) == limit
    message
  else
    new_instructions = message.instructions.dup
    new_instructions[T.must(existing_idx)] = new_ix
    TransactionMessage.new(
      version:               message.version,
      instructions:          new_instructions,
      fee_payer:             message.fee_payer,
      lifetime_constraint:   message.lifetime_constraint,
      address_table_lookups: message.address_table_lookups
    )
  end
end

.set_transaction_message_loaded_accounts_data_size_limit(limit, message) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/solana/ruby/kit/transaction_messages/compute_budget.rb', line 124

def set_transaction_message_loaded_accounts_data_size_limit(limit, message)
  existing_idx = message.instructions.index { |i| set_loaded_accounts_data_size_limit_instruction?(i) }
  new_ix = get_set_loaded_accounts_data_size_limit_instruction(limit)

  if existing_idx.nil?
    append_instructions(message, [new_ix])
  elsif loaded_accounts_data_size_limit_from_instruction_data(T.must(message.instructions[existing_idx].data)) == limit
    message
  else
    new_instructions = message.instructions.dup
    new_instructions[T.must(existing_idx)] = new_ix
    TransactionMessage.new(
      version:               message.version,
      instructions:          new_instructions,
      fee_payer:             message.fee_payer,
      lifetime_constraint:   message.lifetime_constraint,
      address_table_lookups: message.address_table_lookups
    )
  end
end