solana-ruby-kit 0.1.9 → 7.0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,134 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../errors'
5
+ require_relative '../instructions/instruction'
6
+ require_relative '../instructions/roles'
7
+ require_relative 'compiled_transaction_message'
8
+ require_relative 'loaded_addresses'
9
+
10
+ module Solana::Ruby::Kit
11
+ module TransactionIntrospection
12
+ extend T::Sig
13
+
14
+ module_function
15
+
16
+ # Builds the full ordered list of AccountMetas for a compiled transaction
17
+ # message.
18
+ #
19
+ # The order matches the runtime's resolution order:
20
+ # 1. Static accounts, with role bits derived from the message header
21
+ # (writable signers, readonly signers, writable non-signers, readonly
22
+ # non-signers).
23
+ # 2. ALT-loaded writable accounts (always non-signer, writable).
24
+ # 3. ALT-loaded readonly accounts (always non-signer, readonly).
25
+ #
26
+ # Inner-instruction account indices reference the same flat list, so this
27
+ # helper is also useful for resolving inner instructions.
28
+ #
29
+ # Mirrors `getAccountMetasFromCompiledTransactionMessage()`.
30
+ sig do
31
+ params(
32
+ compiled_message: CompiledTransactionMessage,
33
+ loaded_addresses: T.nilable(LoadedAddresses)
34
+ ).returns(T::Array[Instructions::AccountMeta])
35
+ end
36
+ def get_account_metas_from_compiled_transaction_message(compiled_message, loaded_addresses = nil)
37
+ header = compiled_message.header
38
+ static_accounts = compiled_message.static_accounts
39
+
40
+ num_writable_signer_accounts =
41
+ header.num_signer_accounts - header.num_readonly_signer_accounts
42
+ num_writable_non_signer_accounts =
43
+ static_accounts.length - header.num_signer_accounts - header.num_readonly_non_signer_accounts
44
+
45
+ metas = T.let([], T::Array[Instructions::AccountMeta])
46
+ i = 0
47
+ num_writable_signer_accounts.times do
48
+ metas << Instructions::AccountMeta.new(address: T.must(static_accounts[i]), role: Instructions::AccountRole::WRITABLE_SIGNER)
49
+ i += 1
50
+ end
51
+ header.num_readonly_signer_accounts.times do
52
+ metas << Instructions::AccountMeta.new(address: T.must(static_accounts[i]), role: Instructions::AccountRole::READONLY_SIGNER)
53
+ i += 1
54
+ end
55
+ num_writable_non_signer_accounts.times do
56
+ metas << Instructions::AccountMeta.new(address: T.must(static_accounts[i]), role: Instructions::AccountRole::WRITABLE)
57
+ i += 1
58
+ end
59
+ header.num_readonly_non_signer_accounts.times do
60
+ metas << Instructions::AccountMeta.new(address: T.must(static_accounts[i]), role: Instructions::AccountRole::READONLY)
61
+ i += 1
62
+ end
63
+
64
+ if loaded_addresses
65
+ loaded_addresses.writable.each { |addr| metas << Instructions::AccountMeta.new(address: addr, role: Instructions::AccountRole::WRITABLE) }
66
+ loaded_addresses.readonly.each { |addr| metas << Instructions::AccountMeta.new(address: addr, role: Instructions::AccountRole::READONLY) }
67
+ end
68
+
69
+ metas
70
+ end
71
+
72
+ # Returns the outer instructions of a compiled transaction message as
73
+ # Instructions::Instruction objects, with account indices resolved to
74
+ # AccountMetas and data exposed as a raw binary String. `accounts` and
75
+ # `data` are omitted (nil) when empty.
76
+ #
77
+ # Mirrors `getInstructionsFromCompiledTransactionMessage()`.
78
+ sig do
79
+ params(
80
+ compiled_message: CompiledTransactionMessage,
81
+ loaded_addresses: T.nilable(LoadedAddresses)
82
+ ).returns(T::Array[Instructions::Instruction])
83
+ end
84
+ def get_instructions_from_compiled_transaction_message(compiled_message, loaded_addresses = nil)
85
+ metas = get_account_metas_from_compiled_transaction_message(compiled_message, loaded_addresses)
86
+ get_instructions_from_compiled_transaction_message_with_metas(compiled_message, metas)
87
+ end
88
+
89
+ # Internal variant of get_instructions_from_compiled_transaction_message
90
+ # that takes pre-built AccountMetas. Used by WalkInstructions to avoid
91
+ # rebuilding the meta list when it is already needed for resolving inner
92
+ # instructions.
93
+ sig do
94
+ params(
95
+ compiled_message: CompiledTransactionMessage,
96
+ account_metas: T::Array[Instructions::AccountMeta]
97
+ ).returns(T::Array[Instructions::Instruction])
98
+ end
99
+ def get_instructions_from_compiled_transaction_message_with_metas(compiled_message, account_metas)
100
+ compiled_message.instructions.map { |ix| resolve_instruction(ix, account_metas) }
101
+ end
102
+
103
+ # ── Private helpers ────────────────────────────────────────────────────────
104
+
105
+ sig { params(ix: CompiledInstruction, metas: T::Array[Instructions::AccountMeta]).returns(Instructions::Instruction) }
106
+ def resolve_instruction(ix, metas)
107
+ program_meta = metas[ix.program_address_index]
108
+ if program_meta.nil?
109
+ Kernel.raise SolanaError.new(
110
+ SolanaError::TRANSACTIONS__FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND,
111
+ { index: ix.program_address_index }
112
+ )
113
+ end
114
+
115
+ accounts = ix.account_indices.map do |i|
116
+ account_meta = metas[i]
117
+ if account_meta.nil?
118
+ Kernel.raise SolanaError.new(
119
+ SolanaError::TRANSACTION__FAILED_TO_DECOMPILE_INSTRUCTION_ACCOUNT_INDEX_OUT_OF_RANGE,
120
+ { index: i }
121
+ )
122
+ end
123
+ account_meta
124
+ end
125
+
126
+ Instructions::Instruction.new(
127
+ program_address: program_meta.address,
128
+ accounts: accounts.empty? ? nil : accounts,
129
+ data: ix.data.empty? ? nil : ix.data
130
+ )
131
+ end
132
+ private_class_method :resolve_instruction
133
+ end
134
+ end
@@ -0,0 +1,19 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../addresses/address'
5
+
6
+ module Solana::Ruby::Kit
7
+ module TransactionIntrospection
8
+ # Loaded ALT addresses as returned by `getTransaction`'s `meta.loadedAddresses`.
9
+ # The two arrays are kept in the same order the runtime uses to resolve
10
+ # instruction account indices.
11
+ # Mirrors `LoadedAddresses` from @solana/transaction-introspection.
12
+ class LoadedAddresses < T::Struct
13
+ const :readonly, T::Array[Addresses::Address]
14
+ const :writable, T::Array[Addresses::Address]
15
+ end
16
+
17
+ EMPTY_LOADED_ADDRESSES = T.let(LoadedAddresses.new(readonly: [], writable: []), LoadedAddresses)
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../instructions/instruction'
5
+
6
+ module Solana::Ruby::Kit
7
+ module TransactionIntrospection
8
+ # A ResolvedInstruction carrying its location in the transaction as a
9
+ # `trace` Hash.
10
+ #
11
+ # TypeScript flattens `TracedInstruction` into the instruction itself
12
+ # (`ResolvedInstruction & { trace }`), so callers there access
13
+ # `ix.programAddress` directly. Ruby's `Instructions::Instruction` is a
14
+ # `T::Struct`, which is final and cannot be subclassed (see
15
+ # `Transactions::FullySignedTransaction` for the same constraint), so here
16
+ # the instruction and its trace are held as sibling fields instead:
17
+ # `traced.instruction.program_address`, `traced.trace`.
18
+ #
19
+ # `trace` is one of:
20
+ # { kind: :outer, index: Integer }
21
+ # { kind: :inner, outer_index: Integer, inner_index: Integer, stack_height: T.nilable(Integer) }
22
+ #
23
+ # Mirrors `TracedInstruction` / `InstructionTrace` from @solana/transaction-introspection.
24
+ class TracedInstruction < T::Struct
25
+ const :instruction, Instructions::Instruction
26
+ const :trace, T::Hash[Symbol, T.untyped]
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,60 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'compiled_transaction_message'
5
+ require_relative 'get_inner_instructions'
6
+ require_relative 'get_instructions'
7
+ require_relative 'loaded_addresses'
8
+ require_relative 'types'
9
+
10
+ module Solana::Ruby::Kit
11
+ module TransactionIntrospection
12
+ extend T::Sig
13
+
14
+ module_function
15
+
16
+ # Returns every instruction in a confirmed transaction as
17
+ # TracedInstructions, in the order an explorer displays them: each outer
18
+ # instruction followed immediately by the inner instructions its CPIs
19
+ # produced.
20
+ #
21
+ # If +meta+ is nil, only outer instructions are returned. If
22
+ # +loaded_addresses+ is nil, only static accounts are used to resolve
23
+ # indices — pass the `loadedAddresses` from a `getTransaction` response's
24
+ # `meta` for v0 transactions that load accounts from address lookup tables.
25
+ #
26
+ # Mirrors `walkInstructions({ compiledMessage, loadedAddresses, meta })`.
27
+ sig do
28
+ params(
29
+ compiled_message: CompiledTransactionMessage,
30
+ loaded_addresses: T.nilable(LoadedAddresses),
31
+ meta: T.nilable(T::Hash[String, T.untyped])
32
+ ).returns(T::Array[TracedInstruction])
33
+ end
34
+ def walk_instructions(compiled_message:, loaded_addresses: nil, meta: nil)
35
+ account_metas = get_account_metas_from_compiled_transaction_message(compiled_message, loaded_addresses)
36
+ outer_instructions = get_instructions_from_compiled_transaction_message_with_metas(compiled_message, account_metas)
37
+
38
+ inner_by_outer_index = T.let({}, T::Hash[Integer, T::Array[TracedInstruction]])
39
+ if meta
40
+ get_inner_instructions_from_meta(meta, account_metas).each do |inner|
41
+ outer_index = inner.trace.fetch(:outer_index)
42
+ (inner_by_outer_index[outer_index] ||= []) << inner
43
+ end
44
+ end
45
+
46
+ result = T.let([], T::Array[TracedInstruction])
47
+ outer_instructions.each_with_index do |instruction, index|
48
+ result << TracedInstruction.new(instruction: instruction, trace: { kind: :outer, index: index })
49
+ group = inner_by_outer_index.delete(index)
50
+ result.concat(group) if group
51
+ end
52
+ # Inner groups whose index matches no outer instruction can only come
53
+ # from malformed input (e.g. `meta` paired with the wrong message).
54
+ # Append them rather than dropping them so no instruction is ever lost.
55
+ inner_by_outer_index.each_value { |group| result.concat(group) }
56
+
57
+ result
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,13 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ # Helpers for inspecting confirmed Solana transactions and walking their
5
+ # outer and inner instructions.
6
+ # Mirrors @solana/transaction-introspection.
7
+ require_relative 'transaction_introspection/loaded_addresses'
8
+ require_relative 'transaction_introspection/compiled_transaction_message'
9
+ require_relative 'transaction_introspection/types'
10
+ require_relative 'transaction_introspection/get_instructions'
11
+ require_relative 'transaction_introspection/get_inner_instructions'
12
+ require_relative 'transaction_introspection/walk_instructions'
13
+ require_relative 'transaction_introspection/decode_rpc_transaction'
@@ -4,7 +4,7 @@
4
4
  module Solana
