solana_rpc_ruby 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8994ffbb52e73a3bca8479da9dcef3cc95bbed08785c3250341a0172843aa634
4
- data.tar.gz: 9865e6aec8576bb6d1ab7ae8bb342b1cdd548b60bab0831b302a17782d941e06
3
+ metadata.gz: 7e28d62428fcd7647449c83fa04c5725303e1b2f4c2775834f2ec6a09253b47f
4
+ data.tar.gz: ca4ba1695ff8d517beeb3e9fb500a700bc1d44cc2f640a5efcd4d32fb87b7ece
5
5
  SHA512:
6
- metadata.gz: 198abd4194eca1ae77dee6d73ebfd16abeab319d2a14488602dab1403cf75c3fb2ce2a27f54c1cb67d88165defcf425fd1a6b6873520298a6f7f2a6700a151dd
7
- data.tar.gz: c107ba6e72f81988c0545763dc8096f031ce705ee96e860c7d913ad3c07906006ce1196fc66e0fd6885f87fd3c6001b7b9276d52ba7885cc47062aa49b2d1c1b
6
+ metadata.gz: 6eefa191b58abfcbf3ac9d6150aaf1f8d5405bc465ffe4361b675a82c7849649dc09bd0d0aa1a02e85407fd5bc68c72e239d016a9a99de5cf695fc36d7120d02
7
+ data.tar.gz: 50a8622360d7ebbe48ea4e5b2565ff651bc37fab5abb42644d0b04cc203671be91bfd649ef8c2c503f9438d355205170862e4e61a89a1e22fab4062716105134
data/CHANGELOG.md CHANGED
@@ -4,3 +4,6 @@
4
4
 
5
5
  ## 1.0.1
6
6
  * Add optional id argument that can be passed to MethodsWrapper class.
7
+
8
+ ## 1.1.0
9
+ * Add websockets connection to gem.
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
- cluster: 'https://api.testnet.solana.com', # optional, if not passed, default cluster from config will be used
42
- id: 123 # optional, if not passed, default random number from range 1 to 99_999 will be used
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
 
@@ -5,6 +5,8 @@ SolanaRpcRuby.config do |c|
5
5
  #
6
6
  # You can use this setting or pass cluster directly, check the docs.
7
7
  # c.cluster = 'https://api.testnet.solana.com'
8
+ # c.ws_cluster = 'ws://api.testnet.solana.com'
9
+
8
10
 
9
11
  # This one is mandatory.
10
12
  c.json_rpc_version = '2.0'
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolanaRpcRuby
4
- VERSION = '1.0.1'
4
+ VERSION = '1.1.0'
5
5
  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
@@ -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
@@ -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.13.1'
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.3'
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.1
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-06 00:00:00.000000000 Z
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.13.1
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.13.1
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.3
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.3
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: