solana-ruby-kit 0.1.0
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 +7 -0
- data/lib/core_extensions/tapioca/name_patch.rb +32 -0
- data/lib/core_extensions/tapioca/required_ancestors.rb +13 -0
- data/lib/generators/solana/ruby/kit/install/install_generator.rb +17 -0
- data/lib/generators/solana/ruby/kit/install/templates/solana_ruby_kit.rb.tt +8 -0
- data/lib/solana/ruby/kit/accounts/account.rb +47 -0
- data/lib/solana/ruby/kit/accounts/maybe_account.rb +86 -0
- data/lib/solana/ruby/kit/accounts.rb +6 -0
- data/lib/solana/ruby/kit/addresses/address.rb +133 -0
- data/lib/solana/ruby/kit/addresses/curve.rb +112 -0
- data/lib/solana/ruby/kit/addresses/program_derived_address.rb +155 -0
- data/lib/solana/ruby/kit/addresses/public_key.rb +39 -0
- data/lib/solana/ruby/kit/addresses.rb +11 -0
- data/lib/solana/ruby/kit/codecs/bytes.rb +58 -0
- data/lib/solana/ruby/kit/codecs/codec.rb +135 -0
- data/lib/solana/ruby/kit/codecs/data_structures.rb +177 -0
- data/lib/solana/ruby/kit/codecs/decoder.rb +43 -0
- data/lib/solana/ruby/kit/codecs/encoder.rb +52 -0
- data/lib/solana/ruby/kit/codecs/numbers.rb +217 -0
- data/lib/solana/ruby/kit/codecs/strings.rb +116 -0
- data/lib/solana/ruby/kit/codecs.rb +25 -0
- data/lib/solana/ruby/kit/configuration.rb +48 -0
- data/lib/solana/ruby/kit/encoding/base58.rb +62 -0
- data/lib/solana/ruby/kit/errors.rb +226 -0
- data/lib/solana/ruby/kit/fast_stable_stringify.rb +62 -0
- data/lib/solana/ruby/kit/functional.rb +29 -0
- data/lib/solana/ruby/kit/instruction_plans/plans.rb +27 -0
- data/lib/solana/ruby/kit/instruction_plans.rb +47 -0
- data/lib/solana/ruby/kit/instructions/accounts.rb +80 -0
- data/lib/solana/ruby/kit/instructions/instruction.rb +71 -0
- data/lib/solana/ruby/kit/instructions/roles.rb +84 -0
- data/lib/solana/ruby/kit/instructions.rb +7 -0
- data/lib/solana/ruby/kit/keys/key_pair.rb +84 -0
- data/lib/solana/ruby/kit/keys/private_key.rb +39 -0
- data/lib/solana/ruby/kit/keys/public_key.rb +31 -0
- data/lib/solana/ruby/kit/keys/signatures.rb +171 -0
- data/lib/solana/ruby/kit/keys.rb +11 -0
- data/lib/solana/ruby/kit/offchain_messages/codec.rb +107 -0
- data/lib/solana/ruby/kit/offchain_messages/message.rb +22 -0
- data/lib/solana/ruby/kit/offchain_messages.rb +16 -0
- data/lib/solana/ruby/kit/options/option.rb +132 -0
- data/lib/solana/ruby/kit/options.rb +5 -0
- data/lib/solana/ruby/kit/plugin_core.rb +58 -0
- data/lib/solana/ruby/kit/programs.rb +42 -0
- data/lib/solana/ruby/kit/promises.rb +85 -0
- data/lib/solana/ruby/kit/railtie.rb +18 -0
- data/lib/solana/ruby/kit/rpc/api/get_account_info.rb +76 -0
- data/lib/solana/ruby/kit/rpc/api/get_balance.rb +41 -0
- data/lib/solana/ruby/kit/rpc/api/get_block_height.rb +29 -0
- data/lib/solana/ruby/kit/rpc/api/get_epoch_info.rb +47 -0
- data/lib/solana/ruby/kit/rpc/api/get_latest_blockhash.rb +52 -0
- data/lib/solana/ruby/kit/rpc/api/get_minimum_balance_for_rent_exemption.rb +29 -0
- data/lib/solana/ruby/kit/rpc/api/get_multiple_accounts.rb +56 -0
- data/lib/solana/ruby/kit/rpc/api/get_program_accounts.rb +60 -0
- data/lib/solana/ruby/kit/rpc/api/get_signature_statuses.rb +56 -0
- data/lib/solana/ruby/kit/rpc/api/get_slot.rb +30 -0
- data/lib/solana/ruby/kit/rpc/api/get_token_account_balance.rb +38 -0
- data/lib/solana/ruby/kit/rpc/api/get_token_accounts_by_owner.rb +48 -0
- data/lib/solana/ruby/kit/rpc/api/get_transaction.rb +36 -0
- data/lib/solana/ruby/kit/rpc/api/get_vote_accounts.rb +62 -0
- data/lib/solana/ruby/kit/rpc/api/is_blockhash_valid.rb +41 -0
- data/lib/solana/ruby/kit/rpc/api/request_airdrop.rb +35 -0
- data/lib/solana/ruby/kit/rpc/api/send_transaction.rb +61 -0
- data/lib/solana/ruby/kit/rpc/api/simulate_transaction.rb +47 -0
- data/lib/solana/ruby/kit/rpc/client.rb +83 -0
- data/lib/solana/ruby/kit/rpc/transport.rb +137 -0
- data/lib/solana/ruby/kit/rpc.rb +13 -0
- data/lib/solana/ruby/kit/rpc_parsed_types/address_lookup_table.rb +33 -0
- data/lib/solana/ruby/kit/rpc_parsed_types/nonce_account.rb +33 -0
- data/lib/solana/ruby/kit/rpc_parsed_types/stake_account.rb +51 -0
- data/lib/solana/ruby/kit/rpc_parsed_types/token_account.rb +52 -0
- data/lib/solana/ruby/kit/rpc_parsed_types/vote_account.rb +38 -0
- data/lib/solana/ruby/kit/rpc_parsed_types.rb +16 -0
- data/lib/solana/ruby/kit/rpc_subscriptions/api/account_notifications.rb +29 -0
- data/lib/solana/ruby/kit/rpc_subscriptions/api/logs_notifications.rb +28 -0
- data/lib/solana/ruby/kit/rpc_subscriptions/api/program_notifications.rb +30 -0
- data/lib/solana/ruby/kit/rpc_subscriptions/api/root_notifications.rb +19 -0
- data/lib/solana/ruby/kit/rpc_subscriptions/api/signature_notifications.rb +28 -0
- data/lib/solana/ruby/kit/rpc_subscriptions/api/slot_notifications.rb +19 -0
- data/lib/solana/ruby/kit/rpc_subscriptions/autopinger.rb +42 -0
- data/lib/solana/ruby/kit/rpc_subscriptions/client.rb +80 -0
- data/lib/solana/ruby/kit/rpc_subscriptions/subscription.rb +58 -0
- data/lib/solana/ruby/kit/rpc_subscriptions/transport.rb +163 -0
- data/lib/solana/ruby/kit/rpc_subscriptions.rb +12 -0
- data/lib/solana/ruby/kit/rpc_types/account_info.rb +53 -0
- data/lib/solana/ruby/kit/rpc_types/cluster_url.rb +56 -0
- data/lib/solana/ruby/kit/rpc_types/commitment.rb +52 -0
- data/lib/solana/ruby/kit/rpc_types/lamports.rb +43 -0
- data/lib/solana/ruby/kit/rpc_types.rb +8 -0
- data/lib/solana/ruby/kit/signers/keypair_signer.rb +126 -0
- data/lib/solana/ruby/kit/signers.rb +5 -0
- data/lib/solana/ruby/kit/subscribable/async_iterable.rb +80 -0
- data/lib/solana/ruby/kit/subscribable/data_publisher.rb +90 -0
- data/lib/solana/ruby/kit/subscribable.rb +13 -0
- data/lib/solana/ruby/kit/sysvars/addresses.rb +19 -0
- data/lib/solana/ruby/kit/sysvars/clock.rb +37 -0
- data/lib/solana/ruby/kit/sysvars/epoch_schedule.rb +34 -0
- data/lib/solana/ruby/kit/sysvars/last_restart_slot.rb +22 -0
- data/lib/solana/ruby/kit/sysvars/rent.rb +29 -0
- data/lib/solana/ruby/kit/sysvars.rb +33 -0
- data/lib/solana/ruby/kit/transaction_confirmation.rb +159 -0
- data/lib/solana/ruby/kit/transaction_messages/transaction_message.rb +168 -0
- data/lib/solana/ruby/kit/transaction_messages.rb +5 -0
- data/lib/solana/ruby/kit/transactions/transaction.rb +135 -0
- data/lib/solana/ruby/kit/transactions.rb +5 -0
- data/lib/solana/ruby/kit/version.rb +10 -0
- data/lib/solana/ruby/kit.rb +100 -0
- data/solana-ruby-kit.gemspec +29 -0
- metadata +311 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'base64'
|
|
5
|
+
|
|
6
|
+
module Solana::Ruby::Kit
|
|
7
|
+
module Codecs
|
|
8
|
+
# String codecs — mirrors @solana/codecs-strings.
|
|
9
|
+
module Strings
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# UTF-8 string codec.
|
|
15
|
+
# When +size+ is given the encoded bytes are fixed to that length
|
|
16
|
+
# (zero-padded or truncated); otherwise the codec is variable-length
|
|
17
|
+
# and must be used inside a size-prefixed container.
|
|
18
|
+
sig { params(size: T.nilable(Integer)).returns(Codec) }
|
|
19
|
+
def utf8_codec(size: nil)
|
|
20
|
+
enc = Encoder.new(fixed_size: size) do |v|
|
|
21
|
+
raw = v.to_s.encode('UTF-8').b
|
|
22
|
+
if size
|
|
23
|
+
raw.bytesize <= size ? raw.ljust(size, "\x00") : raw.byteslice(0, size) || ''.b
|
|
24
|
+
else
|
|
25
|
+
raw
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
dec = Decoder.new(fixed_size: size) do |bytes, offset|
|
|
29
|
+
len = size || (bytes.bytesize - offset)
|
|
30
|
+
slice = bytes.b.byteslice(offset, len) || ''.b
|
|
31
|
+
# Strip null padding for fixed-size strings
|
|
32
|
+
str = size ? slice.delete_suffix("\x00" * slice.bytesize.times.take_while { |i| slice.b[-1 - i] == "\x00" }.length) : slice
|
|
33
|
+
[str.force_encoding('UTF-8'), len]
|
|
34
|
+
end
|
|
35
|
+
Codec.new(enc, dec)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Base58 codec — uses Solana::Ruby::Kit::Encoding::Base58.
|
|
39
|
+
sig { returns(Codec) }
|
|
40
|
+
def base58_codec
|
|
41
|
+
enc = Encoder.new do |v|
|
|
42
|
+
Solana::Ruby::Kit::Encoding::Base58.decode(v.to_s)
|
|
43
|
+
end
|
|
44
|
+
dec = Decoder.new do |bytes, offset|
|
|
45
|
+
remaining = bytes.b.byteslice(offset..) || ''.b
|
|
46
|
+
[Solana::Ruby::Kit::Encoding::Base58.encode(remaining), remaining.bytesize]
|
|
47
|
+
end
|
|
48
|
+
Codec.new(enc, dec)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Base64 codec — strict (no newlines).
|
|
52
|
+
sig { returns(Codec) }
|
|
53
|
+
def base64_codec
|
|
54
|
+
enc = Encoder.new do |v|
|
|
55
|
+
Base64.strict_encode64(v.to_s)
|
|
56
|
+
end
|
|
57
|
+
dec = Decoder.new do |bytes, offset|
|
|
58
|
+
remaining = bytes.b.byteslice(offset..) || ''.b
|
|
59
|
+
[Base64.strict_decode64(remaining.force_encoding('ASCII')), remaining.bytesize]
|
|
60
|
+
end
|
|
61
|
+
Codec.new(enc, dec)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Hex codec — lower-case hex string ↔ binary bytes.
|
|
65
|
+
sig { returns(Codec) }
|
|
66
|
+
def hex_codec
|
|
67
|
+
enc = Encoder.new do |v|
|
|
68
|
+
[v.to_s.tr(' ', '')].pack('H*')
|
|
69
|
+
end
|
|
70
|
+
dec = Decoder.new do |bytes, offset|
|
|
71
|
+
remaining = bytes.b.byteslice(offset..) || ''.b
|
|
72
|
+
[remaining.unpack1('H*'), remaining.bytesize]
|
|
73
|
+
end
|
|
74
|
+
Codec.new(enc, dec)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Fixed-size raw bytes passthrough.
|
|
78
|
+
sig { params(size: Integer).returns(Codec) }
|
|
79
|
+
def bytes_codec(size)
|
|
80
|
+
enc = Encoder.new(fixed_size: size) do |v|
|
|
81
|
+
b = v.is_a?(String) ? v.b : v.to_s.b
|
|
82
|
+
Kernel.raise ArgumentError, "Expected #{size} bytes, got #{b.bytesize}" if b.bytesize != size
|
|
83
|
+
|
|
84
|
+
b
|
|
85
|
+
end
|
|
86
|
+
dec = Decoder.new(fixed_size: size) do |bytes, offset|
|
|
87
|
+
slice = bytes.b.byteslice(offset, size) || ''.b
|
|
88
|
+
[slice, size]
|
|
89
|
+
end
|
|
90
|
+
Codec.new(enc, dec)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Bit-array codec.
|
|
94
|
+
# Encodes an Array of booleans into +size+ bytes (LSB-first within each byte).
|
|
95
|
+
sig { params(size: Integer).returns(Codec) }
|
|
96
|
+
def bit_array_codec(size)
|
|
97
|
+
total_bits = size * 8
|
|
98
|
+
enc = Encoder.new(fixed_size: size) do |bits|
|
|
99
|
+
arr = T.cast(bits, T::Array[T::Boolean])
|
|
100
|
+
bytes = Array.new(size, 0)
|
|
101
|
+
arr.first(total_bits).each_with_index do |bit, idx|
|
|
102
|
+
bytes[idx / 8] |= (1 << (idx % 8)) if bit
|
|
103
|
+
end
|
|
104
|
+
bytes.pack('C*')
|
|
105
|
+
end
|
|
106
|
+
dec = Decoder.new(fixed_size: size) do |bytes, offset|
|
|
107
|
+
slice = bytes.b.byteslice(offset, size) || ''.b
|
|
108
|
+
byte_arr = T.cast(T.unsafe(slice).unpack('C*'), T::Array[Integer])
|
|
109
|
+
bits = byte_arr.flat_map { |byte| 8.times.map { |i| byte[i] == 1 } }
|
|
110
|
+
[bits, size]
|
|
111
|
+
end
|
|
112
|
+
Codec.new(enc, dec)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'set'
|
|
5
|
+
|
|
6
|
+
# Codec system — mirrors @solana/codecs.
|
|
7
|
+
# Provides Encoder, Decoder, and Codec classes plus helpers for numbers,
|
|
8
|
+
# strings, and composite data structures.
|
|
9
|
+
require_relative 'codecs/bytes'
|
|
10
|
+
require_relative 'codecs/encoder'
|
|
11
|
+
require_relative 'codecs/decoder'
|
|
12
|
+
require_relative 'codecs/codec'
|
|
13
|
+
require_relative 'codecs/numbers'
|
|
14
|
+
require_relative 'codecs/strings'
|
|
15
|
+
require_relative 'codecs/data_structures'
|
|
16
|
+
|
|
17
|
+
module Solana::Ruby::Kit
|
|
18
|
+
module Codecs
|
|
19
|
+
# Make number helpers directly available as Codecs.u8_codec etc.
|
|
20
|
+
extend Numbers
|
|
21
|
+
extend Strings
|
|
22
|
+
extend DataStructures
|
|
23
|
+
extend Bytes
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
class Configuration
|
|
6
|
+
extend T::Sig
|
|
7
|
+
|
|
8
|
+
sig { returns(String) }
|
|
9
|
+
attr_reader :rpc_url
|
|
10
|
+
|
|
11
|
+
sig { returns(T.nilable(String)) }
|
|
12
|
+
attr_reader :ws_url
|
|
13
|
+
|
|
14
|
+
sig { returns(Symbol) }
|
|
15
|
+
attr_reader :commitment
|
|
16
|
+
|
|
17
|
+
sig { returns(Integer) }
|
|
18
|
+
attr_reader :timeout
|
|
19
|
+
|
|
20
|
+
sig { void }
|
|
21
|
+
def initialize
|
|
22
|
+
@rpc_url = T.let('https://api.mainnet-beta.solana.com', String)
|
|
23
|
+
@ws_url = T.let(nil, T.nilable(String))
|
|
24
|
+
@commitment = T.let(:confirmed, Symbol)
|
|
25
|
+
@timeout = T.let(30, Integer)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
sig { params(value: String).void }
|
|
29
|
+
def rpc_url=(value)
|
|
30
|
+
@rpc_url = value
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
sig { params(value: T.nilable(String)).void }
|
|
34
|
+
def ws_url=(value)
|
|
35
|
+
@ws_url = value
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
sig { params(value: Symbol).void }
|
|
39
|
+
def commitment=(value)
|
|
40
|
+
@commitment = value
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
sig { params(value: Integer).void }
|
|
44
|
+
def timeout=(value)
|
|
45
|
+
@timeout = value
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
module Encoding
|
|
6
|
+
# Base58 codec using the Bitcoin/Solana alphabet (no check bytes).
|
|
7
|
+
# This module provides the shared base58 encoding used by both
|
|
8
|
+
# Solana::Ruby::Kit::Addresses and Solana::Ruby::Kit::Keys::Signatures.
|
|
9
|
+
module Base58
|
|
10
|
+
extend T::Sig
|
|
11
|
+
ALPHABET = T.let(
|
|
12
|
+
'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
|
|
13
|
+
String
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
# Encodes a binary String to a base58 string.
|
|
19
|
+
sig { params(bytes: String).returns(String) }
|
|
20
|
+
def encode(bytes)
|
|
21
|
+
# Each leading zero byte maps to a leading '1'.
|
|
22
|
+
leading_ones = 0
|
|
23
|
+
bytes.each_byte { |b| b == 0 ? leading_ones += 1 : break }
|
|
24
|
+
|
|
25
|
+
# Convert big-endian byte string to an integer.
|
|
26
|
+
n = bytes.unpack1('H*').to_i(16)
|
|
27
|
+
|
|
28
|
+
result = +''
|
|
29
|
+
while n > 0
|
|
30
|
+
result.prepend(T.must(ALPHABET[n % 58]))
|
|
31
|
+
n /= 58
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
('1' * leading_ones) + result
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Decodes a base58 string to a binary String.
|
|
38
|
+
# Raises ArgumentError if the string contains characters not in the alphabet.
|
|
39
|
+
sig { params(str: String).returns(String) }
|
|
40
|
+
def decode(str)
|
|
41
|
+
# Each leading '1' maps to a leading zero byte.
|
|
42
|
+
leading_zeros = 0
|
|
43
|
+
str.each_char { |c| c == '1' ? leading_zeros += 1 : break }
|
|
44
|
+
|
|
45
|
+
n = 0
|
|
46
|
+
str.each_char do |c|
|
|
47
|
+
idx = ALPHABET.index(c)
|
|
48
|
+
Kernel.raise ArgumentError, "Invalid base58 character: #{c.inspect}" if idx.nil?
|
|
49
|
+
|
|
50
|
+
n = n * 58 + idx
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Convert integer back to a big-endian byte string.
|
|
54
|
+
hex = n.zero? ? '' : n.to_s(16)
|
|
55
|
+
hex = hex.rjust((hex.length + 1) & ~1, '0') # guarantee even hex length
|
|
56
|
+
|
|
57
|
+
raw = [hex].pack('H*')
|
|
58
|
+
("\x00" * leading_zeros + raw).b
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
# Maps TypeScript's SolanaError pattern to Ruby exceptions.
|
|
6
|
+
# Each error code corresponds to a constant and carries structured context.
|
|
7
|
+
# Mirrors @solana/errors codes.ts + messages.ts.
|
|
8
|
+
class SolanaError < StandardError
|
|
9
|
+
extend T::Sig
|
|
10
|
+
|
|
11
|
+
# ── General ──────────────────────────────────────────────────────────────
|
|
12
|
+
INVALID_KEYPAIR_SEED_LENGTH = :SOLANA_ERROR__INVALID_KEYPAIR_SEED_LENGTH
|
|
13
|
+
INVALID_NONCE = :SOLANA_ERROR__INVALID_NONCE
|
|
14
|
+
|
|
15
|
+
# ── Addresses ─────────────────────────────────────────────────────────────
|
|
16
|
+
ADDRESSES__INVALID_BASE58_ENCODED_ADDRESS = :SOLANA_ERROR__ADDRESSES__INVALID_BASE58_ENCODED_ADDRESS
|
|
17
|
+
ADDRESSES__INVALID_BYTE_LENGTH_FOR_ADDRESS = :SOLANA_ERROR__ADDRESSES__INVALID_BYTE_LENGTH_FOR_ADDRESS
|
|
18
|
+
ADDRESSES__STRING_LENGTH_OUT_OF_RANGE = :SOLANA_ERROR__ADDRESSES__STRING_LENGTH_OUT_OF_RANGE_FOR_ADDRESS
|
|
19
|
+
ADDRESSES__INVALID_ED25519_PUBLIC_KEY = :SOLANA_ERROR__ADDRESSES__INVALID_ED25519_PUBLIC_KEY
|
|
20
|
+
ADDRESSES__SEEDS_POINT_ON_CURVE = :SOLANA_ERROR__ADDRESSES__SEEDS_POINT_ON_CURVE
|
|
21
|
+
ADDRESSES__MAX_SEED_LENGTH_EXCEEDED = :SOLANA_ERROR__ADDRESSES__MAX_SEED_LENGTH_EXCEEDED
|
|
22
|
+
ADDRESSES__TOO_MANY_SEEDS = :SOLANA_ERROR__ADDRESSES__TOO_MANY_SEEDS
|
|
23
|
+
ADDRESSES__FAILED_TO_FIND_VIABLE_PDA_BUMP_SEED = :SOLANA_ERROR__ADDRESSES__FAILED_TO_FIND_VIABLE_PDA_BUMP_SEED
|
|
24
|
+
ADDRESSES__INVALID_SEEDS_POINT_ON_CURVE = :SOLANA_ERROR__ADDRESSES__INVALID_SEEDS_POINT_ON_CURVE
|
|
25
|
+
ADDRESSES__PDA_BUMP_SEED_OUT_OF_RANGE = :SOLANA_ERROR__ADDRESSES__PDA_BUMP_SEED_OUT_OF_RANGE
|
|
26
|
+
|
|
27
|
+
# ── Accounts ──────────────────────────────────────────────────────────────
|
|
28
|
+
ACCOUNTS__ACCOUNT_NOT_FOUND = :SOLANA_ERROR__ACCOUNTS__ACCOUNT_NOT_FOUND
|
|
29
|
+
ACCOUNTS__ONE_OR_MORE_ACCOUNTS_NOT_FOUND = :SOLANA_ERROR__ACCOUNTS__ONE_OR_MORE_ACCOUNTS_NOT_FOUND
|
|
30
|
+
ACCOUNTS__EXPECTED_DECODED_ACCOUNT = :SOLANA_ERROR__ACCOUNTS__EXPECTED_DECODED_ACCOUNT
|
|
31
|
+
ACCOUNTS__EXPECTED_ALL_ACCOUNTS_TO_BE_DECODED = :SOLANA_ERROR__ACCOUNTS__EXPECTED_ALL_ACCOUNTS_TO_BE_DECODED
|
|
32
|
+
ACCOUNTS__FAILED_TO_DECODE_ACCOUNT = :SOLANA_ERROR__ACCOUNTS__FAILED_TO_DECODE_ACCOUNT
|
|
33
|
+
|
|
34
|
+
# ── Keys ──────────────────────────────────────────────────────────────────
|
|
35
|
+
KEYS__INVALID_KEY_PAIR_BYTE_LENGTH = :SOLANA_ERROR__KEYS__INVALID_KEY_PAIR_BYTE_LENGTH
|
|
36
|
+
KEYS__PUBLIC_KEY_MUST_MATCH_PRIVATE_KEY = :SOLANA_ERROR__KEYS__PUBLIC_KEY_MUST_MATCH_PRIVATE_KEY
|
|
37
|
+
KEYS__INVALID_SIGNATURE_BYTE_LENGTH = :SOLANA_ERROR__KEYS__INVALID_SIGNATURE_BYTE_LENGTH
|
|
38
|
+
KEYS__SIGNATURE_STRING_LENGTH_OUT_OF_RANGE = :SOLANA_ERROR__KEYS__SIGNATURE_STRING_LENGTH_OUT_OF_RANGE
|
|
39
|
+
|
|
40
|
+
# ── Instructions ──────────────────────────────────────────────────────────
|
|
41
|
+
INSTRUCTIONS__EXPECTED_TO_HAVE_ACCOUNTS = :SOLANA_ERROR__INSTRUCTIONS__EXPECTED_TO_HAVE_ACCOUNTS
|
|
42
|
+
INSTRUCTIONS__EXPECTED_TO_HAVE_DATA = :SOLANA_ERROR__INSTRUCTIONS__EXPECTED_TO_HAVE_DATA
|
|
43
|
+
INSTRUCTIONS__PROGRAM_ADDRESS_MISMATCH = :SOLANA_ERROR__INSTRUCTIONS__PROGRAM_ADDRESS_MISMATCH
|
|
44
|
+
INSTRUCTIONS__ACCOUNT_NOT_FOUND = :SOLANA_ERROR__INSTRUCTIONS__ACCOUNT_NOT_FOUND
|
|
45
|
+
INSTRUCTIONS__EXPECTED_ALL_ACCOUNTS_TO_BE_DECODED = :SOLANA_ERROR__INSTRUCTIONS__EXPECTED_ALL_ACCOUNTS_TO_BE_DECODED
|
|
46
|
+
|
|
47
|
+
# ── Transactions ──────────────────────────────────────────────────────────
|
|
48
|
+
TRANSACTIONS__TRANSACTION_NOT_SIGNABLE = :SOLANA_ERROR__TRANSACTIONS__TRANSACTION_NOT_SIGNABLE
|
|
49
|
+
TRANSACTIONS__MISSING_SIGNER = :SOLANA_ERROR__TRANSACTIONS__MISSING_SIGNER
|
|
50
|
+
TRANSACTIONS__VERSION_NUMBER_OUT_OF_RANGE = :SOLANA_ERROR__TRANSACTIONS__VERSION_NUMBER_OUT_OF_RANGE
|
|
51
|
+
TRANSACTIONS__FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_CONTENTS = :SOLANA_ERROR__TRANSACTIONS__FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_CONTENTS
|
|
52
|
+
TRANSACTIONS__FAILED_TO_DECOMPILE_FEE_PAYER_MISSING = :SOLANA_ERROR__TRANSACTIONS__FAILED_TO_DECOMPILE_FEE_PAYER_MISSING
|
|
53
|
+
TRANSACTIONS__FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND = :SOLANA_ERROR__TRANSACTIONS__FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND
|
|
54
|
+
TRANSACTIONS__SEND_TRANSACTION_PREFLIGHT_FAILURE = :SOLANA_ERROR__TRANSACTIONS__SEND_TRANSACTION_PREFLIGHT_FAILURE
|
|
55
|
+
TRANSACTIONS__BLOCKHASH_NOT_FOUND = :SOLANA_ERROR__TRANSACTIONS__BLOCKHASH_NOT_FOUND
|
|
56
|
+
TRANSACTIONS__FAILED_TRANSACTION_PLAN = :SOLANA_ERROR__TRANSACTIONS__FAILED_TRANSACTION_PLAN
|
|
57
|
+
TRANSACTIONS__TRANSACTION_EXPIRED_BLOCKHEIGHT_EXCEEDED = :SOLANA_ERROR__TRANSACTIONS__TRANSACTION_EXPIRED_BLOCKHEIGHT_EXCEEDED
|
|
58
|
+
TRANSACTIONS__TRANSACTION_EXPIRED_NONCE_INVALID = :SOLANA_ERROR__TRANSACTIONS__TRANSACTION_EXPIRED_NONCE_INVALID
|
|
59
|
+
TRANSACTIONS__TRANSACTION_CONFIRMATION_TIMEOUT = :SOLANA_ERROR__TRANSACTIONS__TRANSACTION_CONFIRMATION_TIMEOUT
|
|
60
|
+
|
|
61
|
+
# ── Signers ───────────────────────────────────────────────────────────────
|
|
62
|
+
SIGNERS__ADDRESS_CANNOT_HAVE_MULTIPLE_SIGNERS = :SOLANA_ERROR__SIGNERS__ADDRESS_CANNOT_HAVE_MULTIPLE_SIGNERS
|
|
63
|
+
SIGNERS__EXPECTED_MESSAGE_MODIFYING_SIGNER = :SOLANA_ERROR__SIGNERS__EXPECTED_MESSAGE_MODIFYING_SIGNER
|
|
64
|
+
SIGNERS__EXPECTED_TRANSACTION_MODIFYING_SIGNER = :SOLANA_ERROR__SIGNERS__EXPECTED_TRANSACTION_MODIFYING_SIGNER
|
|
65
|
+
SIGNERS__EXPECTED_TRANSACTION_SENDING_SIGNER = :SOLANA_ERROR__SIGNERS__EXPECTED_TRANSACTION_SENDING_SIGNER
|
|
66
|
+
SIGNERS__TRANSACTION_CANNOT_HAVE_MULTIPLE_SENDING_SIGNERS = :SOLANA_ERROR__SIGNERS__TRANSACTION_CANNOT_HAVE_MULTIPLE_SENDING_SIGNERS
|
|
67
|
+
SIGNERS__WALLET_MULTISIGN_UNIMPLEMENTED = :SOLANA_ERROR__SIGNERS__WALLET_MULTISIGN_UNIMPLEMENTED
|
|
68
|
+
|
|
69
|
+
# ── Codecs ────────────────────────────────────────────────────────────────
|
|
70
|
+
CODECS__CANNOT_DECODE_EMPTY_BYTE_ARRAY = :SOLANA_ERROR__CODECS__CANNOT_DECODE_EMPTY_BYTE_ARRAY
|
|
71
|
+
CODECS__EXPECTED_POSITIVE_BYTE_LENGTH = :SOLANA_ERROR__CODECS__EXPECTED_POSITIVE_BYTE_LENGTH
|
|
72
|
+
CODECS__ENCODER_DECODER_FIXED_SIZE_MISMATCH = :SOLANA_ERROR__CODECS__ENCODER_DECODER_FIXED_SIZE_MISMATCH
|
|
73
|
+
CODECS__ENCODER_DECODER_MAX_SIZE_MISMATCH = :SOLANA_ERROR__CODECS__ENCODER_DECODER_MAX_SIZE_MISMATCH
|
|
74
|
+
CODECS__INVALID_NUMBER_OF_ITEMS = :SOLANA_ERROR__CODECS__INVALID_NUMBER_OF_ITEMS
|
|
75
|
+
CODECS__ENUM_DISCRIMINATOR_OUT_OF_RANGE = :SOLANA_ERROR__CODECS__ENUM_DISCRIMINATOR_OUT_OF_RANGE
|
|
76
|
+
CODECS__UNION_VARIANT_OUT_OF_RANGE = :SOLANA_ERROR__CODECS__UNION_VARIANT_OUT_OF_RANGE
|
|
77
|
+
CODECS__OFFSET_OUT_OF_RANGE = :SOLANA_ERROR__CODECS__OFFSET_OUT_OF_RANGE
|
|
78
|
+
CODECS__SENTINEL_MISSING_IN_DECODED_BYTES = :SOLANA_ERROR__CODECS__SENTINEL_MISSING_IN_DECODED_BYTES
|
|
79
|
+
CODECS__INVALID_STRING_FOR_BASE58_CODEC = :SOLANA_ERROR__CODECS__INVALID_STRING_FOR_BASE58_CODEC
|
|
80
|
+
CODECS__INVALID_STRING_FOR_BASE64_CODEC = :SOLANA_ERROR__CODECS__INVALID_STRING_FOR_BASE64_CODEC
|
|
81
|
+
CODECS__INVALID_STRING_FOR_HEX_CODEC = :SOLANA_ERROR__CODECS__INVALID_STRING_FOR_HEX_CODEC
|
|
82
|
+
CODECS__INVALID_BYTE_LENGTH = :SOLANA_ERROR__CODECS__INVALID_BYTE_LENGTH
|
|
83
|
+
CODECS__EXPECTED_ZERO_VALUE_TO_MATCH_ITEM_FIXED_DISCRIMINATOR = :SOLANA_ERROR__CODECS__EXPECTED_ZERO_VALUE_TO_MATCH_ITEM_FIXED_DISCRIMINATOR
|
|
84
|
+
CODECS__FIXED_NULLABLE_CANNOT_WRAP_VARIABLE_SIZE_CODEC = :SOLANA_ERROR__CODECS__FIXED_NULLABLE_CANNOT_WRAP_VARIABLE_SIZE_CODEC
|
|
85
|
+
|
|
86
|
+
# ── RPC / JSON-RPC ────────────────────────────────────────────────────────
|
|
87
|
+
RPC__INTEGER_OVERFLOW_WHILE_SERIALIZING_LARGE_INTEGER = :SOLANA_ERROR__RPC__INTEGER_OVERFLOW_WHILE_SERIALIZING_LARGE_INTEGER
|
|
88
|
+
RPC__INTEGER_OVERFLOW_WHILE_DESERIALIZING_LARGE_INTEGER = :SOLANA_ERROR__RPC__INTEGER_OVERFLOW_WHILE_DESERIALIZING_LARGE_INTEGER
|
|
89
|
+
RPC__TRANSPORT_HTTP_ERROR = :SOLANA_ERROR__RPC__TRANSPORT_HTTP_ERROR
|
|
90
|
+
RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST = :SOLANA_ERROR__RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST
|
|
91
|
+
RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID = :SOLANA_ERROR__RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID
|
|
92
|
+
RPC_SUBSCRIPTIONS__CHANNEL_CLOSED_BEFORE_MESSAGE_BUFFERED = :SOLANA_ERROR__RPC_SUBSCRIPTIONS__CHANNEL_CLOSED_BEFORE_MESSAGE_BUFFERED
|
|
93
|
+
RPC_SUBSCRIPTIONS__CHANNEL_CONNECTION_CLOSED = :SOLANA_ERROR__RPC_SUBSCRIPTIONS__CHANNEL_CONNECTION_CLOSED
|
|
94
|
+
RPC_SUBSCRIPTIONS__WEBSOCKET_CONNECTION_FAILED = :SOLANA_ERROR__RPC_SUBSCRIPTIONS__WEBSOCKET_CONNECTION_FAILED
|
|
95
|
+
|
|
96
|
+
# ── Offchain messages ─────────────────────────────────────────────────────
|
|
97
|
+
OFFCHAIN_MESSAGES__FAILED_TO_DECODE_MESSAGE = :SOLANA_ERROR__OFFCHAIN_MESSAGES__FAILED_TO_DECODE_MESSAGE
|
|
98
|
+
OFFCHAIN_MESSAGES__INVALID_MESSAGE_FORMAT = :SOLANA_ERROR__OFFCHAIN_MESSAGES__INVALID_MESSAGE_FORMAT
|
|
99
|
+
OFFCHAIN_MESSAGES__INVALID_MESSAGE_VERSION = :SOLANA_ERROR__OFFCHAIN_MESSAGES__INVALID_MESSAGE_VERSION
|
|
100
|
+
OFFCHAIN_MESSAGES__NON_PRINTABLE_ASCII_CHARACTER = :SOLANA_ERROR__OFFCHAIN_MESSAGES__NON_PRINTABLE_ASCII_CHARACTER
|
|
101
|
+
OFFCHAIN_MESSAGES__MESSAGE_TOO_LONG = :SOLANA_ERROR__OFFCHAIN_MESSAGES__MESSAGE_TOO_LONG
|
|
102
|
+
OFFCHAIN_MESSAGES__LEADING_ZERO_IN_SIGNING_DOMAIN = :SOLANA_ERROR__OFFCHAIN_MESSAGES__LEADING_ZERO_IN_SIGNING_DOMAIN
|
|
103
|
+
|
|
104
|
+
# ── Invariant violations (internal) ──────────────────────────────────────
|
|
105
|
+
INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING = :SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING
|
|
106
|
+
INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE = :SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE
|
|
107
|
+
|
|
108
|
+
ERROR_MESSAGES = T.let(
|
|
109
|
+
{
|
|
110
|
+
# General
|
|
111
|
+
INVALID_KEYPAIR_SEED_LENGTH => 'Keypair seed must be 32 bytes, got %{actual_length}',
|
|
112
|
+
INVALID_NONCE => 'The supplied nonce is not a valid nonce',
|
|
113
|
+
|
|
114
|
+
# Addresses
|
|
115
|
+
ADDRESSES__INVALID_BASE58_ENCODED_ADDRESS => 'Not a valid base58-encoded address',
|
|
116
|
+
ADDRESSES__INVALID_BYTE_LENGTH_FOR_ADDRESS => 'Expected 32 bytes for an address, got %{byte_length}',
|
|
117
|
+
ADDRESSES__STRING_LENGTH_OUT_OF_RANGE => 'Address string length out of range (32–44 chars), got %{actual_length}',
|
|
118
|
+
ADDRESSES__INVALID_ED25519_PUBLIC_KEY => 'The public key is not a valid Ed25519 public key',
|
|
119
|
+
ADDRESSES__SEEDS_POINT_ON_CURVE => 'The seeds resolve to a point on the Ed25519 curve; it cannot be used as a PDA',
|
|
120
|
+
ADDRESSES__MAX_SEED_LENGTH_EXCEEDED => 'A seed exceeds the maximum allowed length of 32 bytes, got %{actual_length}',
|
|
121
|
+
ADDRESSES__TOO_MANY_SEEDS => 'Too many seeds provided (max 16)',
|
|
122
|
+
ADDRESSES__FAILED_TO_FIND_VIABLE_PDA_BUMP_SEED => 'Could not find a viable bump seed for the given program and seeds',
|
|
123
|
+
ADDRESSES__INVALID_SEEDS_POINT_ON_CURVE => 'The seeds result in a public key that lies on the Ed25519 curve',
|
|
124
|
+
ADDRESSES__PDA_BUMP_SEED_OUT_OF_RANGE => 'Bump seed must be in range [0, 255]',
|
|
125
|
+
|
|
126
|
+
# Accounts
|
|
127
|
+
ACCOUNTS__ACCOUNT_NOT_FOUND => 'Account not found at address %{address}',
|
|
128
|
+
ACCOUNTS__ONE_OR_MORE_ACCOUNTS_NOT_FOUND => 'One or more accounts were not found',
|
|
129
|
+
ACCOUNTS__EXPECTED_DECODED_ACCOUNT => 'Expected account at address %{address} to be decoded',
|
|
130
|
+
ACCOUNTS__EXPECTED_ALL_ACCOUNTS_TO_BE_DECODED => 'Expected all %{count} account(s) to be decoded',
|
|
131
|
+
ACCOUNTS__FAILED_TO_DECODE_ACCOUNT => 'Failed to decode account data at address %{address}',
|
|
132
|
+
|
|
133
|
+
# Keys
|
|
134
|
+
KEYS__INVALID_KEY_PAIR_BYTE_LENGTH => 'Key pair byte array must be 64 bytes, got %{byte_length}',
|
|
135
|
+
KEYS__PUBLIC_KEY_MUST_MATCH_PRIVATE_KEY => 'The public key does not match the private key',
|
|
136
|
+
KEYS__INVALID_SIGNATURE_BYTE_LENGTH => 'Signature must be 64 bytes, got %{actual_length}',
|
|
137
|
+
KEYS__SIGNATURE_STRING_LENGTH_OUT_OF_RANGE => 'Signature string length out of range (64–88 chars), got %{actual_length}',
|
|
138
|
+
|
|
139
|
+
# Instructions
|
|
140
|
+
INSTRUCTIONS__EXPECTED_TO_HAVE_ACCOUNTS => 'Expected instruction to have accounts',
|
|
141
|
+
INSTRUCTIONS__EXPECTED_TO_HAVE_DATA => 'Expected instruction to have data',
|
|
142
|
+
INSTRUCTIONS__PROGRAM_ADDRESS_MISMATCH => 'Instruction program address mismatch: expected %{expected}, got %{actual}',
|
|
143
|
+
INSTRUCTIONS__ACCOUNT_NOT_FOUND => 'Account at index %{index} not found in instruction',
|
|
144
|
+
INSTRUCTIONS__EXPECTED_ALL_ACCOUNTS_TO_BE_DECODED => 'Expected all instruction accounts to be decoded',
|
|
145
|
+
|
|
146
|
+
# Transactions
|
|
147
|
+
TRANSACTIONS__TRANSACTION_NOT_SIGNABLE => 'Transaction is not signable (missing fee payer or lifetime constraint)',
|
|
148
|
+
TRANSACTIONS__MISSING_SIGNER => 'Transaction is missing a required signer: %{address}',
|
|
149
|
+
TRANSACTIONS__VERSION_NUMBER_OUT_OF_RANGE => 'Transaction version %{version} is out of range',
|
|
150
|
+
TRANSACTIONS__FAILED_TO_DECOMPILE_ADDRESS_LOOKUP_TABLE_CONTENTS => 'Failed to decompile address lookup table contents',
|
|
151
|
+
TRANSACTIONS__FAILED_TO_DECOMPILE_FEE_PAYER_MISSING => 'Failed to decompile transaction: fee payer is missing',
|
|
152
|
+
TRANSACTIONS__FAILED_TO_DECOMPILE_INSTRUCTION_PROGRAM_ADDRESS_NOT_FOUND => 'Failed to decompile instruction: program address not found',
|
|
153
|
+
TRANSACTIONS__SEND_TRANSACTION_PREFLIGHT_FAILURE => 'Transaction simulation failed: %{message}',
|
|
154
|
+
TRANSACTIONS__BLOCKHASH_NOT_FOUND => 'Blockhash not found',
|
|
155
|
+
TRANSACTIONS__FAILED_TRANSACTION_PLAN => 'Failed to execute transaction plan',
|
|
156
|
+
TRANSACTIONS__TRANSACTION_EXPIRED_BLOCKHEIGHT_EXCEEDED => 'Transaction expired: block height exceeded',
|
|
157
|
+
TRANSACTIONS__TRANSACTION_EXPIRED_NONCE_INVALID => 'Transaction expired: nonce is no longer valid',
|
|
158
|
+
TRANSACTIONS__TRANSACTION_CONFIRMATION_TIMEOUT => 'Timed out waiting for transaction %{signature} to confirm',
|
|
159
|
+
|
|
160
|
+
# Signers
|
|
161
|
+
SIGNERS__ADDRESS_CANNOT_HAVE_MULTIPLE_SIGNERS => 'Address %{address} cannot be assigned multiple signers',
|
|
162
|
+
SIGNERS__EXPECTED_MESSAGE_MODIFYING_SIGNER => 'Expected signer to be a message modifying signer',
|
|
163
|
+
SIGNERS__EXPECTED_TRANSACTION_MODIFYING_SIGNER => 'Expected signer to be a transaction modifying signer',
|
|
164
|
+
SIGNERS__EXPECTED_TRANSACTION_SENDING_SIGNER => 'Expected signer to be a transaction sending signer',
|
|
165
|
+
SIGNERS__TRANSACTION_CANNOT_HAVE_MULTIPLE_SENDING_SIGNERS => 'Transaction cannot have multiple sending signers',
|
|
166
|
+
SIGNERS__WALLET_MULTISIGN_UNIMPLEMENTED => 'Wallet multisign is not yet implemented',
|
|
167
|
+
|
|
168
|
+
# Codecs
|
|
169
|
+
CODECS__CANNOT_DECODE_EMPTY_BYTE_ARRAY => 'Cannot decode an empty byte array',
|
|
170
|
+
CODECS__EXPECTED_POSITIVE_BYTE_LENGTH => 'Expected a positive byte length, got %{byte_length}',
|
|
171
|
+
CODECS__ENCODER_DECODER_FIXED_SIZE_MISMATCH => 'Encoder fixed size (%{encoder_size}) does not match decoder fixed size (%{decoder_size})',
|
|
172
|
+
CODECS__ENCODER_DECODER_MAX_SIZE_MISMATCH => 'Encoder max size (%{encoder_max}) does not match decoder max size (%{decoder_max})',
|
|
173
|
+
CODECS__INVALID_NUMBER_OF_ITEMS => 'Expected %{expected} items but got %{actual}',
|
|
174
|
+
CODECS__ENUM_DISCRIMINATOR_OUT_OF_RANGE => 'Enum discriminator %{discriminator} is out of range [0, %{max}]',
|
|
175
|
+
CODECS__UNION_VARIANT_OUT_OF_RANGE => 'Union variant index %{index} is out of range',
|
|
176
|
+
CODECS__OFFSET_OUT_OF_RANGE => 'Codec offset %{offset} is out of range for byte array of length %{byte_length}',
|
|
177
|
+
CODECS__SENTINEL_MISSING_IN_DECODED_BYTES => 'Sentinel bytes not found in decoded data',
|
|
178
|
+
CODECS__INVALID_STRING_FOR_BASE58_CODEC => 'Invalid base58 string: %{value}',
|
|
179
|
+
CODECS__INVALID_STRING_FOR_BASE64_CODEC => 'Invalid base64 string: %{value}',
|
|
180
|
+
CODECS__INVALID_STRING_FOR_HEX_CODEC => 'Invalid hex string: %{value}',
|
|
181
|
+
CODECS__INVALID_BYTE_LENGTH => 'Expected %{expected} bytes but got %{actual}',
|
|
182
|
+
CODECS__EXPECTED_ZERO_VALUE_TO_MATCH_ITEM_FIXED_DISCRIMINATOR => 'Expected zero value to match fixed discriminator',
|
|
183
|
+
CODECS__FIXED_NULLABLE_CANNOT_WRAP_VARIABLE_SIZE_CODEC => 'A fixed-size nullable codec cannot wrap a variable-size codec',
|
|
184
|
+
|
|
185
|
+
# RPC
|
|
186
|
+
RPC__INTEGER_OVERFLOW_WHILE_SERIALIZING_LARGE_INTEGER => 'Integer overflow while serializing large integer %{value}',
|
|
187
|
+
RPC__INTEGER_OVERFLOW_WHILE_DESERIALIZING_LARGE_INTEGER => 'Integer overflow while deserializing large integer %{value}',
|
|
188
|
+
RPC__TRANSPORT_HTTP_ERROR => 'HTTP transport error: %{status} %{message}',
|
|
189
|
+
RPC_SUBSCRIPTIONS__CANNOT_CREATE_SUBSCRIPTION_REQUEST => 'Cannot create subscription request',
|
|
190
|
+
RPC_SUBSCRIPTIONS__EXPECTED_SERVER_SUBSCRIPTION_ID => 'Expected server to return a subscription ID',
|
|
191
|
+
RPC_SUBSCRIPTIONS__CHANNEL_CLOSED_BEFORE_MESSAGE_BUFFERED => 'WebSocket channel closed before message could be buffered',
|
|
192
|
+
RPC_SUBSCRIPTIONS__CHANNEL_CONNECTION_CLOSED => 'WebSocket connection closed: %{reason}',
|
|
193
|
+
RPC_SUBSCRIPTIONS__WEBSOCKET_CONNECTION_FAILED => 'Failed to establish WebSocket connection to %{url}',
|
|
194
|
+
|
|
195
|
+
# Offchain messages
|
|
196
|
+
OFFCHAIN_MESSAGES__FAILED_TO_DECODE_MESSAGE => 'Failed to decode offchain message',
|
|
197
|
+
OFFCHAIN_MESSAGES__INVALID_MESSAGE_FORMAT => 'Invalid offchain message format',
|
|
198
|
+
OFFCHAIN_MESSAGES__INVALID_MESSAGE_VERSION => 'Invalid offchain message version %{version}',
|
|
199
|
+
OFFCHAIN_MESSAGES__NON_PRINTABLE_ASCII_CHARACTER => 'Offchain message v0 contains non-printable ASCII character at index %{index}',
|
|
200
|
+
OFFCHAIN_MESSAGES__MESSAGE_TOO_LONG => 'Offchain message is too long (%{length} bytes, max %{max})',
|
|
201
|
+
OFFCHAIN_MESSAGES__LEADING_ZERO_IN_SIGNING_DOMAIN => 'Offchain message signing domain must not start with a null byte',
|
|
202
|
+
|
|
203
|
+
# Invariant violations
|
|
204
|
+
INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING => 'Subscription iterator state is missing (internal error)',
|
|
205
|
+
INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE => 'Subscription iterator must not poll before resolving existing message (internal error)',
|
|
206
|
+
}.freeze,
|
|
207
|
+
T::Hash[Symbol, String]
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
sig { returns(Symbol) }
|
|
211
|
+
attr_reader :code
|
|
212
|
+
|
|
213
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
214
|
+
attr_reader :context
|
|
215
|
+
|
|
216
|
+
sig { params(code: Symbol, context: T::Hash[Symbol, T.untyped]).void }
|
|
217
|
+
def initialize(code, context = {})
|
|
218
|
+
@code = T.let(code, Symbol)
|
|
219
|
+
@context = T.let(context, T::Hash[Symbol, T.untyped])
|
|
220
|
+
|
|
221
|
+
template = ERROR_MESSAGES[code] || code.to_s
|
|
222
|
+
message = context.empty? ? template : (template % context rescue "#{template} #{context}")
|
|
223
|
+
super(message)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Mirrors @solana/fast-stable-stringify.
|
|
5
|
+
# Produces deterministic JSON by sorting Hash keys recursively.
|
|
6
|
+
# Used internally by RPC transport so request bodies are stable.
|
|
7
|
+
module Solana::Ruby::Kit
|
|
8
|
+
module FastStableStringify
|
|
9
|
+
extend T::Sig
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# Serialize +value+ to a deterministic JSON string.
|
|
14
|
+
# Hash keys are sorted lexicographically at every level.
|
|
15
|
+
# Unsupported types (custom objects, Symbols as values, etc.) are treated
|
|
16
|
+
# as +nil+ (same behaviour as JSON.generate's default replacer).
|
|
17
|
+
sig { params(value: T.untyped).returns(String) }
|
|
18
|
+
def stringify(value)
|
|
19
|
+
serialize(value)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Internal recursive serializer (exposed as module_function for testability).
|
|
23
|
+
sig { params(value: T.untyped).returns(String) }
|
|
24
|
+
def serialize(value) # rubocop:disable Metrics/MethodLength
|
|
25
|
+
case value
|
|
26
|
+
when NilClass then 'null'
|
|
27
|
+
when TrueClass then 'true'
|
|
28
|
+
when FalseClass then 'false'
|
|
29
|
+
when Integer
|
|
30
|
+
# Sorbet: plain integer — output as-is
|
|
31
|
+
value.to_s
|
|
32
|
+
when Float
|
|
33
|
+
# Match JSON behaviour: NaN / Infinity → null
|
|
34
|
+
if value.nan? || value.infinite?
|
|
35
|
+
'null'
|
|
36
|
+
else
|
|
37
|
+
# Avoid trailing zeros while staying valid JSON
|
|
38
|
+
int = value.to_i
|
|
39
|
+
int.to_f == value ? int.to_s : value.to_s
|
|
40
|
+
end
|
|
41
|
+
when String
|
|
42
|
+
value.to_json
|
|
43
|
+
when Array
|
|
44
|
+
inner = value.map { |el| serialize(el) }.join(',')
|
|
45
|
+
"[#{inner}]"
|
|
46
|
+
when Hash
|
|
47
|
+
pairs = value.keys.map(&:to_s).sort.filter_map do |k|
|
|
48
|
+
raw_val = value[k] || value[k.to_sym]
|
|
49
|
+
serialized = serialize(raw_val)
|
|
50
|
+
next if serialized == 'undefined' # skip undefined-equivalent keys
|
|
51
|
+
|
|
52
|
+
"#{k.to_s.to_json}:#{serialized}"
|
|
53
|
+
end
|
|
54
|
+
"{#{pairs.join(',')}}"
|
|
55
|
+
else
|
|
56
|
+
# Symbol, custom class, etc. → null
|
|
57
|
+
'null'
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
private_class_method :serialize
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
# Function composition utilities.
|
|
6
|
+
# Mirrors the TypeScript package @solana/functional.
|
|
7
|
+
module Functional
|
|
8
|
+
extend T::Sig
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
# Passes an initial value through a series of single-argument callables,
|
|
12
|
+
# returning the final result.
|
|
13
|
+
#
|
|
14
|
+
# Mirrors TypeScript's `pipe(init, fn1, fn2, ...)`. TypeScript requires
|
|
15
|
+
# 10 overloads for static typing; Ruby achieves the same with a single
|
|
16
|
+
# variadic implementation.
|
|
17
|
+
#
|
|
18
|
+
# @example Building a transaction message
|
|
19
|
+
# msg = Solana::Ruby::Kit::Functional.pipe(
|
|
20
|
+
# Solana::Ruby::Kit::TransactionMessages.create_transaction_message(version: 0),
|
|
21
|
+
# ->(tx) { Solana::Ruby::Kit::TransactionMessages.set_fee_payer(fee_payer_address, tx) },
|
|
22
|
+
# ->(tx) { Solana::Ruby::Kit::TransactionMessages.set_blockhash_lifetime(blockhash, tx) },
|
|
23
|
+
# )
|
|
24
|
+
sig { params(value: T.untyped, fns: T::Array[T.proc.params(arg0: T.untyped).returns(T.untyped)]).returns(T.untyped) }
|
|
25
|
+
def pipe(value, *fns)
|
|
26
|
+
fns.reduce(value) { |acc, fn| T.unsafe(fn).call(acc) }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
module InstructionPlans
|
|
6
|
+
# A plan that wraps a single instruction.
|
|
7
|
+
class SingleInstructionPlan < T::Struct
|
|
8
|
+
const :instruction, Instructions::Instruction
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# A plan that executes a sequence of sub-plans in order.
|
|
12
|
+
# When +divisible+ is true the planner may split steps across transactions.
|
|
13
|
+
class SequentialInstructionPlan < T::Struct
|
|
14
|
+
const :steps, T::Array[T.untyped] # Array[InstructionPlan]
|
|
15
|
+
const :divisible, T::Boolean, default: false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# A plan whose sub-plans may execute concurrently or be packed into the
|
|
19
|
+
# same transaction in any order.
|
|
20
|
+
class ParallelInstructionPlan < T::Struct
|
|
21
|
+
const :plans, T::Array[T.untyped] # Array[InstructionPlan]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Union type alias (used only in doc strings; Ruby is duck-typed).
|
|
25
|
+
# InstructionPlan = SingleInstructionPlan | SequentialInstructionPlan | ParallelInstructionPlan
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Mirrors @solana/instruction-plans.
|
|
5
|
+
# An InstructionPlan describes operations that may span multiple transactions.
|
|
6
|
+
require_relative 'instruction_plans/plans'
|
|
7
|
+
|
|
8
|
+
module Solana::Ruby::Kit
|
|
9
|
+
module InstructionPlans
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# Build a plan that wraps a single instruction.
|
|
15
|
+
sig { params(instruction: Instructions::Instruction).returns(SingleInstructionPlan) }
|
|
16
|
+
def single_instruction_plan(instruction)
|
|
17
|
+
SingleInstructionPlan.new(instruction: instruction)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Build a sequential plan from an array of sub-plans.
|
|
21
|
+
sig { params(steps: T::Array[T.untyped], divisible: T::Boolean).returns(SequentialInstructionPlan) }
|
|
22
|
+
def sequential_instruction_plan(steps, divisible: false)
|
|
23
|
+
SequentialInstructionPlan.new(steps: steps, divisible: divisible)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Build a parallel plan from an array of sub-plans.
|
|
27
|
+
sig { params(plans: T::Array[T.untyped]).returns(ParallelInstructionPlan) }
|
|
28
|
+
def parallel_instruction_plan(plans)
|
|
29
|
+
ParallelInstructionPlan.new(plans: plans)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Flatten a plan tree into a single ordered Array of Instructions.
|
|
33
|
+
sig { params(plan: T.untyped).returns(T::Array[Instructions::Instruction]) }
|
|
34
|
+
def flatten_instruction_plan(plan)
|
|
35
|
+
case plan
|
|
36
|
+
when SingleInstructionPlan
|
|
37
|
+
[plan.instruction]
|
|
38
|
+
when SequentialInstructionPlan
|
|
39
|
+
plan.steps.flat_map { |s| flatten_instruction_plan(s) }
|
|
40
|
+
when ParallelInstructionPlan
|
|
41
|
+
plan.plans.flat_map { |p| flatten_instruction_plan(p) }
|
|
42
|
+
else
|
|
43
|
+
Kernel.raise ArgumentError, "Unknown InstructionPlan type: #{plan.class}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|