5
5
  module Ruby
6
6
  module Kit
7
- VERSION = '0.1.9'
7
+ VERSION = '7.0.0.1'
8
8
  end
9
9
  end
10
10
  end
@@ -51,6 +51,7 @@ require_relative 'kit/sysvars'
51
51
  require_relative 'kit/transaction_confirmation'
52
52
  require_relative 'kit/instruction_plans'
53
53
  require_relative 'kit/resource_limit_estimation'
54
+ require_relative 'kit/transaction_introspection'
54
55
 
55
56
  # Solana::Ruby::Kit is a Ruby translation of @anza-xyz/kit — the JavaScript SDK for
56
57
  # building Solana apps — into idiomatic Ruby with Sorbet static types.
@@ -80,6 +81,7 @@ require_relative 'kit/resource_limit_estimation'
80
81
  # Solana::Ruby::Kit::TransactionConfirmation — confirmation polling (@solana/transaction-confirmation)
81
82
  # Solana::Ruby::Kit::InstructionPlans — multi-tx planning (@solana/instruction-plans)
82
83
  # Solana::Ruby::Kit::WalletStandard — server-side sig verify (@solana/wallet-standard)
84
+ # Solana::Ruby::Kit::TransactionIntrospection — decode/walk instructions (@solana/transaction-introspection)
83
85
 
