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,56 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'base64'
|
|
5
|
+
|
|
6
|
+
module Solana::Ruby::Kit
|
|
7
|
+
module Rpc
|
|
8
|
+
module Api
|
|
9
|
+
# Fetch multiple account infos in a single RPC call.
|
|
10
|
+
# Mirrors TypeScript's GetMultipleAccountsApi.getMultipleAccounts.
|
|
11
|
+
module GetMultipleAccounts
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
sig do
|
|
15
|
+
params(
|
|
16
|
+
addresses: T::Array[String],
|
|
17
|
+
encoding: String,
|
|
18
|
+
commitment: T.nilable(Symbol),
|
|
19
|
+
min_context_slot: T.nilable(Integer),
|
|
20
|
+
data_slice: T.nilable(T::Hash[String, Integer])
|
|
21
|
+
).returns(RpcTypes::RpcContextualValue)
|
|
22
|
+
end
|
|
23
|
+
def get_multiple_accounts(
|
|
24
|
+
addresses,
|
|
25
|
+
encoding: 'base64',
|
|
26
|
+
commitment: nil,
|
|
27
|
+
min_context_slot: nil,
|
|
28
|
+
data_slice: nil
|
|
29
|
+
)
|
|
30
|
+
config = { 'encoding' => encoding }
|
|
31
|
+
config['commitment'] = commitment.to_s if commitment
|
|
32
|
+
config['minContextSlot'] = min_context_slot if min_context_slot
|
|
33
|
+
config['dataSlice'] = data_slice if data_slice
|
|
34
|
+
|
|
35
|
+
result = transport.request('getMultipleAccounts', [addresses, config])
|
|
36
|
+
|
|
37
|
+
slot = Kernel.Integer(result['context']['slot'])
|
|
38
|
+
values = Kernel.Array(result['value']).map do |raw|
|
|
39
|
+
next nil if raw.nil?
|
|
40
|
+
|
|
41
|
+
RpcTypes::AccountInfoWithBase64Data.new(
|
|
42
|
+
executable: raw['executable'],
|
|
43
|
+
lamports: Kernel.Integer(raw['lamports']),
|
|
44
|
+
owner: raw['owner'],
|
|
45
|
+
space: Kernel.Integer(raw.fetch('space', 0)),
|
|
46
|
+
rent_epoch: Kernel.Integer(raw.fetch('rentEpoch', 0)),
|
|
47
|
+
data: Kernel.Array(raw['data'])
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
RpcTypes::RpcContextualValue.new(slot: slot, value: values)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
module Rpc
|
|
6
|
+
module Api
|
|
7
|
+
# Fetch all accounts owned by a program.
|
|
8
|
+
# Mirrors TypeScript's GetProgramAccountsApi.getProgramAccounts.
|
|
9
|
+
#
|
|
10
|
+
# Returns an Array of { pubkey:, account: AccountInfoWithBase64Data }.
|
|
11
|
+
module GetProgramAccounts
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
sig do
|
|
15
|
+
params(
|
|
16
|
+
program_id: String,
|
|
17
|
+
encoding: String,
|
|
18
|
+
filters: T::Array[T::Hash[String, T.untyped]],
|
|
19
|
+
commitment: T.nilable(Symbol),
|
|
20
|
+
min_context_slot: T.nilable(Integer),
|
|
21
|
+
with_context: T::Boolean
|
|
22
|
+
).returns(T.untyped)
|
|
23
|
+
end
|
|
24
|
+
def get_program_accounts(
|
|
25
|
+
program_id,
|
|
26
|
+
encoding: 'base64',
|
|
27
|
+
filters: [],
|
|
28
|
+
commitment: nil,
|
|
29
|
+
min_context_slot: nil,
|
|
30
|
+
with_context: false
|
|
31
|
+
)
|
|
32
|
+
config = { 'encoding' => encoding, 'withContext' => with_context }
|
|
33
|
+
config['filters'] = filters unless filters.empty?
|
|
34
|
+
config['commitment'] = commitment.to_s if commitment
|
|
35
|
+
config['minContextSlot'] = min_context_slot if min_context_slot
|
|
36
|
+
|
|
37
|
+
result = transport.request('getProgramAccounts', [program_id, config])
|
|
38
|
+
|
|
39
|
+
# withContext wraps result in {context:, value:}
|
|
40
|
+
accounts = with_context ? result['value'] : result
|
|
41
|
+
|
|
42
|
+
Kernel.Array(accounts).map do |item|
|
|
43
|
+
account_raw = item['account']
|
|
44
|
+
{
|
|
45
|
+
pubkey: item['pubkey'],
|
|
46
|
+
account: RpcTypes::AccountInfoWithBase64Data.new(
|
|
47
|
+
executable: account_raw['executable'],
|
|
48
|
+
lamports: Kernel.Integer(account_raw['lamports']),
|
|
49
|
+
owner: account_raw['owner'],
|
|
50
|
+
space: Kernel.Integer(account_raw.fetch('space', 0)),
|
|
51
|
+
rent_epoch: Kernel.Integer(account_raw.fetch('rentEpoch', 0)),
|
|
52
|
+
data: Kernel.Array(account_raw['data'])
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../../rpc_types/account_info'
|
|
5
|
+
|
|
6
|
+
module Solana::Ruby::Kit
|
|
7
|
+
module Rpc
|
|
8
|
+
module Api
|
|
9
|
+
# Status of a confirmed transaction signature.
|
|
10
|
+
# Mirrors TypeScript's signature-status response shape.
|
|
11
|
+
class SignatureStatus < T::Struct
|
|
12
|
+
const :slot, Integer
|
|
13
|
+
const :confirmations, T.nilable(Integer) # nil when finalized
|
|
14
|
+
const :err, T.untyped # nil or error object
|
|
15
|
+
const :confirmation_status, T.nilable(Symbol) # :processed | :confirmed | :finalized
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Returns the confirmation status of one or more transaction signatures.
|
|
19
|
+
# Mirrors TypeScript's `GetSignatureStatusesApi.getSignatureStatuses(sigs, config?)`.
|
|
20
|
+
#
|
|
21
|
+
# Returns a `RpcContextualValue` with:
|
|
22
|
+
# .slot — context slot
|
|
23
|
+
# .value — Array of SignatureStatus | nil (nil for unknown signatures)
|
|
24
|
+
module GetSignatureStatuses
|
|
25
|
+
extend T::Sig
|
|
26
|
+
|
|
27
|
+
sig do
|
|
28
|
+
params(
|
|
29
|
+
signatures: T::Array[String],
|
|
30
|
+
search_transaction_history: T::Boolean
|
|
31
|
+
).returns(RpcTypes::RpcContextualValue)
|
|
32
|
+
end
|
|
33
|
+
def get_signature_statuses(signatures, search_transaction_history: false)
|
|
34
|
+
config = { 'searchTransactionHistory' => search_transaction_history }
|
|
35
|
+
result = transport.request('getSignatureStatuses', [signatures, config])
|
|
36
|
+
|
|
37
|
+
statuses = result['value'].map do |raw|
|
|
38
|
+
next nil if raw.nil?
|
|
39
|
+
|
|
40
|
+
SignatureStatus.new(
|
|
41
|
+
slot: Kernel.Integer(raw['slot']),
|
|
42
|
+
confirmations: raw['confirmations'] ? Kernel.Integer(raw['confirmations']) : nil,
|
|
43
|
+
err: raw['err'],
|
|
44
|
+
confirmation_status: raw['confirmationStatus']&.to_sym
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
RpcTypes::RpcContextualValue.new(
|
|
49
|
+
slot: Kernel.Integer(result['context']['slot']),
|
|
50
|
+
value: statuses
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
module Rpc
|
|
6
|
+
module Api
|
|
7
|
+
# Returns the current slot number at the given commitment level.
|
|
8
|
+
# Mirrors TypeScript's `GetSlotApi.getSlot()`.
|
|
9
|
+
module GetSlot
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig do
|
|
13
|
+
params(
|
|
14
|
+
commitment: T.nilable(Symbol),
|
|
15
|
+
min_context_slot: T.nilable(Integer)
|
|
16
|
+
).returns(Integer)
|
|
17
|
+
end
|
|
18
|
+
def get_slot(commitment: nil, min_context_slot: nil)
|
|
19
|
+
config = {}
|
|
20
|
+
config['commitment'] = commitment.to_s if commitment
|
|
21
|
+
config['minContextSlot'] = min_context_slot if min_context_slot
|
|
22
|
+
|
|
23
|
+
params = config.empty? ? [] : [config]
|
|
24
|
+
result = transport.request('getSlot', params)
|
|
25
|
+
Kernel.Integer(result)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
module Rpc
|
|
6
|
+
module Api
|
|
7
|
+
# Fetch the token balance of an SPL Token account.
|
|
8
|
+
# Mirrors TypeScript's GetTokenAccountBalanceApi.getTokenAccountBalance.
|
|
9
|
+
module GetTokenAccountBalance
|
|
10
|
+
extend T::Sig
|
|
11
|
+
|
|
12
|
+
sig do
|
|
13
|
+
params(
|
|
14
|
+
token_account: String,
|
|
15
|
+
commitment: T.nilable(Symbol)
|
|
16
|
+
).returns(RpcTypes::RpcContextualValue)
|
|
17
|
+
end
|
|
18
|
+
def get_token_account_balance(token_account, commitment: nil)
|
|
19
|
+
config = {}
|
|
20
|
+
config['commitment'] = commitment.to_s if commitment
|
|
21
|
+
|
|
22
|
+
result = transport.request('getTokenAccountBalance', [token_account, config].tap { |a| a.pop if a.last.empty? })
|
|
23
|
+
slot = Kernel.Integer(result['context']['slot'])
|
|
24
|
+
raw = result['value']
|
|
25
|
+
|
|
26
|
+
value = {
|
|
27
|
+
amount: raw['amount'],
|
|
28
|
+
decimals: Kernel.Integer(raw['decimals']),
|
|
29
|
+
ui_amount: raw['uiAmount'],
|
|
30
|
+
ui_amount_string: raw['uiAmountString']
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
RpcTypes::RpcContextualValue.new(slot: slot, value: value)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
module Rpc
|
|
6
|
+
module Api
|
|
7
|
+
# Fetch all SPL Token accounts by owner.
|
|
8
|
+
# +filter+ must be one of:
|
|
9
|
+
# { 'mint' => mint_address }
|
|
10
|
+
# { 'programId' => program_id }
|
|
11
|
+
module GetTokenAccountsByOwner
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
sig do
|
|
15
|
+
params(
|
|
16
|
+
owner: String,
|
|
17
|
+
filter: T::Hash[String, String],
|
|
18
|
+
encoding: String,
|
|
19
|
+
commitment: T.nilable(Symbol)
|
|
20
|
+
).returns(RpcTypes::RpcContextualValue)
|
|
21
|
+
end
|
|
22
|
+
def get_token_accounts_by_owner(owner, filter, encoding: 'base64', commitment: nil)
|
|
23
|
+
config = { 'encoding' => encoding }
|
|
24
|
+
config['commitment'] = commitment.to_s if commitment
|
|
25
|
+
|
|
26
|
+
result = transport.request('getTokenAccountsByOwner', [owner, filter, config])
|
|
27
|
+
slot = Kernel.Integer(result['context']['slot'])
|
|
28
|
+
value = Kernel.Array(result['value']).map do |item|
|
|
29
|
+
raw = item['account']
|
|
30
|
+
{
|
|
31
|
+
pubkey: item['pubkey'],
|
|
32
|
+
account: RpcTypes::AccountInfoWithBase64Data.new(
|
|
33
|
+
executable: raw['executable'],
|
|
34
|
+
lamports: Kernel.Integer(raw['lamports']),
|
|
35
|
+
owner: raw['owner'],
|
|
36
|
+
space: Kernel.Integer(raw.fetch('space', 0)),
|
|
37
|
+
rent_epoch: Kernel.Integer(raw.fetch('rentEpoch', 0)),
|
|
38
|
+
data: Kernel.Array(raw['data'])
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
RpcTypes::RpcContextualValue.new(slot: slot, value: value)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
module Rpc
|
|
6
|
+
module Api
|
|
7
|
+
# Fetch a confirmed transaction.
|
|
8
|
+
# Mirrors TypeScript's GetTransactionApi.getTransaction.
|
|
9
|
+
# Returns the raw JSON hash (nil if not found / not yet confirmed).
|
|
10
|
+
module GetTransaction
|
|
11
|
+
extend T::Sig
|
|
12
|
+
|
|
13
|
+
sig do
|
|
14
|
+
params(
|
|
15
|
+
signature: String,
|
|
16
|
+
encoding: String,
|
|
17
|
+
commitment: T.nilable(Symbol),
|
|
18
|
+
max_supported_transaction_version: T.nilable(Integer)
|
|
19
|
+
).returns(T.nilable(T::Hash[String, T.untyped]))
|
|
20
|
+
end
|
|
21
|
+
def get_transaction(
|
|
22
|
+
signature,
|
|
23
|
+
encoding: 'json',
|
|
24
|
+
commitment: nil,
|
|
25
|
+
max_supported_transaction_version: nil
|
|
26
|
+
)
|
|
27
|
+
config = { 'encoding' => encoding }
|
|
28
|
+
config['commitment'] = commitment.to_s if commitment
|
|
29
|
+
config['maxSupportedTransactionVersion'] = max_supported_transaction_version if max_supported_transaction_version
|
|
30
|
+
|
|
31
|
+
transport.request('getTransaction', [signature, config])
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
module Rpc
|
|
6
|
+
module Api
|
|
7
|
+
# Vote account info struct.
|
|
8
|
+
VoteAccountInfo = T.let(
|
|
9
|
+
Struct.new(
|
|
10
|
+
:vote_pubkey,
|
|
11
|
+
:node_pubkey,
|
|
12
|
+
:activated_stake,
|
|
13
|
+
:epoch_vote_account,
|
|
14
|
+
:commission,
|
|
15
|
+
:last_vote,
|
|
16
|
+
:epoch_credits,
|
|
17
|
+
:root_slot,
|
|
18
|
+
keyword_init: true
|
|
19
|
+
),
|
|
20
|
+
T.untyped
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Fetch current and delinquent vote accounts.
|
|
24
|
+
# Returns { current: [], delinquent: [] }.
|
|
25
|
+
module GetVoteAccounts
|
|
26
|
+
extend T::Sig
|
|
27
|
+
|
|
28
|
+
sig do
|
|
29
|
+
params(
|
|
30
|
+
commitment: T.nilable(Symbol),
|
|
31
|
+
vote_pubkey: T.nilable(String)
|
|
32
|
+
).returns(T::Hash[Symbol, T::Array[T.untyped]])
|
|
33
|
+
end
|
|
34
|
+
def get_vote_accounts(commitment: nil, vote_pubkey: nil)
|
|
35
|
+
config = {}
|
|
36
|
+
config['commitment'] = commitment.to_s if commitment
|
|
37
|
+
config['votePubkey'] = vote_pubkey if vote_pubkey
|
|
38
|
+
|
|
39
|
+
raw = transport.request('getVoteAccounts', config.empty? ? [] : [config])
|
|
40
|
+
|
|
41
|
+
parse_vote = Kernel.lambda do |v|
|
|
42
|
+
VoteAccountInfo.new(
|
|
43
|
+
vote_pubkey: v['votePubkey'],
|
|
44
|
+
node_pubkey: v['nodePubkey'],
|
|
45
|
+
activated_stake: Kernel.Integer(v['activatedStake']),
|
|
46
|
+
epoch_vote_account: v['epochVoteAccount'],
|
|
47
|
+
commission: Kernel.Integer(v['commission']),
|
|
48
|
+
last_vote: Kernel.Integer(v['lastVote']),
|
|
49
|
+
epoch_credits: v['epochCredits'],
|
|
50
|
+
root_slot: v['rootSlot'] ? Kernel.Integer(v['rootSlot']) : nil
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
{
|
|
55
|
+
current: Kernel.Array(raw['current']).map(&parse_vote),
|
|
56
|
+
delinquent: Kernel.Array(raw['delinquent']).map(&parse_vote)
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../../rpc_types/account_info'
|
|
5
|
+
|
|
6
|
+
module Solana::Ruby::Kit
|
|
7
|
+
module Rpc
|
|
8
|
+
module Api
|
|
9
|
+
# Returns whether a blockhash is still valid (not yet expired).
|
|
10
|
+
# Mirrors TypeScript's `IsBlockhashValidApi.isBlockhashValid(blockhash, config?)`.
|
|
11
|
+
#
|
|
12
|
+
# Returns a `RpcContextualValue` with:
|
|
13
|
+
# .slot — context slot
|
|
14
|
+
# .value — Boolean
|
|
15
|
+
module IsBlockhashValid
|
|
16
|
+
extend T::Sig
|
|
17
|
+
|
|
18
|
+
sig do
|
|
19
|
+
params(
|
|
20
|
+
blockhash: String,
|
|
21
|
+
commitment: T.nilable(Symbol),
|
|
22
|
+
min_context_slot: T.nilable(Integer)
|
|
23
|
+
).returns(RpcTypes::RpcContextualValue)
|
|
24
|
+
end
|
|
25
|
+
def is_blockhash_valid(blockhash, commitment: nil, min_context_slot: nil)
|
|
26
|
+
config = {}
|
|
27
|
+
config['commitment'] = commitment.to_s if commitment
|
|
28
|
+
config['minContextSlot'] = min_context_slot if min_context_slot
|
|
29
|
+
|
|
30
|
+
params = config.empty? ? [blockhash] : [blockhash, config]
|
|
31
|
+
result = transport.request('isBlockhashValid', params)
|
|
32
|
+
|
|
33
|
+
RpcTypes::RpcContextualValue.new(
|
|
34
|
+
slot: Kernel.Integer(result['context']['slot']),
|
|
35
|
+
value: result['value'] == true
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../../keys/signatures'
|
|
5
|
+
|
|
6
|
+
module Solana::Ruby::Kit
|
|
7
|
+
module Rpc
|
|
8
|
+
module Api
|
|
9
|
+
# Requests an airdrop of lamports to the given address.
|
|
10
|
+
# Only available on devnet and testnet.
|
|
11
|
+
# Mirrors TypeScript's `RequestAirdropApi.requestAirdrop(address, lamports, config?)`.
|
|
12
|
+
#
|
|
13
|
+
# Returns the transaction `Signature` of the airdrop.
|
|
14
|
+
module RequestAirdrop
|
|
15
|
+
extend T::Sig
|
|
16
|
+
|
|
17
|
+
sig do
|
|
18
|
+
params(
|
|
19
|
+
address: String,
|
|
20
|
+
lamports: Integer,
|
|
21
|
+
commitment: T.nilable(Symbol)
|
|
22
|
+
).returns(Keys::Signature)
|
|
23
|
+
end
|
|
24
|
+
def request_airdrop(address, lamports, commitment: nil)
|
|
25
|
+
config = {}
|
|
26
|
+
config['commitment'] = commitment.to_s if commitment
|
|
27
|
+
|
|
28
|
+
params = config.empty? ? [address, lamports] : [address, lamports, config]
|
|
29
|
+
sig_str = transport.request('requestAirdrop', params)
|
|
30
|
+
Keys::Signature.new(sig_str)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'base64'
|
|
5
|
+
require_relative '../../keys/signatures'
|
|
6
|
+
|
|
7
|
+
module Solana::Ruby::Kit
|
|
8
|
+
module Rpc
|
|
9
|
+
module Api
|
|
10
|
+
# Submits a signed transaction to the cluster for processing.
|
|
11
|
+
# Mirrors TypeScript's `SendTransactionApi.sendTransaction(transaction, config?)`.
|
|
12
|
+
#
|
|
13
|
+
# Accepts the transaction in two forms:
|
|
14
|
+
# - A `Solana::Ruby::Kit::Transactions::Transaction` struct (wire bytes are base64-encoded internally).
|
|
15
|
+
# - A raw base64 String (the already-encoded wire transaction).
|
|
16
|
+
#
|
|
17
|
+
# Returns a `Solana::Ruby::Kit::Keys::Signature` (base58-encoded transaction signature).
|
|
18
|
+
#
|
|
19
|
+
# Note: This method returns as soon as the node receives the transaction; it does
|
|
20
|
+
# NOT wait for confirmation. Use `get_signature_statuses` to poll for commitment.
|
|
21
|
+
module SendTransaction
|
|
22
|
+
extend T::Sig
|
|
23
|
+
|
|
24
|
+
sig do
|
|
25
|
+
params(
|
|
26
|
+
transaction: T.untyped, # Transactions::Transaction or String (base64)
|
|
27
|
+
skip_preflight: T::Boolean,
|
|
28
|
+
preflight_commitment: T.nilable(Symbol),
|
|
29
|
+
max_retries: T.nilable(Integer),
|
|
30
|
+
min_context_slot: T.nilable(Integer)
|
|
31
|
+
).returns(Keys::Signature)
|
|
32
|
+
end
|
|
33
|
+
def send_transaction(
|
|
34
|
+
transaction,
|
|
35
|
+
skip_preflight: false,
|
|
36
|
+
preflight_commitment: nil,
|
|
37
|
+
max_retries: nil,
|
|
38
|
+
min_context_slot: nil
|
|
39
|
+
)
|
|
40
|
+
wire_base64 =
|
|
41
|
+
case transaction
|
|
42
|
+
when String
|
|
43
|
+
transaction
|
|
44
|
+
else
|
|
45
|
+
# Assume it responds to .message_bytes (Transactions::Transaction)
|
|
46
|
+
Base64.strict_encode64(transaction.message_bytes)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
config = { 'encoding' => 'base64' }
|
|
50
|
+
config['skipPreflight'] = skip_preflight if skip_preflight
|
|
51
|
+
config['preflightCommitment'] = preflight_commitment.to_s if preflight_commitment
|
|
52
|
+
config['maxRetries'] = max_retries if max_retries
|
|
53
|
+
config['minContextSlot'] = min_context_slot if min_context_slot
|
|
54
|
+
|
|
55
|
+
sig_str = transport.request('sendTransaction', [wire_base64, config])
|
|
56
|
+
Keys::Signature.new(sig_str)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Solana::Ruby::Kit
|
|
5
|
+
module Rpc
|
|
6
|
+
module Api
|
|
7
|
+
# Simulate a transaction without broadcasting it.
|
|
8
|
+
# Mirrors TypeScript's SimulateTransactionApi.simulateTransaction.
|
|
9
|
+
# Returns an RpcContextualValue whose value is the simulation result hash.
|
|
10
|
+
module SimulateTransaction
|
|
11
|
+
extend T::Sig
|
|
12
|
+
|
|
13
|
+
sig do
|
|
14
|
+
params(
|
|
15
|
+
encoded_transaction: String,
|
|
16
|
+
encoding: String,
|
|
17
|
+
commitment: T.nilable(Symbol),
|
|
18
|
+
sig_verify: T::Boolean,
|
|
19
|
+
replace_recent_blockhash: T::Boolean,
|
|
20
|
+
accounts: T.nilable(T::Hash[String, T.untyped])
|
|
21
|
+
).returns(RpcTypes::RpcContextualValue)
|
|
22
|
+
end
|
|
23
|
+
def simulate_transaction(
|
|
24
|
+
encoded_transaction,
|
|
25
|
+
encoding: 'base64',
|
|
26
|
+
commitment: nil,
|
|
27
|
+
sig_verify: false,
|
|
28
|
+
replace_recent_blockhash: false,
|
|
29
|
+
accounts: nil
|
|
30
|
+
)
|
|
31
|
+
config = {
|
|
32
|
+
'encoding' => encoding,
|
|
33
|
+
'sigVerify' => sig_verify,
|
|
34
|
+
'replaceRecentBlockhash' => replace_recent_blockhash
|
|
35
|
+
}
|
|
36
|
+
config['commitment'] = commitment.to_s if commitment
|
|
37
|
+
config['accounts'] = accounts if accounts
|
|
38
|
+
|
|
39
|
+
result = transport.request('simulateTransaction', [encoded_transaction, config])
|
|
40
|
+
slot = Kernel.Integer(result['context']['slot'])
|
|
41
|
+
|
|
42
|
+
RpcTypes::RpcContextualValue.new(slot: slot, value: result['value'])
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'transport'
|
|
5
|
+
require_relative 'api/get_slot'
|
|
6
|
+
require_relative 'api/get_balance'
|
|
7
|
+
require_relative 'api/get_latest_blockhash'
|
|
8
|
+
require_relative 'api/get_account_info'
|
|
9
|
+
require_relative 'api/get_block_height'
|
|
10
|
+
require_relative 'api/get_signature_statuses'
|
|
11
|
+
require_relative 'api/send_transaction'
|
|
12
|
+
require_relative 'api/request_airdrop'
|
|
13
|
+
require_relative 'api/is_blockhash_valid'
|
|
14
|
+
require_relative 'api/get_minimum_balance_for_rent_exemption'
|
|
15
|
+
require_relative 'api/get_multiple_accounts'
|
|
16
|
+
require_relative 'api/get_program_accounts'
|
|
17
|
+
require_relative 'api/get_transaction'
|
|
18
|
+
require_relative 'api/get_token_account_balance'
|
|
19
|
+
require_relative 'api/get_token_accounts_by_owner'
|
|
20
|
+
require_relative 'api/get_epoch_info'
|
|
21
|
+
require_relative 'api/get_vote_accounts'
|
|
22
|
+
require_relative 'api/simulate_transaction'
|
|
23
|
+
|
|
24
|
+
module Solana::Ruby::Kit
|
|
25
|
+
module Rpc
|
|
26
|
+
# Solana JSON-RPC client.
|
|
27
|
+
# Mirrors TypeScript's `Rpc` object created by `createSolanaRpc(url)`.
|
|
28
|
+
#
|
|
29
|
+
# Includes every API method as a mixin so the class surface exactly matches
|
|
30
|
+
# the TypeScript package's method set. The `transport` accessor provides
|
|
31
|
+
# sub-modules with the HTTP connection.
|
|
32
|
+
#
|
|
33
|
+
# @example
|
|
34
|
+
# rpc = Solana::Ruby::Kit::Rpc::Client.new(Solana::Ruby::Kit::RpcTypes.devnet)
|
|
35
|
+
# slot = rpc.get_slot
|
|
36
|
+
# res = rpc.get_balance(address)
|
|
37
|
+
# puts "#{res.value} lamports at slot #{res.slot}"
|
|
38
|
+
class Client
|
|
39
|
+
extend T::Sig
|
|
40
|
+
|
|
41
|
+
include Api::GetSlot
|
|
42
|
+
include Api::GetBalance
|
|
43
|
+
include Api::GetLatestBlockhash
|
|
44
|
+
include Api::GetAccountInfo
|
|
45
|
+
include Api::GetBlockHeight
|
|
46
|
+
include Api::GetSignatureStatuses
|
|
47
|
+
include Api::SendTransaction
|
|
48
|
+
include Api::RequestAirdrop
|
|
49
|
+
include Api::IsBlockhashValid
|
|
50
|
+
include Api::GetMinimumBalanceForRentExemption
|
|
51
|
+
include Api::GetMultipleAccounts
|
|
52
|
+
include Api::GetProgramAccounts
|
|
53
|
+
include Api::GetTransaction
|
|
54
|
+
include Api::GetTokenAccountBalance
|
|
55
|
+
include Api::GetTokenAccountsByOwner
|
|
56
|
+
include Api::GetEpochInfo
|
|
57
|
+
include Api::GetVoteAccounts
|
|
58
|
+
include Api::SimulateTransaction
|
|
59
|
+
|
|
60
|
+
sig { returns(Transport) }
|
|
61
|
+
attr_reader :transport
|
|
62
|
+
|
|
63
|
+
# @param cluster_url [RpcTypes::ClusterUrl, String] endpoint to connect to.
|
|
64
|
+
# @param headers [Hash<String, String>] additional HTTP headers.
|
|
65
|
+
# @param timeout [Integer] read timeout in seconds.
|
|
66
|
+
sig do
|
|
67
|
+
params(
|
|
68
|
+
cluster_url: T.untyped, # RpcTypes::ClusterUrl or String
|
|
69
|
+
headers: T::Hash[String, String],
|
|
70
|
+
timeout: Integer,
|
|
71
|
+
open_timeout: Integer
|
|
72
|
+
).void
|
|
73
|
+
end
|
|
74
|
+
def initialize(cluster_url, headers: {}, timeout: Transport::DEFAULT_TIMEOUT, open_timeout: Transport::DEFAULT_OPEN_TIMEOUT)
|
|
75
|
+
url = cluster_url.respond_to?(:url) ? cluster_url.url : cluster_url.to_s
|
|
76
|
+
@transport = T.let(
|
|
77
|
+
Transport.new(url: url, headers: headers, timeout: timeout, open_timeout: open_timeout),
|
|
78
|
+
Transport
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|