iost_sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/lib/iost_sdk/crypto.rb +144 -0
  3. data/lib/iost_sdk/errors.rb +10 -0
  4. data/lib/iost_sdk/http/client.rb +58 -0
  5. data/lib/iost_sdk/http/get_account.rb +17 -0
  6. data/lib/iost_sdk/http/get_block_by_hash.rb +17 -0
  7. data/lib/iost_sdk/http/get_block_by_number.rb +17 -0
  8. data/lib/iost_sdk/http/get_chain_info.rb +17 -0
  9. data/lib/iost_sdk/http/get_contract.rb +17 -0
  10. data/lib/iost_sdk/http/get_contract_storage.rb +25 -0
  11. data/lib/iost_sdk/http/get_contract_storage_fields.rb +25 -0
  12. data/lib/iost_sdk/http/get_gas_ratio.rb +17 -0
  13. data/lib/iost_sdk/http/get_node_info.rb +17 -0
  14. data/lib/iost_sdk/http/get_ram_info.rb +17 -0
  15. data/lib/iost_sdk/http/get_token_balance.rb +17 -0
  16. data/lib/iost_sdk/http/get_tx_by_hash.rb +17 -0
  17. data/lib/iost_sdk/http/get_tx_receipt_by_tx_hash.rb +17 -0
  18. data/lib/iost_sdk/http/http_request_error.rb +11 -0
  19. data/lib/iost_sdk/http/send_tx.rb +26 -0
  20. data/lib/iost_sdk/models/abi.rb +20 -0
  21. data/lib/iost_sdk/models/account.rb +32 -0
  22. data/lib/iost_sdk/models/account_ram_info.rb +19 -0
  23. data/lib/iost_sdk/models/action.rb +24 -0
  24. data/lib/iost_sdk/models/amount_limit.rb +22 -0
  25. data/lib/iost_sdk/models/block.rb +30 -0
  26. data/lib/iost_sdk/models/block_info.rb +19 -0
  27. data/lib/iost_sdk/models/chain_info.rb +25 -0
  28. data/lib/iost_sdk/models/contract.rb +22 -0
  29. data/lib/iost_sdk/models/frozen_balance.rb +18 -0
  30. data/lib/iost_sdk/models/gas_info.rb +23 -0
  31. data/lib/iost_sdk/models/gas_ratio.rb +18 -0
  32. data/lib/iost_sdk/models/info.rb +19 -0
  33. data/lib/iost_sdk/models/network_info.rb +20 -0
  34. data/lib/iost_sdk/models/node_info.rb +21 -0
  35. data/lib/iost_sdk/models/peer_info.rb +18 -0
  36. data/lib/iost_sdk/models/permission.rb +20 -0
  37. data/lib/iost_sdk/models/permission_group.rb +19 -0
  38. data/lib/iost_sdk/models/permission_item.rb +20 -0
  39. data/lib/iost_sdk/models/pledge_info.rb +18 -0
  40. data/lib/iost_sdk/models/query/contract_storage.rb +24 -0
  41. data/lib/iost_sdk/models/query/contract_storage_fields.rb +23 -0
  42. data/lib/iost_sdk/models/query/signed_transaction.rb +114 -0
  43. data/lib/iost_sdk/models/query/transaction.rb +75 -0
  44. data/lib/iost_sdk/models/ram_info.rb +21 -0
  45. data/lib/iost_sdk/models/receipt.rb +18 -0
  46. data/lib/iost_sdk/models/signature.rb +42 -0
  47. data/lib/iost_sdk/models/token_balance.rb +19 -0
  48. data/lib/iost_sdk/models/transaction.rb +32 -0
  49. data/lib/iost_sdk/models/transaction_info.rb +19 -0
  50. data/lib/iost_sdk/models/tx_receipt.rb +24 -0
  51. data/lib/iost_sdk/models/util/serializer.rb +52 -0
  52. data/lib/iost_sdk/models/vote_info.rb +19 -0
  53. data/lib/iost_sdk/models.rb +276 -0
  54. data/lib/iost_sdk/string.rb +11 -0
  55. data/lib/iost_sdk/version.rb +3 -0
  56. data/lib/iost_sdk.rb +141 -0
  57. metadata +255 -0
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'iost_sdk/models'
4
+ require 'iost_sdk/models/receipt'
5
+
6
+ module IOSTSdk
7
+ module Models
8
+ class TxReceipt
9
+ include Models
10
+
11
+ def self.attr_names
12
+ [
13
+ 'tx_hash',
14
+ 'gas_usage',
15
+ 'ram_usage',
16
+ 'status_code',
17
+ 'message',
18
+ 'returns',
19
+ 'receipts'
20
+ ]
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IOSTSdk
4
+ module Models
5
+ module Util
6
+ module Serializer
7
+ def self.int32_to_bytes(int32)
8
+ [int32].pack('L>').unpack('C*')
9
+ end
10
+
11
+ def self.int64_to_bytes(int64)
12
+ [int64].pack('Q>').unpack('C*')
13
+ end
14
+
15
+ def self.string_to_bytes(str)
16
+ int32_to_bytes(str.size) + str.unpack('C*')
17
+ end
18
+
19
+ def self.array_to_bytes(arr, size_prefix=true)
20
+ return int32_to_bytes(0) if !arr || arr.empty?
21
+
22
+ data_bytes = arr.map do |elem|
23
+ if elem.class.name == 'Integer'
24
+ int64_to_bytes(elem)
25
+ elsif elem.class.name == 'String'
26
+ string_to_bytes(elem)
27
+ elsif elem.class.name == 'Array'
28
+ array_to_bytes(elem)
29
+ elsif elem.class.name == 'Hash'
30
+ hash_to_bytes(elem)
31
+ elsif elem.respond_to?(:raw_data_bytes)
32
+ raw_data_bytes = array_to_bytes(elem.raw_data_bytes, false)
33
+ (int32_to_bytes(raw_data_bytes.size) + raw_data_bytes).flatten
34
+ end
35
+ end
36
+
37
+ data_bytes.flatten!
38
+
39
+ size_prefix ? int32_to_bytes(arr.size) + data_bytes : data_bytes
40
+ end
41
+
42
+ def self.hash_to_bytes(h)
43
+ return int32_to_bytes(0) if !h || h.empty?
44
+
45
+ # hash to array of key-value pairs, sorted by key
46
+ key_value_pairs = h.sort.map { |k, v| [k.to_s, v] }.flatten
47
+ int32_to_bytes(h.size) + array_to_bytes(key_value_pairs, false)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'iost_sdk/models'
4
+
5
+ module IOSTSdk
6
+ module Models
7
+ class VoteInfo
8
+ include Models
9
+
10
+ def self.attr_names
11
+ [
12
+ 'option',
13
+ 'votes',
14
+ 'cleared_votes'
15
+ ]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,276 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'iost_sdk/string'
5
+
6
+ module IOSTSdk
7
+ module Models
8
+ MODEL_REGISTRY = {
9
+ # query
10
+ 'IOSTSdk::Models::Query::ContractStorage' => {},
11
+ 'IOSTSdk::Models::Query::ContractStorageFields' => {},
12
+ 'IOSTSdk::Models::Query::Transaction' => {
13
+ 'actions' => {
14
+ mode: :list,
15
+ class: 'IOSTSdk::Models::Action'
16
+ },
17
+ 'amount_limit' => {
18
+ mode: :list,
19
+ class: 'IOSTSdk::Models::AmountLimit'
20
+ },
21
+ 'signatures' => {
22
+ mode: :list,
23
+ class: 'IOSTSdk::Models::Signature'
24
+ }
25
+ },
26
+ 'IOSTSdk::Models::Query::SignedTransaction' => {
27
+ 'actions' => {
28
+ mode: :list,
29
+ class: 'IOSTSdk::Models::Action'
30
+ },
31
+ 'amount_limit' => {
32
+ mode: :list,
33
+ class: 'IOSTSdk::Models::AmountLimit'
34
+ },
35
+ 'signatures' => {
36
+ mode: :list,
37
+ class: 'IOSTSdk::Models::Signature'
38
+ },
39
+ 'publisher_sigs' => {
40
+ mode: :list,
41
+ class: 'IOSTSdk::Models::Signature'
42
+ }
43
+ },
44
+ # result
45
+ 'IOSTSdk::Models::NodeInfo' => {
46
+ 'network' => {
47
+ mode: :object,
48
+ class: 'IOSTSdk::Models::NetworkInfo'
49
+ }
50
+ },
51
+ 'IOSTSdk::Models::NetworkInfo' => {
52
+ 'peer_info' => {
53
+ mode: :list,
54
+ class: 'IOSTSdk::Models::PeerInfo'
55
+ }
56
+ },
57
+ 'IOSTSdk::Models::Info' => {},
58
+ 'IOSTSdk::Models::VoteInfo' => {},
59
+ 'IOSTSdk::Models::PeerInfo' => {},
60
+ 'IOSTSdk::Models::ChainInfo' => {},
61
+ 'IOSTSdk::Models::GasRatio' => {},
62
+ 'IOSTSdk::Models::RAMInfo' => {},
63
+ 'IOSTSdk::Models::Action' => {},
64
+ 'IOSTSdk::Models::AmountLimit' => {},
65
+ 'IOSTSdk::Models::Receipt' => {},
66
+ 'IOSTSdk::Models::TxReceipt' => {
67
+ 'receipts' => {
68
+ mode: :list,
69
+ class: 'IOSTSdk::Models::Receipt'
70
+ }
71
+ },
72
+ 'IOSTSdk::Models::Transaction' => {
73
+ 'actions' => {
74
+ mode: :list,
75
+ class: 'IOSTSdk::Models::Action'
76
+ },
77
+ 'amount_limit' => {
78
+ mode: :list,
79
+ class: 'IOSTSdk::Models::AmountLimit'
80
+ },
81
+ 'tx_receipt' => {
82
+ mode: :object,
83
+ class: 'IOSTSdk::Models::TxReceipt'
84
+ }
85
+ },
86
+ 'IOSTSdk::Models::TransactionInfo' => {
87
+ 'transaction' => {
88
+ mode: :object,
89
+ class: 'IOSTSdk::Models::Transaction'
90
+ }
91
+ },
92
+ 'IOSTSdk::Models::Block' => {
93
+ 'transactions' => {
94
+ mode: :list,
95
+ class: 'IOSTSdk::Models::Transaction'
96
+ },
97
+ 'info' => {
98
+ mode: :object,
99
+ class: 'IOSTSdk::Models::Info'
100
+ }
101
+ },
102
+ 'IOSTSdk::Models::BlockInfo' => {
103
+ 'block' => {
104
+ mode: :object,
105
+ class: 'IOSTSdk::Models::Block'
106
+ }
107
+ },
108
+ 'IOSTSdk::Models::AccountRAMInfo' => {},
109
+ 'IOSTSdk::Models::FrozenBalance' => {},
110
+ 'IOSTSdk::Models::PledgeInfo' => {},
111
+ 'IOSTSdk::Models::GasInfo' => {
112
+ 'pledged_info' => {
113
+ mode: :list,
114
+ class: 'IOSTSdk::Models::PledgeInfo'
115
+ }
116
+ },
117
+ 'IOSTSdk::Models::PermissionItem' => {},
118
+ 'IOSTSdk::Models::PermissionGroup' => {
119
+ 'items' => {
120
+ mode: :list,
121
+ class: 'IOSTSdk::Models::PermissionItem'
122
+ }
123
+ },
124
+ 'IOSTSdk::Models::Permission' => {
125
+ 'items' => {
126
+ mode: :list,
127
+ class: 'IOSTSdk::Models::PermissionItem'
128
+ }
129
+ },
130
+ 'IOSTSdk::Models::Account' => {
131
+ 'gas_info' => {
132
+ mode: :object,
133
+ class: 'IOSTSdk::Models::GasInfo'
134
+ },
135
+ 'ram_info' => {
136
+ mode: :object,
137
+ class: 'IOSTSdk::Models::AccountRAMInfo'
138
+ },
139
+ 'permissions' => {
140
+ mode: :hash,
141
+ class: 'IOSTSdk::Models::Permission'
142
+ },
143
+ 'groups' => {
144
+ mode: :hash,
145
+ class: 'IOSTSdk::Models::PermissionGroup'
146
+ },
147
+ 'frozen_balances' => {
148
+ mode: :list,
149
+ class: 'IOSTSdk::Models::FrozenBalance'
150
+ },
151
+ 'vote_infos' => {
152
+ mode: :list,
153
+ class: 'IOSTSdk::Models::VoteInfo'
154
+ }
155
+ },
156
+ 'IOSTSdk::Models::TokenBalance' => {
157
+ 'frozen_balances' => {
158
+ mode: :list,
159
+ class: 'IOSTSdk::Models::FrozenBalance'
160
+ }
161
+ },
162
+ 'IOSTSdk::Models::ABI' => {
163
+ 'amount_limit' => {
164
+ mode: :list,
165
+ class: 'IOSTSdk::Models::AmountLimit'
166
+ }
167
+ },
168
+ 'IOSTSdk::Models::Contract' => {
169
+ 'abis' => {
170
+ mode: :list,
171
+ class: 'IOSTSdk::Models::ABI'
172
+ }
173
+ },
174
+ 'IOSTSdk::Models::Signature' => {}
175
+ }.freeze
176
+
177
+ def self.included base
178
+ base.send :include, InstanceMethods
179
+ base.extend ClassMethods
180
+ end
181
+
182
+ module ClassMethods
183
+ end
184
+
185
+ module InstanceMethods
186
+ require 'iost_sdk/errors'
187
+
188
+ # Creates an instance of a model from the JSON string.
189
+ #
190
+ # @param model_data [Hash] the JSON string of the model data
191
+ # @return an instance of +model_class+ if +model_data+ is valid.
192
+ def populate(model_data:)
193
+ # if nil, short-curcuit
194
+ return nil unless model_data
195
+
196
+ # the model class is expected implement "attr_names" method
197
+ model_attr_names = self.class.attr_names || []
198
+ # proceed ONLY if actual data has a subset of keys of what's defined by the class
199
+ unless Set.new(model_data.keys).subset?(Set.new(model_attr_names))
200
+ raise IOSTSdk::Errors::InvalidModelDataError.new(
201
+ self.class.name,
202
+ model_attr_names,
203
+ model_data.keys
204
+ )
205
+ end
206
+ # create the model
207
+ model_attr_names.each do |k|
208
+ v = model_data[k]
209
+ # set the instance var
210
+ instance_variable_set("@#{k}", parse(data_key: k, data_value: v))
211
+ # define the attr_reader
212
+ self.class.send(:define_method, k.to_sym) do
213
+ instance_variable_get("@#{k}")
214
+ end
215
+ end
216
+
217
+ self
218
+ end
219
+ end
220
+
221
+ def parse(data_key:, data_value:)
222
+ # if nil, short-curcuit
223
+ return nil unless data_value
224
+
225
+ if IOSTSdk::Models::MODEL_REGISTRY[self.class.name].has_key?(data_key)
226
+ instruction = IOSTSdk::Models::MODEL_REGISTRY[self.class.name][data_key]
227
+ mode = instruction[:mode]
228
+ class_name = instruction[:class]
229
+ clazz = IOSTSdk::String.classify(class_name)
230
+
231
+ case mode
232
+ when :object
233
+ clazz.new.populate(model_data: data_value)
234
+ when :list
235
+ data_value.empty? ? [] : data_value.map { |item| clazz.new.populate(model_data: item) }
236
+ when :hash
237
+ if data_value.empty?
238
+ {}
239
+ else
240
+ data_value.each_with_object({}) do |(v_key, v_value), memo|
241
+ memo[v_key] = clazz.new.populate(model_data: v_value)
242
+ end
243
+ end
244
+ end
245
+ else
246
+ data_value
247
+ end
248
+ end
249
+
250
+ def raw_data
251
+ raw_data = instance_variables.each_with_object({}) do |var_name, memo|
252
+ n = var_name.to_s[1..-1].to_sym
253
+ v = send(n)
254
+
255
+ final_v = if IOSTSdk::Models::MODEL_REGISTRY[self.class.name].has_key?(n.to_s)
256
+ mode = IOSTSdk::Models::MODEL_REGISTRY[self.class.name][n.to_s][:mode]
257
+ case mode
258
+ when :object
259
+ v.raw_data
260
+ when :list
261
+ v.map(&:raw_data)
262
+ when :hash
263
+ v.each_with_object({}) do |(v_key, v_value), memo|
264
+ memo[v_key] = v_value.raw_data
265
+ end
266
+ end
267
+ else
268
+ v
269
+ end
270
+ memo[n] = final_v
271
+ end
272
+
273
+ raw_data
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,11 @@
1
+ module IOSTSdk
2
+ module String
3
+ def self.camelize(str)
4
+ str.to_s.split('_').map(&:capitalize).join
5
+ end
6
+
7
+ def self.classify(fq_class_name)
8
+ fq_class_name.split('::').inject(Object) { |o, c| o.const_get c }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module IOSTSdk
2
+ VERSION = ENV['IOST_SDK_VERSION'] || '0.1.0'
3
+ end
data/lib/iost_sdk.rb ADDED
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'iost_sdk/version'
4
+ require 'iost_sdk/crypto'
5
+ require 'iost_sdk/errors'
6
+ require 'iost_sdk/http/client'
7
+ require 'iost_sdk/models/query/transaction'
8
+ require 'iost_sdk/models/query/signed_transaction'
9
+
10
+ module IOSTSdk
11
+ class Main
12
+ attr_accessor :gas_limit, :gas_ratio, :delay,
13
+ :expiration, :approval_limit_amount,
14
+ :transaction
15
+
16
+ DEFAULTS = {
17
+ gas_limit: 2_000_000,
18
+ gas_ratio: 1,
19
+ delay: 0,
20
+ expiration: 90,
21
+ approval_limit_amount: :unlimited
22
+ }.freeze
23
+
24
+ #
25
+ # @param endpoint [String] a URL of the JSON RPC endpoint of IOST
26
+ def initialize(endpoint:)
27
+ @endpoint = endpoint
28
+
29
+ DEFAULTS.each do |k, v|
30
+ instance_variable_set("@#{k}".to_sym, v)
31
+ end
32
+ end
33
+
34
+ def send(account_name:, key_pair:)
35
+ if @transaction
36
+ client = IOSTSdk::Http::Client.new(base_url: @endpoint)
37
+ client.send_tx(
38
+ transaction: @transaction,
39
+ account_name: account_name,
40
+ key_pair: key_pair
41
+ )
42
+ end
43
+ end
44
+
45
+ # Create an instance of IOSTSdk::Models::Transaction with an action to call the ABI.
46
+ #
47
+ # @param contract_id [String] a Contract's ID
48
+ # @param abi_name [String] the name of an ABI to call
49
+ # @param abi_args [any] args to the ABI
50
+ # @return a new instance of Transaction
51
+ def call_abi(contract_id:, abi_name:, abi_args:)
52
+ transaction = init_transaction
53
+ transaction.add_action(contract_id: contract_id, action_name: abi_name, action_data: abi_args)
54
+ transaction.add_approve(token: '*', amount: :unlimited)
55
+ transaction.set_time_params(expiration: expiration, delay: delay)
56
+
57
+ @transaction = transaction
58
+ self
59
+ end
60
+
61
+ # Create an instance IOSTSdk::Models::Transaction with an action to transfer tokens
62
+ #
63
+ # @param token [String] name of the token in the transfer
64
+ # @param from [String] sender
65
+ # @param to [String] recipient
66
+ # @param amount [Integer] amount to transfer
67
+ # @param memo [String] memo/notes for the transfer
68
+ # @return a new instance of Transaction
69
+ def transfer(token:, from:, to:, amount:, memo:)
70
+ call_abi(
71
+ contract_id: 'token.iost',
72
+ abi_name: :transfer,
73
+ abi_args: [token, from, to, amount, memo]
74
+ )
75
+ @transaction.add_approve(token: :iost, amount: amount)
76
+ @transaction.set_time_params(expiration: expiration, delay: delay)
77
+
78
+ self
79
+ end
80
+
81
+ # Create an instance IOSTSdk::Models::Transaction to create a new account
82
+ #
83
+ # @param name [String] the name of the account to be created
84
+ # @param creator [String] the name of the account that's creating a new account
85
+ # @param owner_key [IOSTSdk::Crypto::KeyPair] the owner key of the new account
86
+ # @param active_key [IOSTSdk::Crypto::KeyPair] the active key of the new account
87
+ # @param initial_ram [Integer] the initial RAM of the new account
88
+ # @param initial_gas_pledge [Integer] the initial gas pledge of the new account
89
+ def new_account(name:, creator:, owner_key:, active_key:, initial_ram:, initial_gas_pledge:)
90
+ transaction = init_transaction
91
+ transaction.add_action(
92
+ contract_id: 'auth.iost',
93
+ action_name: :signUp,
94
+ action_data: [name, owner_key.id, active_key.id]
95
+ )
96
+
97
+ if initial_ram > 10
98
+ transaction.add_action(
99
+ contract_id: 'ram.iost',
100
+ action_name: :buy,
101
+ action_data: [creator, name, initial_ram]
102
+ )
103
+ end
104
+
105
+ if initial_gas_pledge > 0
106
+ transaction.add_action(
107
+ contract_id: 'ram.iost',
108
+ action_name: :buy,
109
+ action_data: [creator, name, initial_gas_pledge.to_s]
110
+ )
111
+ end
112
+
113
+ transaction.set_time_params(expiration: expiration, delay: delay)
114
+ transaction.add_approve(token: '*', amount: approval_limit_amount)
115
+
116
+ @transaction = transaction
117
+ self
118
+ end
119
+
120
+ private
121
+
122
+ def init_transaction
123
+ time_now = Time.now.utc.to_i * 1_000_000
124
+ transaction = IOSTSdk::Models::Query::Transaction.new.populate(
125
+ model_data: {
126
+ 'time' => time_now,
127
+ 'expiration' => time_now + delay * 1_000_000,
128
+ 'gas_ratio' => gas_ratio,
129
+ 'gas_limit' => gas_limit,
130
+ 'delay' => delay,
131
+ 'chain_id' => 0,
132
+ 'signers' => [],
133
+ 'actions' => [],
134
+ 'amount_limit' => [],
135
+ 'signatures' => []
136
+ }
137
+ )
138
+ transaction
139
+ end
140
+ end
141
+ end