84
86
  module Solana::Ruby::Kit
85
87
  extend T::Sig
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solana-ruby-kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 7.0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Zupan, Idhra Inc.
@@ -209,6 +209,7 @@ files:
209
209
  - lib/solana/ruby/kit/functional.rb
210
210
  - lib/solana/ruby/kit/instruction_plans.rb
211
211
  - lib/solana/ruby/kit/instruction_plans/instruction_plan.rb
212
+ - lib/solana/ruby/kit/instruction_plans/max_instructions.rb
212
213
  - lib/solana/ruby/kit/instruction_plans/plans.rb
213
214
  - lib/solana/ruby/kit/instruction_plans/transaction_plan.rb
214
215
  - lib/solana/ruby/kit/instruction_plans/transaction_plan_executor.rb
@@ -254,6 +255,7 @@ files:
254
255
  - lib/solana/ruby/kit/rpc/api/get_token_account_balance.rb
255
256
  - lib/solana/ruby/kit/rpc/api/get_token_accounts_by_owner.rb
256
257
  - lib/solana/ruby/kit/rpc/api/get_transaction.rb
258
+ - lib/solana/ruby/kit/rpc/api/get_transactions_for_address.rb
257
259
  - lib/solana/ruby/kit/rpc/api/get_vote_accounts.rb
