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.
- checksums.yaml +4 -4
- data/lib/solana/ruby/kit/errors.rb +26 -1
- data/lib/solana/ruby/kit/instruction_plans/instruction_plan.rb +34 -7
- data/lib/solana/ruby/kit/instruction_plans/max_instructions.rb +59 -0
- data/lib/solana/ruby/kit/instruction_plans/transaction_planner.rb +49 -9
- data/lib/solana/ruby/kit/instruction_plans.rb +1 -0
- data/lib/solana/ruby/kit/rpc/api/get_inflation_reward.rb +4 -2
- data/lib/solana/ruby/kit/rpc/api/get_transactions_for_address.rb +208 -0
- data/lib/solana/ruby/kit/rpc/api/get_vote_accounts.rb +2 -0
- data/lib/solana/ruby/kit/rpc/client.rb +2 -0
- data/lib/solana/ruby/kit/subscribable/bridge_store_to_async_iterable.rb +172 -0
- data/lib/solana/ruby/kit/subscribable.rb +1 -0
- data/lib/solana/ruby/kit/transaction_introspection/compiled_transaction_message.rb +168 -0
- data/lib/solana/ruby/kit/transaction_introspection/decode_rpc_transaction.rb +176 -0
- data/lib/solana/ruby/kit/transaction_introspection/get_inner_instructions.rb +88 -0
- data/lib/solana/ruby/kit/transaction_introspection/get_instructions.rb +134 -0
- data/lib/solana/ruby/kit/transaction_introspection/loaded_addresses.rb +19 -0
- data/lib/solana/ruby/kit/transaction_introspection/types.rb +29 -0
- data/lib/solana/ruby/kit/transaction_introspection/walk_instructions.rb +60 -0
- data/lib/solana/ruby/kit/transaction_introspection.rb +13 -0
- data/lib/solana/ruby/kit/version.rb +1 -1
- data/lib/solana/ruby/kit.rb +2 -0
- metadata +12 -1
|
@@ -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'
|
data/lib/solana/ruby/kit.rb
CHANGED
|
@@ -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
|
|
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
|