substrate_client.rb 0.1.4 → 0.1.9
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/Dockerfile +17 -0
- data/Gemfile.lock +16 -18
- data/README.md +115 -146
- data/exe/metadata +5 -6
- data/lib/helper.rb +119 -0
- data/lib/substrate_client.rb +53 -329
- data/lib/substrate_client/version.rb +1 -1
- data/substrate_client.gemspec +3 -3
- metadata +16 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9993008c06625166a02238e5efddd9fe0c840f4c00e7a3dd053d58a9e7ec5a8
|
4
|
+
data.tar.gz: f360d71d6ba20d8ec5ed56d00ba8651a6a5371d88399c2a7113f1315a4b4f2ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a2158b190ebf549901de66c5b590a0780177149427efb77439db24b6756f2b2890de5ab91010448338e8a8da48ce8be6f917428f35e52cd1e578af637746da8
|
7
|
+
data.tar.gz: 3c46a63f1173b4ae417c76fd5325a612b4f22e299483cf6adac447914a8e1a556dc96c4559af29eadfb308645a1950d659d67aa82a07af7587f833a128cd495a
|
data/Dockerfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
FROM ruby:2.6-alpine3.11
|
2
|
+
|
3
|
+
ENV BUILD_PACKAGES curl-dev build-base
|
4
|
+
|
5
|
+
RUN apk update && \
|
6
|
+
apk upgrade && \
|
7
|
+
apk add git curl $BUILD_PACKAGES
|
8
|
+
|
9
|
+
WORKDIR /usr/src/app
|
10
|
+
|
11
|
+
COPY . .
|
12
|
+
|
13
|
+
RUN gem install bundler:1.17.3 && \
|
14
|
+
bundle install && \
|
15
|
+
rake install:local
|
16
|
+
|
17
|
+
CMD ["sh"]
|
data/Gemfile.lock
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
substrate_client.rb (0.1.
|
4
|
+
substrate_client.rb (0.1.9)
|
5
5
|
activesupport (~> 5.2.4)
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
kontena-websocket-client (~> 0.1.1)
|
7
|
+
scale.rb (~> 0.2.12)
|
8
|
+
substrate_common.rb (~> 0.1.9)
|
9
9
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
|
-
activesupport (5.2.4.
|
13
|
+
activesupport (5.2.4.4)
|
14
14
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
15
15
|
i18n (>= 0.7, < 2)
|
16
16
|
minitest (~> 5.1)
|
@@ -18,17 +18,15 @@ GEM
|
|
18
18
|
base58 (0.2.3)
|
19
19
|
blake2b (0.10.0)
|
20
20
|
coderay (1.1.2)
|
21
|
-
concurrent-ruby (1.1.
|
21
|
+
concurrent-ruby (1.1.7)
|
22
22
|
diff-lcs (1.3)
|
23
|
-
|
24
|
-
faye-websocket (0.10.9)
|
25
|
-
eventmachine (>= 0.12.0)
|
26
|
-
websocket-driver (>= 0.5.1)
|
27
|
-
i18n (1.8.2)
|
23
|
+
i18n (1.8.5)
|
28
24
|
concurrent-ruby (~> 1.0)
|
29
|
-
json (2.3.
|
25
|
+
json (2.3.1)
|
26
|
+
kontena-websocket-client (0.1.1)
|
27
|
+
websocket-driver (~> 0.6.5)
|
30
28
|
method_source (0.9.2)
|
31
|
-
minitest (5.14.
|
29
|
+
minitest (5.14.2)
|
32
30
|
pry (0.12.2)
|
33
31
|
coderay (~> 1.1.0)
|
34
32
|
method_source (~> 0.9.0)
|
@@ -46,12 +44,12 @@ GEM
|
|
46
44
|
diff-lcs (>= 1.2.0, < 2.0)
|
47
45
|
rspec-support (~> 3.9.0)
|
48
46
|
rspec-support (3.9.2)
|
49
|
-
scale.rb (0.2.
|
47
|
+
scale.rb (0.2.12)
|
50
48
|
activesupport (>= 4.0.0)
|
51
49
|
json (~> 2.3.0)
|
52
|
-
substrate_common.rb (~> 0.1.
|
50
|
+
substrate_common.rb (~> 0.1.9)
|
53
51
|
thor (~> 0.19.0)
|
54
|
-
substrate_common.rb (0.1.
|
52
|
+
substrate_common.rb (0.1.9)
|
55
53
|
base58
|
56
54
|
blake2b
|
57
55
|
xxhash
|
@@ -59,9 +57,9 @@ GEM
|
|
59
57
|
thread_safe (0.3.6)
|
60
58
|
tzinfo (1.2.7)
|
61
59
|
thread_safe (~> 0.1)
|
62
|
-
websocket-driver (0.
|
60
|
+
websocket-driver (0.6.5)
|
63
61
|
websocket-extensions (>= 0.1.0)
|
64
|
-
websocket-extensions (0.1.
|
62
|
+
websocket-extensions (0.1.5)
|
65
63
|
xxhash (0.4.0)
|
66
64
|
|
67
65
|
PLATFORMS
|
data/README.md
CHANGED
@@ -22,184 +22,153 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
### Supported rpc methods
|
24
24
|
|
25
|
-
#### rpc method list
|
26
|
-
|
27
25
|
```ruby
|
28
26
|
require "substrate_client"
|
29
27
|
|
30
28
|
client = SubstrateClient.new("wss://kusama-rpc.polkadot.io/")
|
31
|
-
|
29
|
+
client.methods
|
30
|
+
```
|
31
|
+
returns like:
|
32
|
+
```shell
|
33
|
+
[
|
34
|
+
"account_nextIndex",
|
35
|
+
"author_hasKey",
|
36
|
+
...
|
37
|
+
"chain_getBlock",
|
38
|
+
"chain_getBlockHash",
|
39
|
+
...
|
40
|
+
]
|
32
41
|
```
|
33
|
-
The rpc methods can be dynamically called by its name, so the methods returned by this method can all be used.
|
34
|
-
|
35
|
-
#### hard-coded rpc method
|
36
|
-
|
37
|
-
But, in order to show the parameters more clearly, some important or frequently used methods are hard-coded:
|
38
|
-
|
39
|
-
- chain_get_finalised_head
|
40
|
-
|
41
|
-
Get hash of the last finalized block in the canon chain
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
- chain_get_head
|
46
|
-
|
47
|
-
- chain_get_header(block_hash = nil)
|
48
|
-
|
49
|
-
Retrieves the header for a specific block
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
- chain_get_block(block_hash = nil)
|
54
|
-
|
55
|
-
Get header and body of a relay chain block
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
- chain_get_block_hash(block_id)
|
60
|
-
|
61
|
-
Get the block hash for a specific block
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
- chain_get_runtime_version(block_hash = nil)
|
66
|
-
|
67
|
-
Get the runtime version for a specific block
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
- state_get_metadata(block_hash = nil)
|
72
|
-
|
73
|
-
Returns the runtime metadata by block
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
- state_get_storage(storage_key, block_hash = nil)
|
78
|
-
|
79
|
-
Retrieves the storage for a key
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
- system_name
|
84
|
-
|
85
|
-
- system_version
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
- chain_subscribe_all_heads(&callback)
|
90
|
-
|
91
|
-
Retrieves the newest header via subscription. This will return data continuously until you unsubscribe the subscription.
|
92
|
-
|
93
|
-
```ruby
|
94
|
-
subscription = client.chain_subscribe_new_heads do |data|
|
95
|
-
p data
|
96
|
-
end
|
97
|
-
```
|
98
|
-
|
99
|
-
- chain_unsubscribe_all_heads(subscription)
|
100
|
-
|
101
|
-
Unsubscribe newest header subscription.
|
102
|
-
|
103
|
-
```ruby
|
104
|
-
client.chain_unsubscribe_all_heads(subscription)
|
105
|
-
```
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
- chain_subscribe_new_heads(&callback)
|
110
|
-
|
111
|
-
Retrieves the best header via subscription. This will return data continuously until you unsubscribe the subscription.
|
112
|
-
|
113
|
-
```ruby
|
114
|
-
subscription = client.chain_subscribe_new_heads do |data|
|
115
|
-
p data
|
116
|
-
end
|
117
|
-
```
|
118
|
-
|
119
|
-
- chain_unsubscribe_new_heads(subscription)
|
120
42
|
|
121
|
-
|
43
|
+
The rpc methods can be dynamically called by its name, so you can call it like:
|
122
44
|
|
123
|
-
|
124
|
-
|
125
|
-
|
45
|
+
```ruby
|
46
|
+
client.chain_getBlockHash(1024)
|
47
|
+
```
|
126
48
|
|
49
|
+
### Origin rpc methods
|
50
|
+
|
51
|
+
- `account_nextIndex`
|
52
|
+
* `author_hasKey`
|
53
|
+
* `author_hasSessionKeys`
|
54
|
+
* `author_insertKey`
|
55
|
+
* `author_pendingExtrinsics`
|
56
|
+
* `author_removeExtrinsic`
|
57
|
+
* `author_rotateKeys`
|
58
|
+
* `author_submitExtrinsic`
|
59
|
+
* `babe_epochAuthorship`
|
60
|
+
* `chain_getBlock(block_hash = nil)`
|
61
|
+
* `chain_getBlockHash(block_id)`
|
62
|
+
* `chain_getFinalisedHead`
|
63
|
+
* `chain_getFinalizedHead`
|
64
|
+
* `chain_getHead`
|
65
|
+
* `chain_getHeader(block_hash = nil)`
|
66
|
+
* `chain_getRuntimeVersion(block_hash = nil)`
|
67
|
+
* `childstate_getKeys`
|
68
|
+
* `childstate_getStorage`
|
69
|
+
* `childstate_getStorageHash`
|
70
|
+
* `childstate_getStorageSize`
|
71
|
+
* `grandpa_roundState`
|
72
|
+
* `offchain_localStorageGet`
|
73
|
+
* `offchain_localStorageSet`
|
74
|
+
* `payment_queryInfo`
|
75
|
+
* `state_call`
|
76
|
+
* `state_callAt`
|
77
|
+
* `state_getKeys`
|
78
|
+
* `state_getKeysPaged`
|
79
|
+
* `state_getKeysPagedAt`
|
80
|
+
* `state_getMetadata(block_hash = nil)`
|
81
|
+
* `state_getPairs`
|
82
|
+
* `state_getReadProof`
|
83
|
+
* `state_getRuntimeVersion`
|
84
|
+
* `state_getStorage(storage_key, block_hash = nil)`
|
85
|
+
* `state_getStorageAt`
|
86
|
+
* `state_getStorageHash`
|
87
|
+
* `state_getStorageHashAt`
|
88
|
+
* `state_getStorageSize`
|
89
|
+
* `state_getStorageSizeAt`
|
90
|
+
* `state_queryStorage`
|
91
|
+
* `state_queryStorageAt`
|
92
|
+
* `system_accountNextIndex`
|
93
|
+
* `system_addReservedPeer`
|
94
|
+
* `system_chain`
|
95
|
+
* `system_chainType`
|
96
|
+
* `system_dryRun`
|
97
|
+
* `system_dryRunAt`
|
98
|
+
* `system_health`
|
99
|
+
* `system_localListenAddresses`
|
100
|
+
* `system_localPeerId`
|
101
|
+
* `system_name`
|
102
|
+
* `system_networkState`
|
103
|
+
* `system_nodeRoles`
|
104
|
+
* `system_peers`
|
105
|
+
* `system_properties`
|
106
|
+
* `system_removeReservedPeer`
|
107
|
+
- `system_version`
|
108
|
+
|
109
|
+
### Wrap methods
|
127
110
|
|
111
|
+
These methods will encode the parameters and decode the returned data
|
128
112
|
|
129
|
-
-
|
113
|
+
- `get_block_number(block_hash)`
|
130
114
|
|
131
|
-
|
115
|
+
- `get_metadata(block_hash)`
|
132
116
|
|
133
|
-
|
134
|
-
subscription = client.chain_subscribe_finalized_heads do |data|
|
135
|
-
p data
|
136
|
-
end
|
137
|
-
```
|
117
|
+
- `get_block(block_hash=nil)`
|
138
118
|
|
139
|
-
-
|
119
|
+
- `get_block_events(block_hash)`
|
140
120
|
|
141
|
-
|
121
|
+
- `get_storage(module_name, storage_name, params = nil, block_hash = nil)`
|
142
122
|
|
143
123
|
```ruby
|
144
|
-
client.
|
124
|
+
client.get_storage("Balances", "TotalIssuance", nil, nil)
|
125
|
+
client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"], nil)
|
126
|
+
client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"], nil)
|
145
127
|
```
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
- state_subscribe_runtime_version(&callback)
|
150
|
-
|
151
|
-
Retrieves the runtime version via subscription.
|
152
|
-
|
153
|
-
- state_unsubscribe_runtime_version(subscription)
|
154
|
-
|
155
|
-
Unsubscribe the runtime version subscription.
|
156
|
-
|
157
128
|
|
158
|
-
|
159
|
-
- state_subscribe_storage(keys, &callback)
|
160
|
-
|
161
|
-
Subscribes to storage changes for the provided keys until unsubscribe.
|
129
|
+
- `compose_call(module_name, call_name, params, block_hash=nil)`
|
162
130
|
|
163
131
|
```ruby
|
164
|
-
|
165
|
-
p data
|
166
|
-
end
|
132
|
+
client.compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }, nil
|
167
133
|
```
|
168
134
|
|
169
|
-
|
170
|
-
|
171
|
-
Unsubscribe storage changes.
|
172
|
-
|
173
|
-
|
174
|
-
### Cutom methods based on rpc methods
|
175
|
-
|
176
|
-
These methods will encode the parameters and decode the returned data
|
135
|
+
## Docker
|
177
136
|
|
178
|
-
|
137
|
+
1. update to latest image
|
179
138
|
|
180
|
-
|
139
|
+
`docker pull itering/substrate_client:latest`
|
181
140
|
|
182
|
-
|
141
|
+
2. Run image:
|
183
142
|
|
184
|
-
-
|
143
|
+
`docker run -it itering/substrate_client:latest`
|
185
144
|
|
186
|
-
|
145
|
+
This will enter the container with a linux shell opened.
|
187
146
|
|
188
|
-
|
147
|
+
```shell
|
148
|
+
/usr/src/app #
|
149
|
+
```
|
189
150
|
|
190
|
-
|
191
|
-
client.get_storage("Sudo", "Key")
|
192
|
-
client.get_storage("Balances", "TotalIssuance")
|
193
|
-
client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"])
|
194
|
-
client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"])
|
195
|
-
```
|
151
|
+
3. Type `rspec` to run all tests
|
196
152
|
|
197
|
-
|
153
|
+
```shell
|
154
|
+
/usr/src/app # rspec
|
155
|
+
...................
|
156
|
+
|
157
|
+
Finished in 0.00883 seconds (files took 0.09656 seconds to load)
|
158
|
+
5 examples, 0 failures
|
159
|
+
```
|
198
160
|
|
199
|
-
|
200
|
-
compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }
|
201
|
-
```
|
161
|
+
4. Or, type `./bin/console` to enter the ruby interactive environment and run any decode or encode code
|
202
162
|
|
163
|
+
```shell
|
164
|
+
/usr/src/app # ./bin/console
|
165
|
+
[1] pry(main)> client = SubstrateClient.new("wss://kusama-rpc.polkadot.io/")
|
166
|
+
=> #<SubstrateClient:0x000055a78f124f58 ...
|
167
|
+
[2] pry(main)> client.methods
|
168
|
+
=> ...
|
169
|
+
[3] pry(main)> client.chain_getHead
|
170
|
+
=> "0xb3c3a220d4639b7c62f179f534b3a66336a115ebc18f13db053f0c57437c45fc"
|
171
|
+
```
|
203
172
|
|
204
173
|
|
205
174
|
## Development
|
data/exe/metadata
CHANGED
@@ -3,11 +3,10 @@
|
|
3
3
|
require "substrate_client"
|
4
4
|
require "json"
|
5
5
|
|
6
|
-
|
7
|
-
url = ARGV[0] || "wss://cc3-5.kusama.network/"
|
8
|
-
block_hash = ARGV[1]
|
9
|
-
|
6
|
+
url = ARGV[0] || "wss://kusama-rpc.polkadot.io"
|
10
7
|
client = SubstrateClient.new(url)
|
11
|
-
|
8
|
+
|
9
|
+
block_hash = ARGV[1] || client.chain_getFinalisedHead
|
10
|
+
|
12
11
|
metadata = client.get_metadata(block_hash)
|
13
|
-
puts JSON.pretty_generate(metadata.value)
|
12
|
+
puts JSON.pretty_generate(metadata.value.to_human)
|
data/lib/helper.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
|
2
|
+
class SubstrateClient::Helper
|
3
|
+
class << self
|
4
|
+
def generate_storage_key_from_metadata(metadata, module_name, storage_name, params = nil)
|
5
|
+
# find the storage item from metadata
|
6
|
+
metadata_modules = metadata.value.value[:metadata][:modules]
|
7
|
+
metadata_module = metadata_modules.detect { |mm| mm[:name] == module_name }
|
8
|
+
raise "Module '#{module_name}' not exist" unless metadata_module
|
9
|
+
storage_item = metadata_module[:storage][:items].detect { |item| item[:name] == storage_name }
|
10
|
+
raise "Storage item '#{storage_name}' not exist. \n#{metadata_module.inspect}" unless storage_item
|
11
|
+
|
12
|
+
if storage_item[:type][:Plain]
|
13
|
+
return_type = storage_item[:type][:Plain]
|
14
|
+
elsif map = storage_item[:type][:Map]
|
15
|
+
raise "Storage call of type \"Map\" requires 1 parameter" if params.nil? || params.length != 1
|
16
|
+
|
17
|
+
hasher = map[:hasher]
|
18
|
+
return_type = map[:value]
|
19
|
+
# TODO: decode to account id if param is address
|
20
|
+
# params[0] = decode(params[0]) if map[:key] == "AccountId"
|
21
|
+
type = Scale::Types.get(map[:key])
|
22
|
+
params[0] = type.new(params[0]).encode
|
23
|
+
elsif map = storage_item[:type][:DoubleMap]
|
24
|
+
raise "Storage call of type \"DoubleMapType\" requires 2 parameters" if params.nil? || params.length != 2
|
25
|
+
|
26
|
+
hasher = map[:hasher]
|
27
|
+
hasher2 = map[:key2Hasher]
|
28
|
+
return_type = map[:value]
|
29
|
+
params[0] = Scale::Types.get(map[:key1]).new(params[0]).encode
|
30
|
+
params[1] = Scale::Types.get(map[:key2]).new(params[1]).encode
|
31
|
+
else
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
storage_key = generate_storage_key(
|
36
|
+
module_name,
|
37
|
+
storage_name,
|
38
|
+
params,
|
39
|
+
hasher,
|
40
|
+
hasher2,
|
41
|
+
metadata.value.value[:metadata][:version]
|
42
|
+
)
|
43
|
+
[storage_key, return_type]
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_storage_key(module_name, storage_name, params = nil, hasher = nil, hasher2 = nil, metadata_version = nil)
|
47
|
+
if metadata_version and metadata_version >= 9
|
48
|
+
storage_key = Crypto.twox128(module_name) + Crypto.twox128(storage_name)
|
49
|
+
|
50
|
+
params&.each_with_index do |param, index|
|
51
|
+
if index == 0
|
52
|
+
param_hasher = hasher
|
53
|
+
elsif index == 1
|
54
|
+
param_hasher = hasher2
|
55
|
+
else
|
56
|
+
raise "Unexpected third parameter for storage call"
|
57
|
+
end
|
58
|
+
|
59
|
+
param_key = param.hex_to_bytes
|
60
|
+
param_hasher = "Twox128" if param_hasher.nil?
|
61
|
+
storage_key += Crypto.send param_hasher.underscore, param_key
|
62
|
+
end
|
63
|
+
|
64
|
+
"0x#{storage_key}"
|
65
|
+
else
|
66
|
+
# TODO: add test
|
67
|
+
storage_key = module_name + " " + storage_name
|
68
|
+
|
69
|
+
unless params.nil?
|
70
|
+
params = [params] if params.class != ::Array
|
71
|
+
params_key = params.join("")
|
72
|
+
hasher = "Twox128" if hasher.nil?
|
73
|
+
storage_key += params_key.hex_to_bytes.bytes_to_utf8
|
74
|
+
end
|
75
|
+
|
76
|
+
"0x#{Crypto.send( hasher.underscore, storage_key )}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def compose_call_from_metadata(metadata, module_name, call_name, params)
|
81
|
+
call = metadata.get_module_call(module_name, call_name)
|
82
|
+
|
83
|
+
value = {
|
84
|
+
call_index: call[:lookup],
|
85
|
+
module_name: module_name,
|
86
|
+
call_name: call_name,
|
87
|
+
params: []
|
88
|
+
}
|
89
|
+
|
90
|
+
params.keys.each_with_index do |call_param_name, i|
|
91
|
+
param_value = params[call_param_name]
|
92
|
+
value[:params] << {
|
93
|
+
name: call_param_name.to_s,
|
94
|
+
type: call[:args][i][:type],
|
95
|
+
value: param_value
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
Scale::Types::Extrinsic.new(value).encode
|
100
|
+
end
|
101
|
+
|
102
|
+
def decode_block(block)
|
103
|
+
block["block"]["header"]["number"] = block["block"]["header"]["number"].to_i(16)
|
104
|
+
|
105
|
+
block["block"]["extrinsics"].each_with_index do |hex, i|
|
106
|
+
scale_bytes = Scale::Bytes.new(hex)
|
107
|
+
block["block"]["extrinsics"][i] = Scale::Types::Extrinsic.decode(scale_bytes).to_human
|
108
|
+
end
|
109
|
+
|
110
|
+
block['block']['header']["digest"]["logs"].each_with_index do |hex, i|
|
111
|
+
scale_bytes = Scale::Bytes.new(hex)
|
112
|
+
block['block']['header']["digest"]["logs"][i] = Scale::Types::LogDigest.decode(scale_bytes).to_human
|
113
|
+
end
|
114
|
+
|
115
|
+
block
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
data/lib/substrate_client.rb
CHANGED
@@ -1,30 +1,46 @@
|
|
1
1
|
require "substrate_client/version"
|
2
2
|
|
3
|
+
require "logger"
|
3
4
|
require "scale.rb"
|
4
|
-
require "faye/websocket"
|
5
|
-
require "eventmachine"
|
6
5
|
require "json"
|
7
6
|
require "active_support"
|
8
7
|
require "active_support/core_ext/string"
|
8
|
+
require "helper"
|
9
|
+
require 'kontena-websocket-client'
|
10
|
+
|
11
|
+
def ws_request(url, payload)
|
12
|
+
result = nil
|
13
|
+
Kontena::Websocket::Client.connect(url, {}) do |client|
|
14
|
+
client.send(payload.to_json)
|
15
|
+
|
16
|
+
client.read do |message|
|
17
|
+
result = JSON.parse message
|
18
|
+
client.close(1000)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
return result
|
23
|
+
rescue Kontena::Websocket::CloseError => e
|
24
|
+
raise SubstrateClient::WebsocketError, e.reason
|
25
|
+
rescue Kontena::Websocket::Error => e
|
26
|
+
raise SubstrateClient::WebsocketError, e.reason
|
27
|
+
end
|
9
28
|
|
10
29
|
class SubstrateClient
|
30
|
+
class WebsocketError < StandardError; end
|
11
31
|
class RpcError < StandardError; end
|
32
|
+
class RpcTimeout < StandardError; end
|
12
33
|
|
13
34
|
attr_accessor :spec_name, :spec_version, :metadata
|
14
|
-
attr_reader :ws
|
15
35
|
|
16
|
-
def initialize(url
|
36
|
+
def initialize(url, spec_name: nil)
|
17
37
|
@url = url
|
18
38
|
@request_id = 1
|
19
39
|
@spec_name = spec_name
|
20
|
-
Scale::TypeRegistry.instance.load(spec_name)
|
21
|
-
|
22
|
-
init_ws
|
40
|
+
Scale::TypeRegistry.instance.load(spec_name: spec_name)
|
23
41
|
end
|
24
42
|
|
25
|
-
def request(method, params
|
26
|
-
queue = Queue.new
|
27
|
-
|
43
|
+
def request(method, params)
|
28
44
|
payload = {
|
29
45
|
"jsonrpc" => "2.0",
|
30
46
|
"method" => method,
|
@@ -32,15 +48,7 @@ class SubstrateClient
|
|
32
48
|
"id" => @request_id
|
33
49
|
}
|
34
50
|
|
35
|
-
|
36
|
-
@ws.send(payload.to_json)
|
37
|
-
@request_id += 1
|
38
|
-
data = queue.pop
|
39
|
-
|
40
|
-
if not subscription_callback.nil? && data["result"]
|
41
|
-
@subscription_callbacks[data["result"]] = subscription_callback
|
42
|
-
end
|
43
|
-
|
51
|
+
data = ws_request(@url, payload)
|
44
52
|
if data["error"]
|
45
53
|
raise RpcError, data["error"]
|
46
54
|
else
|
@@ -48,17 +56,9 @@ class SubstrateClient
|
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
51
|
-
def init_runtime(block_hash
|
52
|
-
if block_hash.nil?
|
53
|
-
if not block_id.nil?
|
54
|
-
block_hash = self.chain_get_block_hash(block_id)
|
55
|
-
else
|
56
|
-
block_hash = self.chain_get_head
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
59
|
+
def init_runtime(block_hash=nil)
|
60
60
|
# set current runtime spec version
|
61
|
-
runtime_version = self.
|
61
|
+
runtime_version = self.state_getRuntimeVersion(block_hash)
|
62
62
|
@spec_version = runtime_version["specVersion"]
|
63
63
|
Scale::TypeRegistry.instance.spec_version = @spec_version
|
64
64
|
|
@@ -73,349 +73,73 @@ class SubstrateClient
|
|
73
73
|
request(method, params)
|
74
74
|
end
|
75
75
|
|
76
|
-
def rpc_method(method_name)
|
77
|
-
SubstrateClient.real_method_name(method_name.to_s)
|
78
|
-
end
|
79
|
-
|
80
76
|
# ################################################
|
81
77
|
# origin rpc methods
|
82
78
|
# ################################################
|
83
79
|
def method_missing(method, *args)
|
84
|
-
|
85
|
-
invoke rpc_method, *args
|
86
|
-
end
|
87
|
-
|
88
|
-
def rpc_methods
|
89
|
-
invoke rpc_method(__method__)
|
90
|
-
end
|
91
|
-
|
92
|
-
def chain_get_head
|
93
|
-
invoke rpc_method(__method__)
|
94
|
-
end
|
95
|
-
|
96
|
-
def chain_get_finalised_head
|
97
|
-
invoke rpc_method(__method__)
|
98
|
-
end
|
99
|
-
|
100
|
-
def chain_get_header(block_hash = nil)
|
101
|
-
invoke rpc_method(__method__), block_hash
|
102
|
-
end
|
103
|
-
|
104
|
-
def chain_get_block(block_hash = nil)
|
105
|
-
invoke rpc_method(__method__), block_hash
|
106
|
-
end
|
107
|
-
|
108
|
-
def chain_get_block_hash(block_id)
|
109
|
-
invoke rpc_method(__method__), block_id
|
110
|
-
end
|
111
|
-
|
112
|
-
def chain_get_runtime_version(block_hash = nil)
|
113
|
-
invoke rpc_method(__method__), block_hash
|
114
|
-
end
|
115
|
-
|
116
|
-
def state_get_metadata(block_hash = nil)
|
117
|
-
invoke rpc_method(__method__), block_hash
|
118
|
-
end
|
119
|
-
|
120
|
-
def state_get_storage(storage_key, block_hash = nil)
|
121
|
-
invoke rpc_method(__method__), storage_key, block_hash
|
122
|
-
end
|
123
|
-
|
124
|
-
def system_name
|
125
|
-
invoke rpc_method(__method__)
|
126
|
-
end
|
127
|
-
|
128
|
-
def system_version
|
129
|
-
invoke rpc_method(__method__)
|
130
|
-
end
|
131
|
-
|
132
|
-
def chain_subscribe_all_heads(&callback)
|
133
|
-
request rpc_method(__method__), [], callback
|
134
|
-
end
|
135
|
-
|
136
|
-
def chain_unsubscribe_all_heads(subscription)
|
137
|
-
invoke rpc_method(__method__), subscription
|
138
|
-
end
|
139
|
-
|
140
|
-
def chain_subscribe_new_heads(&callback)
|
141
|
-
request rpc_method(__method__), [], callback
|
142
|
-
end
|
143
|
-
|
144
|
-
def chain_unsubscribe_new_heads(subscription)
|
145
|
-
invoke rpc_method(__method__), subscription
|
146
|
-
end
|
147
|
-
|
148
|
-
def chain_subscribe_finalized_heads(&callback)
|
149
|
-
request rpc_method(__method__), [], callback
|
150
|
-
end
|
151
|
-
|
152
|
-
def chain_unsubscribe_finalized_heads(subscription)
|
153
|
-
invoke rpc_method(__method__), subscription
|
154
|
-
end
|
155
|
-
|
156
|
-
def state_subscribe_runtime_version(&callback)
|
157
|
-
request rpc_method(__method__), [], callback
|
158
|
-
end
|
159
|
-
|
160
|
-
def state_unsubscribe_runtime_version(subscription)
|
161
|
-
invoke rpc_method(__method__), subscription
|
162
|
-
end
|
163
|
-
|
164
|
-
def state_subscribe_storage(keys, &callback)
|
165
|
-
request rpc_method(__method__), [keys], callback
|
166
|
-
end
|
167
|
-
|
168
|
-
def state_unsubscribe_storage(subscription)
|
169
|
-
invoke rpc_method(__method__), subscription
|
80
|
+
invoke method, *args
|
170
81
|
end
|
171
82
|
|
172
83
|
# ################################################
|
173
84
|
# custom methods based on origin rpc methods
|
174
85
|
# ################################################
|
175
|
-
def
|
176
|
-
|
86
|
+
def methods
|
87
|
+
invoke("rpc_methods")["methods"]
|
177
88
|
end
|
178
89
|
|
179
90
|
def get_block_number(block_hash)
|
180
|
-
header = self.
|
91
|
+
header = self.chain_getHeader(block_hash)
|
181
92
|
header["number"].to_i(16)
|
182
93
|
end
|
183
94
|
|
184
|
-
def get_metadata(block_hash)
|
185
|
-
hex = self.
|
95
|
+
def get_metadata(block_hash=nil)
|
96
|
+
hex = self.state_getMetadata(block_hash)
|
186
97
|
Scale::Types::Metadata.decode(Scale::Bytes.new(hex))
|
187
98
|
end
|
188
99
|
|
189
100
|
def get_block(block_hash=nil)
|
190
|
-
self.init_runtime(block_hash
|
191
|
-
block = self.
|
192
|
-
|
193
|
-
block["block"]["header"]["number"] = block["block"]["header"]["number"].to_i(16)
|
194
|
-
|
195
|
-
block["block"]["extrinsics"].each_with_index do |hex, i|
|
196
|
-
scale_bytes = Scale::Bytes.new(hex)
|
197
|
-
block["block"]["extrinsics"][i] = Scale::Types::Extrinsic.decode(scale_bytes).to_human
|
198
|
-
end
|
199
|
-
|
200
|
-
block['block']['header']["digest"]["logs"].each_with_index do |hex, i|
|
201
|
-
scale_bytes = Scale::Bytes.new(hex)
|
202
|
-
block['block']['header']["digest"]["logs"][i] = Scale::Types::LogDigest.decode(scale_bytes).to_human
|
203
|
-
end
|
204
|
-
|
205
|
-
block
|
101
|
+
self.init_runtime(block_hash)
|
102
|
+
block = self.chain_getBlock(block_hash)
|
103
|
+
SubstrateClient::Helper.decode_block(block)
|
206
104
|
end
|
207
105
|
|
208
|
-
def get_block_events(block_hash)
|
209
|
-
self.init_runtime(block_hash
|
106
|
+
def get_block_events(block_hash=nil)
|
107
|
+
self.init_runtime(block_hash)
|
210
108
|
|
211
109
|
storage_key = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"
|
212
|
-
events_data =
|
110
|
+
events_data = state_getStorage storage_key, block_hash
|
213
111
|
|
214
112
|
scale_bytes = Scale::Bytes.new(events_data)
|
215
113
|
Scale::Types.get("Vec<EventRecord>").decode(scale_bytes).to_human
|
216
114
|
end
|
217
115
|
|
218
|
-
def subscribe_block_events(&callback)
|
219
|
-
self.chain_subscribe_finalised_heads do |data|
|
220
|
-
|
221
|
-
block_number = data["params"]["result"]["number"].to_i(16) - 1
|
222
|
-
block_hash = data["params"]["result"]["parentHash"]
|
223
|
-
|
224
|
-
EM.defer(
|
225
|
-
|
226
|
-
proc {
|
227
|
-
events = get_block_events block_hash
|
228
|
-
{ block_number: block_number, events: events }
|
229
|
-
},
|
230
|
-
|
231
|
-
proc { |result|
|
232
|
-
begin
|
233
|
-
callback.call result
|
234
|
-
rescue => ex
|
235
|
-
puts ex.message
|
236
|
-
puts ex.backtrace.join("\n")
|
237
|
-
end
|
238
|
-
},
|
239
|
-
|
240
|
-
proc { |e|
|
241
|
-
puts e
|
242
|
-
}
|
243
|
-
|
244
|
-
)
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
116
|
# Plain: client.get_storage("Sudo", "Key")
|
249
117
|
# Plain: client.get_storage("Balances", "TotalIssuance")
|
250
118
|
# Map: client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"])
|
251
119
|
# DoubleMap: client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"])
|
252
120
|
def get_storage(module_name, storage_name, params = nil, block_hash = nil)
|
253
|
-
self.init_runtime(block_hash
|
121
|
+
self.init_runtime(block_hash)
|
254
122
|
|
255
|
-
|
256
|
-
metadata_modules = metadata.value.value[:metadata][:modules]
|
257
|
-
metadata_module = metadata_modules.detect { |mm| mm[:name] == module_name }
|
258
|
-
raise "Module '#{module_name}' not exist" unless metadata_module
|
259
|
-
storage_item = metadata_module[:storage][:items].detect { |item| item[:name] == storage_name }
|
260
|
-
raise "Storage item '#{storage_name}' not exist. \n#{metadata_module.inspect}" unless storage_item
|
123
|
+
storage_key, return_type = SubstrateClient::Helper.generate_storage_key_from_metadata(@metadata, module_name, storage_name, params)
|
261
124
|
|
262
|
-
|
263
|
-
return_type = storage_item[:type][:Plain]
|
264
|
-
elsif map = storage_item[:type][:Map]
|
265
|
-
raise "Storage call of type \"Map\" requires 1 parameter" if params.nil? || params.length != 1
|
266
|
-
|
267
|
-
hasher = map[:hasher]
|
268
|
-
return_type = map[:value]
|
269
|
-
# TODO: decode to account id if param is address
|
270
|
-
# params[0] = decode(params[0]) if map[:key] == "AccountId"
|
271
|
-
params[0] = Scale::Types.get(map[:key]).new(params[0]).encode
|
272
|
-
elsif map = storage_item[:type][:DoubleMap]
|
273
|
-
raise "Storage call of type \"DoubleMapType\" requires 2 parameters" if params.nil? || params.length != 2
|
274
|
-
|
275
|
-
hasher = map[:hasher]
|
276
|
-
hasher2 = map[:key2Hasher]
|
277
|
-
return_type = map[:value]
|
278
|
-
params[0] = Scale::Types.get(map[:key1]).new(params[0]).encode
|
279
|
-
params[1] = Scale::Types.get(map[:key2]).new(params[1]).encode
|
280
|
-
else
|
281
|
-
raise NotImplementedError
|
282
|
-
end
|
283
|
-
|
284
|
-
storage_hash = SubstrateClient.generate_storage_hash(
|
285
|
-
module_name,
|
286
|
-
storage_name,
|
287
|
-
params,
|
288
|
-
hasher,
|
289
|
-
hasher2,
|
290
|
-
metadata.value.value[:metadata][:version]
|
291
|
-
)
|
292
|
-
|
293
|
-
result = self.state_get_storage(storage_hash, block_hash)
|
125
|
+
result = self.state_getStorage(storage_key, block_hash)
|
294
126
|
return unless result
|
295
127
|
Scale::Types.get(return_type).decode(Scale::Bytes.new(result))
|
296
128
|
end
|
297
129
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
call = metadata.get_module_call(module_name, call_name)
|
303
|
-
|
304
|
-
value = {
|
305
|
-
call_index: call[:lookup],
|
306
|
-
module_name: module_name,
|
307
|
-
call_name: call_name,
|
308
|
-
params: []
|
309
|
-
}
|
310
|
-
|
311
|
-
params.keys.each_with_index do |call_param_name, i|
|
312
|
-
param_value = params[call_param_name]
|
313
|
-
value[:params] << {
|
314
|
-
name: call_param_name.to_s,
|
315
|
-
type: call[:args][i][:type],
|
316
|
-
value: param_value
|
317
|
-
}
|
318
|
-
end
|
319
|
-
|
320
|
-
Scale::Types::Extrinsic.new(value).encode
|
321
|
-
end
|
322
|
-
|
323
|
-
class << self
|
324
|
-
def generate_storage_hash(module_name, storage_name, params = nil, hasher = nil, hasher2 = nil, metadata_version = nil)
|
325
|
-
if metadata_version and metadata_version >= 9
|
326
|
-
storage_hash = Crypto.twox128(module_name) + Crypto.twox128(storage_name)
|
327
|
-
|
328
|
-
params&.each_with_index do |param, index|
|
329
|
-
if index == 0
|
330
|
-
param_hasher = hasher
|
331
|
-
elsif index == 1
|
332
|
-
param_hasher = hasher2
|
333
|
-
else
|
334
|
-
raise "Unexpected third parameter for storage call"
|
335
|
-
end
|
336
|
-
|
337
|
-
param_key = param.hex_to_bytes
|
338
|
-
param_hasher = "Twox128" if param_hasher.nil?
|
339
|
-
storage_hash += Crypto.send param_hasher.underscore, param_key
|
340
|
-
end
|
341
|
-
|
342
|
-
"0x#{storage_hash}"
|
343
|
-
else
|
344
|
-
# TODO: add test
|
345
|
-
storage_hash = module_name + " " + storage_name
|
346
|
-
|
347
|
-
unless params.nil?
|
348
|
-
params = [params] if params.class != ::Array
|
349
|
-
params_key = params.join("")
|
350
|
-
hasher = "Twox128" if hasher.nil?
|
351
|
-
storage_hash += params_key.hex_to_bytes.bytes_to_utf8
|
352
|
-
end
|
353
|
-
|
354
|
-
"0x#{Crypto.send( hasher.underscore, storage_hash )}"
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
# chain_unsubscribe_runtime_version
|
359
|
-
# =>
|
360
|
-
# chain_unsubscribeRuntimeVersion
|
361
|
-
def real_method_name(method_name)
|
362
|
-
segments = method_name.to_s.split("_")
|
363
|
-
segments[0] + "_" + segments[1] + segments[2..].map(&:capitalize).join
|
364
|
-
end
|
365
|
-
|
130
|
+
def generate_storage_key(module_name, storage_name, params = nil, block_hash = nil)
|
131
|
+
self.init_runtime(block_hash)
|
132
|
+
SubstrateClient::Helper.generate_storage_key_from_metadata(@metadata, module_name, storage_name, params)
|
366
133
|
end
|
367
134
|
|
368
|
-
|
369
|
-
def
|
370
|
-
|
371
|
-
|
372
|
-
Thread.new do
|
373
|
-
EM.run do
|
374
|
-
start_connection
|
375
|
-
queue << "ok"
|
376
|
-
end
|
377
|
-
end
|
378
|
-
|
379
|
-
if queue.pop
|
380
|
-
Thread.new do
|
381
|
-
loop do
|
382
|
-
if @ws && @ws.ready_state == 3
|
383
|
-
puts "try to reconnect"
|
384
|
-
start_connection
|
385
|
-
end
|
386
|
-
|
387
|
-
sleep(3)
|
388
|
-
end
|
389
|
-
end
|
390
|
-
end
|
135
|
+
# compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }
|
136
|
+
def compose_call(module_name, call_name, params, block_hash=nil)
|
137
|
+
self.init_runtime(block_hash)
|
138
|
+
SubstrateClient::Helper.compose_call_from_metadata(@metadata, module_name, call_name, params)
|
391
139
|
end
|
392
140
|
|
393
|
-
def
|
394
|
-
|
395
|
-
@subscription_callbacks = {}
|
396
|
-
|
397
|
-
@ws = Faye::WebSocket::Client.new(@url)
|
398
|
-
@ws.on :message do |event|
|
399
|
-
# p [:message, event.data]
|
400
|
-
if event.data.include?("jsonrpc")
|
401
|
-
begin
|
402
|
-
data = JSON.parse event.data
|
403
|
-
|
404
|
-
if data["params"]
|
405
|
-
if @subscription_callbacks[data["params"]["subscription"]]
|
406
|
-
@subscription_callbacks[data["params"]["subscription"]].call data
|
407
|
-
end
|
408
|
-
else
|
409
|
-
@callbacks[data["id"]].call data
|
410
|
-
@callbacks.delete(data["id"])
|
411
|
-
end
|
412
|
-
|
413
|
-
rescue => ex
|
414
|
-
puts ex.message
|
415
|
-
puts ex.backtrace.join("\n")
|
416
|
-
end
|
417
|
-
end
|
418
|
-
end
|
141
|
+
def generate_storage_hash_from_data(storage_hex_data)
|
142
|
+
"0x" + Crypto.blake2_256(Scale::Bytes.new(storage_hex_data).bytes)
|
419
143
|
end
|
420
144
|
|
421
145
|
end
|
data/substrate_client.gemspec
CHANGED
@@ -36,10 +36,10 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
37
|
spec.require_paths = ["lib"]
|
38
38
|
|
39
|
-
spec.add_dependency "faye-websocket", "~> 0.10.9"
|
40
|
-
spec.add_dependency "eventmachine", "~> 1.2.7"
|
41
39
|
spec.add_dependency "activesupport", "~> 5.2.4"
|
42
|
-
spec.add_dependency "scale.rb", "~> 0.2.
|
40
|
+
spec.add_dependency "scale.rb", "~> 0.2.12"
|
41
|
+
spec.add_dependency "kontena-websocket-client", "~> 0.1.1"
|
42
|
+
spec.add_dependency "substrate_common.rb", "~> 0.1.9"
|
43
43
|
|
44
44
|
spec.add_development_dependency "bundler", "~> 1.17"
|
45
45
|
spec.add_development_dependency "rake", ">= 12.3.3"
|
metadata
CHANGED
@@ -1,71 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: substrate_client.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wu Minzhe
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.2.4
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 5.2.4
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: scale.rb
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.2.12
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 0.2.12
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: kontena-websocket-client
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.1.1
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.1.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: substrate_common.rb
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
61
|
+
version: 0.1.9
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
68
|
+
version: 0.1.9
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: bundler
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,6 +134,7 @@ files:
|
|
134
134
|
- ".rspec"
|
135
135
|
- ".travis.yml"
|
136
136
|
- CODE_OF_CONDUCT.md
|
137
|
+
- Dockerfile
|
137
138
|
- Gemfile
|
138
139
|
- Gemfile.lock
|
139
140
|
- LICENSE.txt
|
@@ -142,6 +143,7 @@ files:
|
|
142
143
|
- bin/console
|
143
144
|
- bin/setup
|
144
145
|
- exe/metadata
|
146
|
+
- lib/helper.rb
|
145
147
|
- lib/substrate_client.rb
|
146
148
|
- lib/substrate_client/version.rb
|
147
149
|
- substrate_client.gemspec
|