258
260
  - lib/solana/ruby/kit/rpc/api/is_blockhash_valid.rb
259
261
  - lib/solana/ruby/kit/rpc/api/request_airdrop.rb
@@ -288,6 +290,7 @@ files:
288
290
  - lib/solana/ruby/kit/signers/keypair_signer.rb
289
291
  - lib/solana/ruby/kit/subscribable.rb
290
292
  - lib/solana/ruby/kit/subscribable/async_iterable.rb
293
+ - lib/solana/ruby/kit/subscribable/bridge_store_to_async_iterable.rb
291
294
  - lib/solana/ruby/kit/subscribable/data_publisher.rb
292
295
  - lib/solana/ruby/kit/subscribable/reactive_action_store.rb
293
296
  - lib/solana/ruby/kit/subscribable/reactive_stream_store.rb
@@ -298,6 +301,14 @@ files:
298
301
  - lib/solana/ruby/kit/sysvars/last_restart_slot.rb
299
302
  - lib/solana/ruby/kit/sysvars/rent.rb
300
303
  - lib/solana/ruby/kit/transaction_confirmation.rb
304
+ - lib/solana/ruby/kit/transaction_introspection.rb
305
+ - lib/solana/ruby/kit/transaction_introspection/compiled_transaction_message.rb
306
+ - lib/solana/ruby/kit/transaction_introspection/decode_rpc_transaction.rb
307
+ - lib/solana/ruby/kit/transaction_introspection/get_inner_instructions.rb
308
+ - lib/solana/ruby/kit/transaction_introspection/get_instructions.rb
309
+ - lib/solana/ruby/kit/transaction_introspection/loaded_addresses.rb
310
+ - lib/solana/ruby/kit/transaction_introspection/types.rb
311
+ - lib/solana/ruby/kit/transaction_introspection/walk_instructions.rb
301
312
  - lib/solana/ruby/kit/transaction_messages.rb
302
313
  - lib/solana/ruby/kit/transaction_messages/compute_budget.rb
303
314
  - lib/solana/ruby/kit/transaction_messages/transaction_message.rb