solana_rpc_ruby 1.0.0.pre → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/README.md +155 -5
- data/lib/generators/solana_rpc_ruby/install_generator.rb +1 -1
- data/lib/generators/templates/solana_rpc_ruby_config.rb +7 -8
- data/lib/solana_rpc_ruby/api_client.rb +11 -12
- data/lib/solana_rpc_ruby/api_error.rb +5 -5
- data/lib/solana_rpc_ruby/helper_methods.rb +13 -0
- data/lib/solana_rpc_ruby/methods_wrapper.rb +191 -182
- data/lib/solana_rpc_ruby/request_body.rb +9 -4
- data/lib/solana_rpc_ruby/version.rb +1 -1
- data/lib/solana_rpc_ruby/websocket_client.rb +129 -0
- data/lib/solana_rpc_ruby/websocket_methods_wrapper.rb +300 -0
- data/lib/solana_rpc_ruby.rb +6 -4
- data/solana_rpc_ruby.gemspec +9 -3
- metadata +31 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b473e11604fa0d409efbe09260aa85bf467a1b76b6fa014e7a0716e019b3c5e
|
4
|
+
data.tar.gz: 7e116f4bdb58358bf85c3260eb526be306445d87ec87788648ee40ffbd9bd80d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5013b6799259a02d77cc232e3a1456a2de52b71c93c31e78a376c702656ed7c6444577e2f4467ceb8c5aa7df7b9b78d4bb05ee27711f003e08d6576c15b84fc4
|
7
|
+
data.tar.gz: ada8e45a050f6bc74e712f9a61a2e8b62032a6a9e60e8a21627c99dd5ce48673833b3cc119c6a893b117dcb81233e1aa8c196bb5cd27145bf684ab88b9c257cb
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
1
|
# Changelog
|
2
|
-
## 1.0.0
|
2
|
+
## 1.0.0
|
3
3
|
* Initial release
|
4
|
+
|
5
|
+
## 1.0.1
|
6
|
+
* Add optional id argument that can be passed to MethodsWrapper class.
|
7
|
+
|
8
|
+
## 1.1.0
|
9
|
+
* Add websockets connection to gem.
|
10
|
+
|
11
|
+
## 1.1.1
|
12
|
+
* Fix SolanaRpcRuby::ApiError occurring when websocket program runs for too long
|
13
|
+
(#<SolanaRpcRuby::ApiError: NoMethodError undefined method ping)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
![specs](https://github.com/Block-Logic/solana-rpc-ruby/actions/workflows/specs.yml/badge.svg
|
1
|
+
![specs](https://github.com/Block-Logic/solana-rpc-ruby/actions/workflows/specs.yml/badge.svg)
|
2
2
|
# solana_rpc_ruby
|
3
3
|
A Solana RPC Client for Ruby. This gem provides a wrapper methods for Solana RPC JSON API https://docs.solana.com/developing/clients/jsonrpc-api.
|
4
4
|
|
@@ -21,7 +21,7 @@ Next, you need to run the generator:
|
|
21
21
|
rails g solana_rpc_ruby:install
|
22
22
|
```
|
23
23
|
|
24
|
-
The latter command will generate a new config file `config/solana_rpc_ruby_config.rb` looking like this:
|
24
|
+
The latter command will generate a new config file `config/initializers/solana_rpc_ruby_config.rb` looking like this:
|
25
25
|
|
26
26
|
```ruby
|
27
27
|
require 'solana_rpc_ruby'
|
@@ -29,21 +29,171 @@ require 'solana_rpc_ruby'
|
|
29
29
|
SolanaRpcRuby.config do |c|
|
30
30
|
c.cluster = 'https://api.testnet.solana.com'
|
31
31
|
c.json_rpc_version = '2.0'
|
32
|
-
c.encoding = 'base58'
|
33
32
|
# ...other options
|
34
33
|
end
|
35
34
|
```
|
36
35
|
You can customize it to your needs.
|
37
36
|
|
38
37
|
### Usage examples
|
38
|
+
|
39
|
+
#### JSON RPC API
|
39
40
|
```ruby
|
40
41
|
# If you set default cluster you don't need to pass it every time.
|
41
|
-
method_wrapper = SolanaRpcRuby::MethodsWrapper.new(
|
42
|
+
method_wrapper = SolanaRpcRuby::MethodsWrapper.new(
|
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
|
49
|
+
)
|
50
|
+
|
42
51
|
response = method_wrapper.get_account_info(account_pubkey)
|
43
52
|
puts response
|
53
|
+
|
54
|
+
# You can check cluster and id that are used.
|
55
|
+
method_wrapper.cluster
|
56
|
+
method_wrapper.id
|
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
|
44
116
|
```
|
45
117
|
|
46
|
-
|
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.
|
194
|
+
|
195
|
+
All info about methods you can find in the docs on: https://www.rubydoc.info/github/Block-Logic/solana-rpc-ruby/main/SolanaRpcRuby
|
196
|
+
|
47
197
|
Also, as a reference you can use docs from solana: https://docs.solana.com/developing/clients/jsonrpc-api
|
48
198
|
## License
|
49
199
|
|
@@ -6,7 +6,7 @@ module SolanaRpcRuby
|
|
6
6
|
|
7
7
|
desc 'Creates a SolanaRpcRuby config file.'
|
8
8
|
def copy_config
|
9
|
-
template 'solana_rpc_ruby_config.rb', "#{Rails.root}/config/solana_rpc_ruby.rb"
|
9
|
+
template 'solana_rpc_ruby_config.rb', "#{Rails.root}/config/initializers/solana_rpc_ruby.rb"
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -1,14 +1,13 @@
|
|
1
1
|
require_relative 'solana_rpc_ruby'
|
2
2
|
|
3
|
-
# DEVNET_CLUSTER = 'https://api.devnet.solana.com'
|
4
|
-
# MAINNET_CLUSTER = 'https://api.mainnet-beta.solana.com'
|
5
|
-
# TESTNET_CLUSTER = 'https://api.testnet.solana.com'
|
6
|
-
|
7
3
|
SolanaRpcRuby.config do |c|
|
8
|
-
# These are
|
4
|
+
# These are options that you can set before using gem:
|
9
5
|
#
|
6
|
+
# You can use this setting or pass cluster directly, check the docs.
|
10
7
|
# c.cluster = 'https://api.testnet.solana.com'
|
11
|
-
# c.
|
12
|
-
|
13
|
-
|
8
|
+
# c.ws_cluster = 'ws://api.testnet.solana.com'
|
9
|
+
|
10
|
+
|
11
|
+
# This one is mandatory.
|
12
|
+
c.json_rpc_version = '2.0'
|
14
13
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'net/http'
|
2
|
-
|
3
2
|
module SolanaRpcRuby
|
4
3
|
##
|
5
4
|
# ApiClient class serves as a client for solana JSON RPC API.
|
@@ -14,29 +13,29 @@ module SolanaRpcRuby
|
|
14
13
|
attr_accessor :default_headers
|
15
14
|
|
16
15
|
# Initialize object with cluster address where requests will be sent.
|
17
|
-
#
|
18
|
-
# @param cluster [String]
|
16
|
+
#
|
17
|
+
# @param cluster [String]
|
19
18
|
def initialize(cluster = nil)
|
20
19
|
@cluster = cluster || SolanaRpcRuby.cluster
|
21
20
|
|
22
21
|
message = 'Cluster is missing. Please provide default cluster in config or pass it to the client directly.'
|
23
22
|
raise ArgumentError, message unless @cluster
|
24
23
|
end
|
25
|
-
|
24
|
+
|
26
25
|
# Sends request to the api.
|
27
26
|
#
|
28
|
-
# @param body [Hash]
|
27
|
+
# @param body [Hash]
|
29
28
|
# @param http_method [Symbol]
|
30
29
|
# @param params [Hash]
|
31
|
-
#
|
30
|
+
#
|
32
31
|
# @return [Object] Net::HTTPOK
|
33
32
|
def call_api(body:, http_method:, params: {})
|
34
33
|
uri = URI(@cluster)
|
35
34
|
rpc_response = Net::HTTP.public_send(
|
36
|
-
http_method,
|
37
|
-
uri,
|
38
|
-
body,
|
39
|
-
default_headers,
|
35
|
+
http_method,
|
36
|
+
uri,
|
37
|
+
body,
|
38
|
+
default_headers,
|
40
39
|
)
|
41
40
|
|
42
41
|
rpc_response
|
@@ -44,12 +43,12 @@ module SolanaRpcRuby
|
|
44
43
|
rescue Timeout::Error,
|
45
44
|
Net::HTTPError,
|
46
45
|
Net::HTTPNotFound,
|
47
|
-
Net::
|
46
|
+
Net::HTTPClientException,
|
48
47
|
Net::HTTPFatalError,
|
49
48
|
Net::ReadTimeout => e
|
50
|
-
|
51
49
|
fail ApiError.new(message: e.message)
|
52
50
|
rescue StandardError => e
|
51
|
+
|
53
52
|
message = "#{e.class} #{e.message}\n Backtrace: \n #{e.backtrace}"
|
54
53
|
fail ApiError.new(message: message)
|
55
54
|
end
|
@@ -6,20 +6,20 @@ module SolanaRpcRuby
|
|
6
6
|
# Error code.
|
7
7
|
# @return [Integer]
|
8
8
|
attr_reader :code
|
9
|
-
|
9
|
+
|
10
10
|
# Error message.
|
11
11
|
# @return [String]
|
12
12
|
attr_reader :message
|
13
|
-
|
13
|
+
|
14
14
|
# Initialize object with json response from the API with error.
|
15
|
-
#
|
15
|
+
#
|
16
16
|
# @param code [Integer]
|
17
17
|
# @param message [String]
|
18
|
-
#
|
18
|
+
#
|
19
19
|
# @return [SolanaRpcRuby::ApiError]
|
20
20
|
def initialize(code: nil, message:)
|
21
21
|
@code = code
|
22
|
-
@message = message
|
22
|
+
@message = message.to_s
|
23
23
|
|
24
24
|
super message
|
25
25
|
end
|
@@ -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
|