solana_rpc_ruby 1.0.1 → 1.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 +4 -4
- data/CHANGELOG.md +3 -0
- data/README.md +145 -3
- data/lib/generators/templates/solana_rpc_ruby_config.rb +2 -0
- data/lib/solana_rpc_ruby/helper_methods.rb +13 -0
- data/lib/solana_rpc_ruby/methods_wrapper.rb +0 -8
- data/lib/solana_rpc_ruby/version.rb +1 -1
- data/lib/solana_rpc_ruby/websocket_client.rb +131 -0
- data/lib/solana_rpc_ruby/websocket_methods_wrapper.rb +300 -0
- data/lib/solana_rpc_ruby.rb +6 -0
- data/solana_rpc_ruby.gemspec +4 -2
- metadata +22 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e28d62428fcd7647449c83fa04c5725303e1b2f4c2775834f2ec6a09253b47f
|
4
|
+
data.tar.gz: ca4ba1695ff8d517beeb3e9fb500a700bc1d44cc2f640a5efcd4d32fb87b7ece
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6eefa191b58abfcbf3ac9d6150aaf1f8d5405bc465ffe4361b675a82c7849649dc09bd0d0aa1a02e85407fd5bc68c72e239d016a9a99de5cf695fc36d7120d02
|
7
|
+
data.tar.gz: 50a8622360d7ebbe48ea4e5b2565ff651bc37fab5abb42644d0b04cc203671be91bfd649ef8c2c503f9438d355205170862e4e61a89a1e22fab4062716105134
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -35,20 +35,162 @@ end
|
|
35
35
|
You can customize it to your needs.
|
36
36
|
|
37
37
|
### Usage examples
|
38
|
+
|
39
|
+
#### JSON RPC API
|
38
40
|
```ruby
|
39
41
|
# If you set default cluster you don't need to pass it every time.
|
40
42
|
method_wrapper = SolanaRpcRuby::MethodsWrapper.new(
|
41
|
-
|
42
|
-
|
43
|
+
# optional, if not passed, default cluster from config will be used
|
44
|
+
cluster: 'https://api.testnet.solana.com',
|
45
|
+
|
46
|
+
# optional, if not passed, default random number
|
47
|
+
# from range 1 to 99_999 will be used
|
48
|
+
id: 123
|
43
49
|
)
|
44
50
|
|
45
51
|
response = method_wrapper.get_account_info(account_pubkey)
|
46
52
|
puts response
|
47
53
|
|
48
|
-
# You can check cluster and that are used.
|
54
|
+
# You can check cluster and id that are used.
|
49
55
|
method_wrapper.cluster
|
50
56
|
method_wrapper.id
|
51
57
|
```
|
58
|
+
#### Subscription Websocket (BETA)
|
59
|
+
```ruby
|
60
|
+
ws_method_wrapper = SolanaRpcRuby::WebsocketsMethodsWrapper.new(
|
61
|
+
# optional, if not passed, default ws_cluster from config will be used
|
62
|
+
cluster: 'ws://api.testnet.solana.com',
|
63
|
+
|
64
|
+
# optional, if not passed, default random number
|
65
|
+
# from range 1 to 99_999 will be used
|
66
|
+
id: 123
|
67
|
+
)
|
68
|
+
|
69
|
+
# You should see stream of messages in your console.
|
70
|
+
ws_method_wrapper.root_subscribe
|
71
|
+
|
72
|
+
# You can pass a block to do something with websocket's messages, ie:
|
73
|
+
block = Proc.new do |message|
|
74
|
+
json = JSON.parse(message)
|
75
|
+
puts json['params']
|
76
|
+
end
|
77
|
+
|
78
|
+
ws_method_wrapper.root_subscribe(&block)
|
79
|
+
|
80
|
+
# You can check cluster and id that are used.
|
81
|
+
ws_method_wrapper.cluster
|
82
|
+
ws_method_wrapper.id
|
83
|
+
```
|
84
|
+
|
85
|
+
#### Websockets usage in Rails
|
86
|
+
You can easily plug-in websockets connection to your rails app by using ActionCable.
|
87
|
+
Here is an example for development environment.
|
88
|
+
More explanation on Action Cable here: https://www.pluralsight.com/guides/updating-a-rails-app's-wall-feed-in-real-time-with-actioncable
|
89
|
+
|
90
|
+
0. Make sure that you have action_cable and solana_rpc_ruby gems installed properly. Also install redis unless you have it.
|
91
|
+
|
92
|
+
1. Mount action_cable in `routes.rb`.
|
93
|
+
```
|
94
|
+
Rails.application.routes.draw do
|
95
|
+
mount ActionCable.server => '/cable'
|
96
|
+
...
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
2. Update `config/environments/development.rb`.
|
101
|
+
```
|
102
|
+
config.action_cable.url = "ws://localhost:3000/cable"
|
103
|
+
config.action_cable.allowed_request_origins = [/http:\/\/*/, /https:\/\/*/]
|
104
|
+
```
|
105
|
+
|
106
|
+
3. Update adapter in `cable.yml`.
|
107
|
+
```
|
108
|
+
development:
|
109
|
+
adapter: redis
|
110
|
+
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
|
111
|
+
```
|
112
|
+
|
113
|
+
4. Create a channel.
|
114
|
+
```
|
115
|
+
rails g channel wall
|
116
|
+
```
|
117
|
+
|
118
|
+
5. Your `wall_channel.rb` should look like this:
|
119
|
+
```
|
120
|
+
class WallChannel < ApplicationCable::Channel
|
121
|
+
def subscribed
|
122
|
+
stream_from "wall_channel"
|
123
|
+
end
|
124
|
+
|
125
|
+
def unsubscribed
|
126
|
+
# Any cleanup needed when channel is unsubscribed
|
127
|
+
end
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
6. Your `wall_channel.js` should look like this (json keys are configured for `root_subscription` method response):
|
132
|
+
```
|
133
|
+
import consumer from "./consumer"
|
134
|
+
|
135
|
+
consumer.subscriptions.create("WallChannel", {
|
136
|
+
connected() {
|
137
|
+
console.log("Connected to WallChannel");
|
138
|
+
// Called when the subscription is ready for use on the server
|
139
|
+
},
|
140
|
+
|
141
|
+
disconnected() {
|
142
|
+
// Called when the subscription has been terminated by the server
|
143
|
+
},
|
144
|
+
|
145
|
+
received(data) {
|
146
|
+
let wall = document.getElementById('wall');
|
147
|
+
|
148
|
+
wall.innerHTML += "<p>Result: "+ data['message']['result'] + "</p>";
|
149
|
+
// Called when there's incoming data on the websocket for this channel
|
150
|
+
}
|
151
|
+
});
|
152
|
+
|
153
|
+
|
154
|
+
```
|
155
|
+
|
156
|
+
7. Create placeholder somewhere in your view for messages.
|
157
|
+
```
|
158
|
+
<div id='wall' style='overflow-y: scroll; height:400px;''>
|
159
|
+
<h1>Solana subscription messages</h1>
|
160
|
+
</div>
|
161
|
+
```
|
162
|
+
|
163
|
+
8. Create a script with a block to run websockets (`script/websockets_solana.rb`).
|
164
|
+
```
|
165
|
+
require_relative '../config/environment'
|
166
|
+
|
167
|
+
ws_method_wrapper = SolanaRpcRuby::WebsocketsMethodsWrapper.new
|
168
|
+
|
169
|
+
# Example of block that can be passed to the method to manipulate the data.
|
170
|
+
block = Proc.new do |message|
|
171
|
+
json = JSON.parse(message)
|
172
|
+
|
173
|
+
ActionCable.server.broadcast(
|
174
|
+
"wall_channel",
|
175
|
+
{
|
176
|
+
message: json['params']
|
177
|
+
}
|
178
|
+
)
|
179
|
+
end
|
180
|
+
|
181
|
+
ws_method_wrapper.root_subscribe(&block)
|
182
|
+
```
|
183
|
+
9. Run `rails s`, open webpage where you put your placeholder.
|
184
|
+
10. Open `http://localhost:3000/address_with_websockets_view`.
|
185
|
+
11. Run `rails r script/websockets_solana.rb` in another terminal window.
|
186
|
+
12. You should see incoming websocket messages on your webpage.
|
187
|
+
### Demo scripts
|
188
|
+
Gem is coming with demo scripts that you can run and test API and Websockets.
|
189
|
+
|
190
|
+
1. Clone the repo
|
191
|
+
2. Set the gemset
|
192
|
+
3. Run `ruby demo.rb` or `ruby demo_ws_METHOD.rb` to see example output.
|
193
|
+
4. Check the gem or Solana JSON RPC API docs to get more information about method usage and modify demo scripts loosely.
|
52
194
|
|
53
195
|
All info about methods you can find in the docs on: https://www.rubydoc.info/github/Block-Logic/solana-rpc-ruby/main/SolanaRpcRuby
|
54
196
|
|
@@ -12,5 +12,18 @@ module SolanaRpcRuby
|
|
12
12
|
|
13
13
|
object.nil? || object.empty?
|
14
14
|
end
|
15
|
+
|
16
|
+
# Creates method name to match names required by Solana RPC JSON.
|
17
|
+
#
|
18
|
+
# @param method [String]
|
19
|
+
#
|
20
|
+
# @return [String]
|
21
|
+
def create_method_name(method)
|
22
|
+
return '' unless method && (method.is_a?(String) || method.is_a?(Symbol))
|
23
|
+
|
24
|
+
method.to_s.split('_').map.with_index do |string, i|
|
25
|
+
i == 0 ? string : string.capitalize
|
26
|
+
end.join
|
27
|
+
end
|
15
28
|
end
|
16
29
|
end
|
@@ -1321,13 +1321,5 @@ module SolanaRpcRuby
|
|
1321
1321
|
return response
|
1322
1322
|
end
|
1323
1323
|
end
|
1324
|
-
|
1325
|
-
def create_method_name(method)
|
1326
|
-
return '' unless method
|
1327
|
-
|
1328
|
-
method.to_s.split('_').map.with_index do |string, i|
|
1329
|
-
i == 0 ? string : string.capitalize
|
1330
|
-
end.join
|
1331
|
-
end
|
1332
1324
|
end
|
1333
1325
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'faye/websocket'
|
3
|
+
module SolanaRpcRuby
|
4
|
+
##
|
5
|
+
# WebsocketClient class serves as a websocket client for solana JSON RPC API.
|
6
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api
|
7
|
+
class WebsocketClient
|
8
|
+
include RequestBody
|
9
|
+
|
10
|
+
KEEPALIVE_TIME = 60
|
11
|
+
SLEEP_TIME = 10
|
12
|
+
RETRIES_LIMIT = 3
|
13
|
+
|
14
|
+
# Determines which cluster will be used to send requests.
|
15
|
+
# @return [String]
|
16
|
+
attr_accessor :cluster
|
17
|
+
|
18
|
+
# Api client used to connect with API.
|
19
|
+
# @return [Object]
|
20
|
+
attr_accessor :client
|
21
|
+
|
22
|
+
# Initialize object with cluster address where requests will be sent.
|
23
|
+
#
|
24
|
+
# @param websocket_client [Object]
|
25
|
+
# @param cluster [String]
|
26
|
+
def initialize(websocket_client: Faye::WebSocket, cluster: nil)
|
27
|
+
@client = websocket_client
|
28
|
+
@cluster = cluster || SolanaRpcRuby.ws_cluster
|
29
|
+
@retries = 0
|
30
|
+
@subscription_info = nil
|
31
|
+
|
32
|
+
message = 'Websocket cluster is missing. Please provide default cluster in config or pass it to the client directly.'
|
33
|
+
raise ArgumentError, message unless @cluster
|
34
|
+
end
|
35
|
+
|
36
|
+
# Connects with cluster's websocket.
|
37
|
+
#
|
38
|
+
# @param body [String]
|
39
|
+
# @param &block [Proc]
|
40
|
+
#
|
41
|
+
# @return [String] # messages from websocket
|
42
|
+
def connect(body, &block)
|
43
|
+
EM.run {
|
44
|
+
# ping option sends some data to the server periodically,
|
45
|
+
# which prevents the connection to go idle.
|
46
|
+
ws = Faye::WebSocket::Client.new(@cluster, nil)
|
47
|
+
|
48
|
+
EM::PeriodicTimer.new(KEEPALIVE_TIME) do
|
49
|
+
while !ws.ping
|
50
|
+
@retries += 1
|
51
|
+
|
52
|
+
unless @retries <= 3
|
53
|
+
puts '3 ping retries failed, close connection.'
|
54
|
+
ws.close
|
55
|
+
break
|
56
|
+
end
|
57
|
+
|
58
|
+
puts 'Ping failed, sleep for 10 seconds...'
|
59
|
+
sleep SLEEP_TIME
|
60
|
+
puts "#{@retries} ping retry..."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Uncomment to disconnect websocket.
|
65
|
+
# EM::Timer.new(2) do
|
66
|
+
# ws.send(unsubscribe_body(body))
|
67
|
+
# end
|
68
|
+
|
69
|
+
ws.on :open do |event|
|
70
|
+
p [:open]
|
71
|
+
p "Status: #{ws.status}"
|
72
|
+
ws.send(body)
|
73
|
+
end
|
74
|
+
|
75
|
+
ws.on :message do |event|
|
76
|
+
# To run websocket_methods_wrapper_spec.rb, uncomment code below
|
77
|
+
# to return info about connection estabilished.
|
78
|
+
# Also, read the comment from the top of the mentioned file.
|
79
|
+
#
|
80
|
+
# if ENV['test'] == 'true'
|
81
|
+
# result = block_given? ? block.call(event.data) : event.data
|
82
|
+
# return result
|
83
|
+
# end
|
84
|
+
@subscription_info = event.data unless @subscription_info
|
85
|
+
|
86
|
+
if block_given?
|
87
|
+
block.call(event.data)
|
88
|
+
else
|
89
|
+
puts event.data
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
ws.on :close do |event|
|
94
|
+
p [:close, event.code, event.reason]
|
95
|
+
ws = nil
|
96
|
+
|
97
|
+
@retries += 1
|
98
|
+
if @retries <= RETRIES_LIMIT
|
99
|
+
puts 'Retry...'
|
100
|
+
# It restarts the websocket connection.
|
101
|
+
connect(body, &block)
|
102
|
+
else
|
103
|
+
puts 'Retries limit reached, closing. Wrong cluster address or unhealthy node might be a reason, please check.'
|
104
|
+
EM.stop
|
105
|
+
end
|
106
|
+
end
|
107
|
+
}
|
108
|
+
rescue Timeout::Error,
|
109
|
+
Net::HTTPError,
|
110
|
+
Net::HTTPNotFound,
|
111
|
+
Net::HTTPClientException,
|
112
|
+
Net::HTTPFatalError,
|
113
|
+
Net::ReadTimeout => e
|
114
|
+
fail ApiError.new(message: e.message)
|
115
|
+
rescue StandardError => e
|
116
|
+
message = "#{e.class} #{e.message}\n Backtrace: \n #{e.backtrace}"
|
117
|
+
fail ApiError.new(message: message)
|
118
|
+
end
|
119
|
+
|
120
|
+
def unsubscribe_body(body)
|
121
|
+
method = JSON.parse(body)['method']
|
122
|
+
info = JSON.parse(@subscription_info)
|
123
|
+
|
124
|
+
subscription_id = info['result']
|
125
|
+
id = info['id']
|
126
|
+
unsubscribe_method = method.gsub('Sub', 'Unsub')
|
127
|
+
|
128
|
+
create_json_body(unsubscribe_method, method_params: [subscription_id], id: id)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,300 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative 'request_body'
|
3
|
+
require_relative 'helper_methods'
|
4
|
+
|
5
|
+
module SolanaRpcRuby
|
6
|
+
##
|
7
|
+
# WebsocketsMethodsWrapper class serves as a wrapper for solana JSON RPC API websocket methods.
|
8
|
+
# All informations about params:
|
9
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#subscription-websocket
|
10
|
+
class WebsocketsMethodsWrapper
|
11
|
+
include RequestBody
|
12
|
+
include HelperMethods
|
13
|
+
|
14
|
+
# Determines which cluster will be used to send requests.
|
15
|
+
# @return [SolanaRpcRuby::WebsocketClient]
|
16
|
+
attr_accessor :websocket_client
|
17
|
+
|
18
|
+
# Cluster where requests will be sent.
|
19
|
+
# @return [String]
|
20
|
+
attr_accessor :cluster
|
21
|
+
|
22
|
+
# Unique client-generated identifying integer.
|
23
|
+
# @return [Integer]
|
24
|
+
attr_accessor :id
|
25
|
+
|
26
|
+
# Initialize object with cluster address where requests will be sent.
|
27
|
+
#
|
28
|
+
# @param api_client [ApiClient]
|
29
|
+
# @param cluster [String] cluster where requests will be sent.
|
30
|
+
# @param id [Integer] unique client-generated identifying integer.
|
31
|
+
def initialize(
|
32
|
+
websocket_client: WebsocketClient,
|
33
|
+
cluster: SolanaRpcRuby.ws_cluster,
|
34
|
+
id: rand(1...99_999)
|
35
|
+
)
|
36
|
+
@websocket_client = websocket_client.new(cluster: cluster)
|
37
|
+
@id = id
|
38
|
+
end
|
39
|
+
|
40
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#accountsubscribe
|
41
|
+
# Subscribe to an account to receive notifications when the lamports or data for a given account public key changes
|
42
|
+
#
|
43
|
+
# @param account_pubkey [String]
|
44
|
+
# @param commitment [String]
|
45
|
+
# @param encoding [String]
|
46
|
+
# @param &block [Proc]
|
47
|
+
#
|
48
|
+
# @return [Integer] Subscription id (needed to unsubscribe)
|
49
|
+
def account_subscribe(account_pubkey, commitment: nil, encoding: '', &block)
|
50
|
+
method = create_method_name(__method__)
|
51
|
+
|
52
|
+
params = []
|
53
|
+
params_hash = {}
|
54
|
+
params_hash['commitment'] = commitment unless blank?(commitment)
|
55
|
+
params_hash['encoding'] = encoding unless blank?(encoding)
|
56
|
+
|
57
|
+
params << account_pubkey
|
58
|
+
params << params_hash if params_hash.any?
|
59
|
+
|
60
|
+
subscribe(method, method_params: params, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#accountunsubscribe
|
64
|
+
# Unsubscribe from account change notifications
|
65
|
+
#
|
66
|
+
# @param subscription_id [Integer]
|
67
|
+
#
|
68
|
+
# @return [Bool] unsubscribe success message
|
69
|
+
def account_unsubscribe(subscription_id)
|
70
|
+
method = create_method_name(__method__)
|
71
|
+
unsubscribe(method, subscription_id: subscription_id)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#logssubscribe
|
75
|
+
# Subscribe to transaction logging
|
76
|
+
#
|
77
|
+
# @param filter [String]|[Hash]
|
78
|
+
# @option filter [Array] :mentions
|
79
|
+
# @param commitment [String]
|
80
|
+
# @param &block [Proc]
|
81
|
+
#
|
82
|
+
# @return [Integer] Subscription id (needed to unsubscribe)
|
83
|
+
def logs_subscribe(filter, commitment: nil, &block)
|
84
|
+
method = create_method_name(__method__)
|
85
|
+
|
86
|
+
params = []
|
87
|
+
params_hash = {}
|
88
|
+
params_hash['commitment'] = commitment unless blank?(commitment)
|
89
|
+
|
90
|
+
params << filter
|
91
|
+
params << params_hash
|
92
|
+
|
93
|
+
subscribe(method, method_params: params, &block)
|
94
|
+
end
|
95
|
+
|
96
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#logsunsubscribe
|
97
|
+
# Unsubscribe from transaction logging
|
98
|
+
#
|
99
|
+
# @param subscription_id [Integer]
|
100
|
+
#
|
101
|
+
# @return [Bool] unsubscribe success message
|
102
|
+
|
103
|
+
def logs_unsubscribe(subscription_id)
|
104
|
+
method = create_method_name(__method__)
|
105
|
+
unsubscribe(method, subscription_id: subscription_id)
|
106
|
+
end
|
107
|
+
|
108
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#programsubscribe
|
109
|
+
# Subscribe to a program to receive notifications when the lamports or data for a given account owned by the program changes
|
110
|
+
#
|
111
|
+
# @param account_pubkey [String]
|
112
|
+
# @param commitment [String]
|
113
|
+
# @param encoding [String]
|
114
|
+
# @param filters [Array]
|
115
|
+
# @param &block [Proc]
|
116
|
+
#
|
117
|
+
# @return [Integer] Subscription id (needed to unsubscribe)
|
118
|
+
def program_subscribe(
|
119
|
+
program_id_pubkey,
|
120
|
+
commitment: nil,
|
121
|
+
encoding: '',
|
122
|
+
filters: [],
|
123
|
+
&block
|
124
|
+
)
|
125
|
+
method = create_method_name(__method__)
|
126
|
+
|
127
|
+
params = []
|
128
|
+
params_hash = {}
|
129
|
+
params_hash['commitment'] = commitment unless blank?(commitment)
|
130
|
+
params_hash['encoding'] = encoding unless blank?(encoding)
|
131
|
+
params_hash['filters'] = filters unless blank?(filters)
|
132
|
+
|
133
|
+
params << program_id_pubkey
|
134
|
+
params << params_hash if params_hash.any?
|
135
|
+
|
136
|
+
subscribe(method, method_params: params, &block)
|
137
|
+
end
|
138
|
+
|
139
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#programunsubscribe
|
140
|
+
# Unsubscribe from program-owned account change notifications
|
141
|
+
#
|
142
|
+
# @param subscription_id [Integer]
|
143
|
+
#
|
144
|
+
# @return [Bool] unsubscribe success message
|
145
|
+
|
146
|
+
def program_unsubscribe(subscription_id)
|
147
|
+
method = create_method_name(__method__)
|
148
|
+
unsubscribe(method, subscription_id: subscription_id)
|
149
|
+
end
|
150
|
+
|
151
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#signaturesubscribe
|
152
|
+
# Subscribe to a transaction signature to receive notification when the transaction is confirmed
|
153
|
+
# On signatureNotification, the subscription is automatically cancelled
|
154
|
+
#
|
155
|
+
# @param transaction_signature [String]
|
156
|
+
# @param commitment [String]
|
157
|
+
# @param &block [Proc]
|
158
|
+
#
|
159
|
+
# @return [Integer] Subscription id (needed to unsubscribe)
|
160
|
+
def signature_subscribe(
|
161
|
+
transaction_signature,
|
162
|
+
commitment: nil,
|
163
|
+
&block
|
164
|
+
)
|
165
|
+
method = create_method_name(__method__)
|
166
|
+
|
167
|
+
params = []
|
168
|
+
params_hash = {}
|
169
|
+
params_hash['commitment'] = commitment unless blank?(commitment)
|
170
|
+
|
171
|
+
params << transaction_signature
|
172
|
+
params << params_hash
|
173
|
+
|
174
|
+
subscribe(method, method_params: params, &block)
|
175
|
+
end
|
176
|
+
|
177
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#signatureunsubscribe
|
178
|
+
# Unsubscribe from signature confirmation notification
|
179
|
+
#
|
180
|
+
# @param subscription_id [Integer]
|
181
|
+
#
|
182
|
+
# @return [Bool] unsubscribe success message
|
183
|
+
def signature_unsubscribe(subscription_id)
|
184
|
+
method = create_method_name(__method__)
|
185
|
+
unsubscribe(method, subscription_id: subscription_id)
|
186
|
+
end
|
187
|
+
|
188
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#slotsubscribe
|
189
|
+
# Subscribe to receive notification anytime a slot is processed by the validator
|
190
|
+
#
|
191
|
+
# @param &block [Proc]
|
192
|
+
#
|
193
|
+
# @return [Integer] Subscription id (needed to unsubscribe)
|
194
|
+
def slot_subscribe(&block)
|
195
|
+
method = create_method_name(__method__)
|
196
|
+
|
197
|
+
subscribe(method, &block)
|
198
|
+
end
|
199
|
+
|
200
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#slotunsubscribe
|
201
|
+
# Unsubscribe from slot notifications
|
202
|
+
#
|
203
|
+
# @param subscription_id [Integer]
|
204
|
+
#
|
205
|
+
# @return [Bool] unsubscribe success message
|
206
|
+
def slot_unsubscribe(subscription_id)
|
207
|
+
method = create_method_name(__method__)
|
208
|
+
unsubscribe(method, subscription_id: subscription_id)
|
209
|
+
end
|
210
|
+
|
211
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#slotsupdatessubscribe---unstable
|
212
|
+
#
|
213
|
+
# This subscription is unstable; the format of this subscription may change in the future and it may not always be supported
|
214
|
+
# Subscribe to receive a notification from the validator on a variety of updates on every slot
|
215
|
+
#
|
216
|
+
# @param &block [Proc]
|
217
|
+
#
|
218
|
+
# @return [Integer] Subscription id (needed to unsubscribe)
|
219
|
+
def slots_updates_subscribe(&block)
|
220
|
+
method = create_method_name(__method__)
|
221
|
+
|
222
|
+
subscribe(method, &block)
|
223
|
+
end
|
224
|
+
|
225
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#slotsupdatesunsubscribe
|
226
|
+
# Unsubscribe from slot-update notifications
|
227
|
+
#
|
228
|
+
# @param subscription_id [Integer]
|
229
|
+
#
|
230
|
+
# @return [Bool] unsubscribe success message
|
231
|
+
def slots_updates_unsubscribe(subscription_id)
|
232
|
+
method = create_method_name(__method__)
|
233
|
+
unsubscribe(method, subscription_id: subscription_id)
|
234
|
+
end
|
235
|
+
|
236
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#rootsubscribe
|
237
|
+
#
|
238
|
+
# Subscribe to receive notification anytime a new root is set by the validator.
|
239
|
+
#
|
240
|
+
# @param &block [Proc]
|
241
|
+
#
|
242
|
+
# @return [Integer] Subscription id (needed to unsubscribe)
|
243
|
+
def root_subscribe(&block)
|
244
|
+
method = create_method_name(__method__)
|
245
|
+
|
246
|
+
subscribe(method, &block)
|
247
|
+
end
|
248
|
+
|
249
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#rootunsubscribe
|
250
|
+
# Unsubscribe from root notifications
|
251
|
+
#
|
252
|
+
# @param subscription_id [Integer]
|
253
|
+
#
|
254
|
+
# @return [Bool] unsubscribe success message
|
255
|
+
def root_unsubscribe(subscription_id)
|
256
|
+
method = create_method_name(__method__)
|
257
|
+
unsubscribe(method, subscription_id: subscription_id)
|
258
|
+
end
|
259
|
+
|
260
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#votesubscribe---unstable-disabled-by-default
|
261
|
+
#
|
262
|
+
# This subscription is unstable and only available if the validator was started with the --rpc-pubsub-enable-vote-subscription flag.
|
263
|
+
# The format of this subscription may change in the future
|
264
|
+
#
|
265
|
+
# Subscribe to receive notification anytime a new vote is observed in gossip.
|
266
|
+
# These votes are pre-consensus therefore there is no guarantee these votes will enter the ledger.
|
267
|
+
#
|
268
|
+
# @param &block [Proc]
|
269
|
+
#
|
270
|
+
# @return [Integer] Subscription id (needed to unsubscribe)
|
271
|
+
def vote_subscribe(&block)
|
272
|
+
method = create_method_name(__method__)
|
273
|
+
|
274
|
+
subscribe(method, &block)
|
275
|
+
end
|
276
|
+
|
277
|
+
# @see https://docs.solana.com/developing/clients/jsonrpc-api#rootunsubscribe
|
278
|
+
# Unsubscribe from vote notifications
|
279
|
+
#
|
280
|
+
# @param subscription_id [Integer]
|
281
|
+
#
|
282
|
+
# @return [Bool] unsubscribe success message
|
283
|
+
def vote_unsubscribe(subscription_id)
|
284
|
+
method = create_method_name(__method__)
|
285
|
+
unsubscribe(method, subscription_id: subscription_id)
|
286
|
+
end
|
287
|
+
|
288
|
+
private
|
289
|
+
|
290
|
+
def subscribe(method, method_params: [], &block)
|
291
|
+
body = create_json_body(method, method_params: method_params)
|
292
|
+
@websocket_client.connect(body, &block)
|
293
|
+
end
|
294
|
+
|
295
|
+
def unsubscribe(method, subscription_id:)
|
296
|
+
body = create_json_body(method, method_params: [subscription_id])
|
297
|
+
@websocket_client.connect(body)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
data/lib/solana_rpc_ruby.rb
CHANGED
@@ -2,6 +2,8 @@ require_relative 'solana_rpc_ruby/api_client'
|
|
2
2
|
require_relative 'solana_rpc_ruby/api_error'
|
3
3
|
require_relative 'solana_rpc_ruby/methods_wrapper'
|
4
4
|
require_relative 'solana_rpc_ruby/response'
|
5
|
+
require_relative 'solana_rpc_ruby/websocket_client'
|
6
|
+
require_relative 'solana_rpc_ruby/websocket_methods_wrapper'
|
5
7
|
|
6
8
|
# Namespace for classes and modules that handle connection with solana JSON RPC API.
|
7
9
|
module SolanaRpcRuby
|
@@ -10,6 +12,10 @@ module SolanaRpcRuby
|
|
10
12
|
# @return [String] cluster address.
|
11
13
|
attr_accessor :cluster
|
12
14
|
|
15
|
+
# Default websocket cluster address that will be used if not passed.
|
16
|
+
# @return [String] websocket cluster address.
|
17
|
+
attr_accessor :ws_cluster
|
18
|
+
|
13
19
|
# Default json rpc version that will be used.
|
14
20
|
# @return [String] json rpc version.
|
15
21
|
attr_accessor :json_rpc_version
|
data/solana_rpc_ruby.gemspec
CHANGED
@@ -28,14 +28,16 @@ Gem::Specification.new do |spec|
|
|
28
28
|
'source_code_uri' => 'https://github.com/Block-Logic/solana-rpc-ruby'
|
29
29
|
}
|
30
30
|
|
31
|
+
spec.add_dependency 'faye-websocket', '~> 0.11'
|
32
|
+
|
31
33
|
spec.add_development_dependency 'rubocop', '~> 1.15'
|
32
34
|
spec.add_development_dependency 'rubocop-performance', '~> 1.11'
|
33
35
|
spec.add_development_dependency 'rubocop-rspec', '~> 2.3'
|
34
|
-
spec.add_development_dependency 'pry', '~> 0.
|
36
|
+
spec.add_development_dependency 'pry', '~> 0.14'
|
35
37
|
|
36
38
|
spec.add_development_dependency 'codecov', '~> 0.4'
|
37
39
|
spec.add_development_dependency 'dotenv', '~> 2.7'
|
38
|
-
spec.add_development_dependency 'rails', '~> 6.1
|
40
|
+
spec.add_development_dependency 'rails', '~> 6.1'
|
39
41
|
spec.add_development_dependency 'rake', '~> 13.0'
|
40
42
|
spec.add_development_dependency 'rspec', '~> 3.10'
|
41
43
|
spec.add_development_dependency 'rspec-rails', '~> 4.0'
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solana_rpc_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Block Logic Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-08-
|
11
|
+
date: 2021-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faye-websocket
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.11'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.11'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rubocop
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +72,14 @@ dependencies:
|
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
75
|
+
version: '0.14'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
82
|
+
version: '0.14'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: codecov
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,14 +114,14 @@ dependencies:
|
|
100
114
|
requirements:
|
101
115
|
- - "~>"
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: 6.1
|
117
|
+
version: '6.1'
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: 6.1
|
124
|
+
version: '6.1'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: rake
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -218,6 +232,8 @@ files:
|
|
218
232
|
- lib/solana_rpc_ruby/request_body.rb
|
219
233
|
- lib/solana_rpc_ruby/response.rb
|
220
234
|
- lib/solana_rpc_ruby/version.rb
|
235
|
+
- lib/solana_rpc_ruby/websocket_client.rb
|
236
|
+
- lib/solana_rpc_ruby/websocket_methods_wrapper.rb
|
221
237
|
- solana_rpc_ruby.gemspec
|
222
238
|
homepage: https://github.com/Block-Logic/solana-rpc-ruby
|
223
239
|
licenses:
|