ever_sdk_client 1.36.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.
@@ -0,0 +1,367 @@
1
+ module EverSdk
2
+ module Debot
3
+
4
+ #
5
+ # types
6
+ #
7
+
8
+ module ErrorCode
9
+ START_FAILED = 801
10
+ FETCH_FAILED = 802
11
+ EXECUTION_FAILED = 803
12
+ INVALID_HANDLE = 804
13
+ INVALID_JSON_PARAMS = 805
14
+ INVALID_FUNCTION_ID = 806
15
+ INVALID_ABI = 807
16
+ GET_METHOD_FAILED = 808
17
+ INVALID_MSG = 809
18
+ EXTERNAL_CALL_FAILED = 810
19
+ BROWSER_CALLBACK_FAILED = 811
20
+ OPERATION_REJECTED = 812
21
+ DEBOT_NO_CODE = 813
22
+ end
23
+
24
+ DebotAction = KwStruct.new(:description, :name, :action_type, :to, :attributes, :misc) do
25
+ def to_h
26
+ {
27
+ description: description,
28
+ name: name,
29
+ action_type: action_type,
30
+ to: to,
31
+ attributes: attributes,
32
+ misc: misc
33
+ }
34
+ end
35
+
36
+ def self.from_json(j)
37
+ return if j.nil?
38
+
39
+ self.new(
40
+ description: j["description"],
41
+ name: j["name"],
42
+ action_type: j["action_type"],
43
+ to: j["to"],
44
+ attributes: j["attributes"],
45
+ misc: j["misc"]
46
+ )
47
+ end
48
+ end
49
+
50
+ DebotInfo = KwStruct.new(
51
+ :name,
52
+ :version,
53
+ :publisher,
54
+ :caption,
55
+ :author,
56
+ :support,
57
+ :hello,
58
+ :language,
59
+ :dabi,
60
+ :icon,
61
+ :interfaces,
62
+ :dabi_version
63
+ ) do
64
+ def initialize(
65
+ name: nil,
66
+ version: nil,
67
+ publisher: nil,
68
+ caption: nil,
69
+ author: nil,
70
+ support: nil,
71
+ hello: nil,
72
+ language: nil,
73
+ dabi: nil,
74
+ icon: nil,
75
+ interfaces: [],
76
+ dabi_version: nil
77
+ )
78
+ super
79
+ end
80
+ end
81
+
82
+ class DebotActivity
83
+ attr_reader :type, :msg, :dst, :out, :fee, :setcode, :signkey, :signing_box_handle
84
+ end
85
+
86
+ Spending = KwStruct.new(:amount, :dst)
87
+
88
+ ParamsOfInit = KwStruct.new(:address)
89
+
90
+ RegisteredDebot = KwStruct.new(:debot_handle, :debot_abi, :info) do
91
+ def to_h
92
+ {
93
+ debot_handle: debot_handle,
94
+ debot_abi: debot_abi,
95
+ info: info.to_h
96
+ }
97
+ end
98
+ end
99
+
100
+ class ParamsOfAppDebotBrowser
101
+ private_class_method :new
102
+
103
+ TYPE_VALUES = [
104
+ :log,
105
+ :switch,
106
+ :switch_completed,
107
+ :show_action,
108
+ :input,
109
+ :get_signing_box,
110
+ :invoke_debot,
111
+ :send,
112
+ :approve
113
+ ]
114
+
115
+ attr_reader :type, :msg, :context_id, :action, :prompt, :debot_addr, :message, :activity
116
+
117
+ def self.new_with_type_log(msg)
118
+ @type = :log
119
+ @msg = msg
120
+ end
121
+
122
+ def self.new_with_type_switch(context_id)
123
+ @type = :switch
124
+ @context_id = context_id
125
+ end
126
+
127
+ def self.new_with_type_switch_completed
128
+ @type = :switch_completed
129
+ end
130
+
131
+ def self.new_with_type_show_action(action)
132
+ @type = :show_action
133
+ @action = action
134
+ end
135
+
136
+ def self.new_with_type_input(prompt)
137
+ @type = :input
138
+ @prompt = prompt
139
+ end
140
+
141
+ def self.new_with_type_get_signing_box
142
+ @type = :get_signing_box
143
+ end
144
+
145
+ def self.new_with_type_invoke_debot(debot_addr, action)
146
+ @type = :invoke_debot
147
+ @debot_addr = debot_addr
148
+ @action = action
149
+ end
150
+
151
+ def self.new_with_type_send(message)
152
+ @type = :send
153
+ @message = message
154
+ end
155
+
156
+ def self.new_with_type_approve(activity)
157
+ @type = :approve
158
+ @activity = activity
159
+ end
160
+
161
+ def to_h
162
+ {
163
+ type: Helper.sym_to_capitalized_case_str(type),
164
+ msg: msg,
165
+ context_id: context_id,
166
+ action: action,
167
+ prompt: prompt,
168
+ debot_addr: debot_addr,
169
+ message: message
170
+ }
171
+ end
172
+
173
+ def self.from_json(j)
174
+ return nil if j.nil?
175
+
176
+ tp = self.parse_type(j["type"])
177
+ case tp
178
+ when :log
179
+ self.new_with_type_log(j["msg"])
180
+
181
+ when :switch
182
+ self.new_with_type_switch(j["context_id"])
183
+
184
+ when :switch_completed
185
+ self.new_with_type_switch_completed
186
+
187
+ when :show_action
188
+ self.new_with_type_show_action(DebotAction.from_json(j["action"]))
189
+
190
+ when :input
191
+ self.new_with_type_input(j["prompt"])
192
+
193
+ when :get_signing_box
194
+ self.new_with_type_get_signing_box
195
+
196
+ when :invoke_debot
197
+ self.new_with_type_invoke_debot(j["debot_addr"], DebotAction.from_json(j["action"]))
198
+
199
+ when :send
200
+ self.new_with_type_send(j["message"])
201
+
202
+ when :approve
203
+ self.new_with_type_send(DebotActivity.from_json(j["activity"]))
204
+
205
+ else
206
+ raise ArgumentError.new("no handler for type: #{tp}")
207
+ end
208
+ end
209
+
210
+ private
211
+
212
+ def self.parse_type(type_str)
213
+ parsed_type = Helper.capitalized_case_str_to_snake_case_sym(type_str)
214
+
215
+ unless TYPE_VALUES.include?(type_str)
216
+ raise ArgumentError.new("type #{type_str} is unknown; known types: #{TYPE_VALUES}")
217
+ end
218
+
219
+ parsed_type
220
+ end
221
+ end
222
+
223
+ class ResultOfAppDebotBrowser
224
+ private_class_method :new
225
+
226
+ attr_reader :type, :value, :signing_box, :is_approved
227
+
228
+ def self.new_with_type_input(a)
229
+ @type = :input
230
+ @value = a
231
+ end
232
+
233
+ def self.new_with_type_get_signing_box(a)
234
+ @type = :get_signing_box
235
+ @signing_box = signing_box
236
+ end
237
+
238
+ def self.new_with_type_invoke_debot
239
+ @type = :invoke_debot
240
+ end
241
+
242
+ def self.new_with_type_approve(a)
243
+ @type = :approve
244
+ @is_approved = a
245
+ end
246
+ end
247
+
248
+ ParamsOfStart = KwStruct.new(:debot_handle)
249
+
250
+ ParamsOfFetch = KwStruct.new(:address)
251
+
252
+ ResultOfFetch = KwStruct.new(:info)
253
+
254
+ ParamsOfExecute = KwStruct.new(:debot_handle, :action) do
255
+ def to_h
256
+ {
257
+ debot_handle: debot_handle,
258
+ action: action.to_h
259
+ }
260
+ end
261
+ end
262
+
263
+ ParamsOfSend = KwStruct.new(:debot_handle, :message)
264
+
265
+ ParamsOfRemove = KwStruct.new(:debot_handle)
266
+
267
+ #
268
+ # functions
269
+ #
270
+
271
+ def self.init(ctx, params, app_browser_obj = nil)
272
+ Interop::request_to_native_lib(
273
+ ctx,
274
+ "debot.init",
275
+ params
276
+ ) do |resp|
277
+ if resp.success?
278
+ debot_info = resp.result["info"].transform_keys(&:to_sym)
279
+ debot_info[:dabi_version] = debot_info.delete(:dabiVersion)
280
+ yield NativeLibResponseResult.new(
281
+ result: RegisteredDebot.new(
282
+ debot_handle: resp.result["debot_handle"],
283
+ debot_abi: resp.result["debot_abi"],
284
+ info: DebotInfo.new(**debot_info)
285
+ )
286
+ )
287
+ else
288
+ yield resp
289
+ end
290
+ end
291
+ end
292
+
293
+ def self.start(ctx, params)
294
+ Interop::request_to_native_lib(
295
+ ctx,
296
+ "debot.start",
297
+ params,
298
+ is_single_thread_only: false
299
+ ) do |resp|
300
+ if resp.success?
301
+ yield NativeLibResponseResult.new(
302
+ result: nil
303
+ )
304
+ else
305
+ yield resp
306
+ end
307
+ end
308
+ end
309
+
310
+ def self.fetch(ctx, params)
311
+ Interop::request_to_native_lib(
312
+ ctx,
313
+ "debot.fetch",
314
+ params,
315
+ is_single_thread_only: false
316
+ ) do |resp|
317
+ if resp.success?
318
+ debot_info = resp.result["info"].transform_keys(&:to_sym)
319
+ debot_info[:dabi_version] = debot_info.delete(:dabiVersion)
320
+ yield NativeLibResponseResult.new(
321
+ result: ResultOfFetch.new(
322
+ info: DebotInfo.new(**debot_info)
323
+ )
324
+ )
325
+ else
326
+ yield resp
327
+ end
328
+ end
329
+ end
330
+
331
+ def self.execute(ctx, params)
332
+ Interop::request_to_native_lib(ctx, "debot.execute", params) do |resp|
333
+ if resp.success?
334
+ yield NativeLibResponseResult.new(
335
+ result: nil
336
+ )
337
+ else
338
+ yield resp
339
+ end
340
+ end
341
+ end
342
+
343
+ def self.remove(ctx, params)
344
+ Interop::request_to_native_lib(ctx, "debot.remove", params) do |resp|
345
+ if resp.success?
346
+ yield NativeLibResponseResult.new(
347
+ result: nil
348
+ )
349
+ else
350
+ yield resp
351
+ end
352
+ end
353
+ end
354
+
355
+ def self.send(ctx, params)
356
+ Interop::request_to_native_lib(ctx, "debot.send", params) do |resp|
357
+ if resp.success?
358
+ yield NativeLibResponseResult.new(
359
+ result: nil
360
+ )
361
+ else
362
+ yield resp
363
+ end
364
+ end
365
+ end
366
+ end
367
+ end
@@ -0,0 +1,20 @@
1
+ module EverSdk
2
+ class Helper
3
+
4
+ # converts a symbol which may contain _, into a capital-case string
5
+ # :aaa_bbb_ccc -> AaaBbbCcc
6
+ def self.sym_to_capitalized_case_str(symb)
7
+ symb.to_s.split('_').map(&:capitalize).join
8
+ end
9
+
10
+
11
+ # converts a capital-case string
12
+ # into a symbol
13
+ # AaaBbbCcc --> :aaa_bbb_ccc
14
+ def self.capitalized_case_str_to_snake_case_sym(str)
15
+ str.split(/(?=[A-Z])/).map(&:downcase).join("_").to_sym
16
+ end
17
+
18
+ def self.base64_from_hex(hex_digest) = [[hex_digest].pack("H*")].pack("m0")
19
+ end
20
+ end
@@ -0,0 +1,230 @@
1
+ require 'ffi'
2
+ require 'rbconfig'
3
+ require 'concurrent'
4
+ require 'logger'
5
+
6
+ module EverSdk
7
+ module Interop
8
+ extend FFI::Library
9
+
10
+ class << self
11
+ attr_reader :logger
12
+ end
13
+
14
+ @logger = Logger.new(STDOUT)
15
+
16
+ class TcStringData < FFI::Struct
17
+ layout :content, :pointer,
18
+ :len, :uint32
19
+
20
+ def self.from_string(s)
21
+ tcs = TcStringData.new
22
+ bytes_count = s.unpack("C*").size
23
+ ptr1 = FFI::MemoryPointer.new(:char, bytes_count)
24
+ ptr1.put_bytes(0, s, 0, bytes_count)
25
+ tcs[:content] = ptr1
26
+ tcs[:len] = ptr1.size
27
+ tcs
28
+ end
29
+ end
30
+
31
+ class TcResponse < FFI::Struct
32
+ layout :result_json, TcStringData,
33
+ :error_json, TcStringData
34
+ end
35
+
36
+ DEFAULT_LIB_NAME = 'tonclient'
37
+ base_lib_name2 = case RbConfig::CONFIG['host_os']
38
+ when /linux/
39
+ 'linux'
40
+ when /darwin/
41
+ 'darwin'
42
+ when /mswin|mingw32|windows/
43
+ 'win32'
44
+ else
45
+ raise "unsupported OS: #{RbConfig::CONFIG['host_os']}"
46
+ end
47
+
48
+ lib_full_name = if !ENV['TON_CLIENT_NATIVE_LIB_NAME'].nil?
49
+ ENV['TON_CLIENT_NATIVE_LIB_NAME']
50
+ else
51
+ fl_nm = "#{DEFAULT_LIB_NAME}.#{FFI::Platform::LIBSUFFIX}"
52
+ File.join(File.expand_path(File.dirname(File.dirname(__dir__))), fl_nm)
53
+ end
54
+
55
+ ffi_lib(lib_full_name)
56
+
57
+
58
+
59
+
60
+ #
61
+ # in C
62
+ #
63
+ # enum tc_response_types_t {
64
+ # tc_response_success = 0,
65
+ # tc_response_error = 1,
66
+ # tc_response_nop = 2,
67
+ # tc_response_custom = 100,
68
+ # };
69
+ module TcResponseCodes
70
+ SUCCESS = 0
71
+ ERROR = 1
72
+ NOP = 2
73
+ APP_REQUEST = 3
74
+ APP_NOTIFY = 4
75
+ CUSTOM = 100
76
+ MAX = 999
77
+ end
78
+
79
+
80
+ #
81
+ # in C
82
+ #
83
+ # tc_string_handle_t* tc_create_context(tc_string_data_t config);
84
+ # void tc_destroy_context(uint32_t context);
85
+
86
+ attach_function(:tc_create_context, [TcStringData.by_value], :pointer)
87
+ attach_function(:tc_destroy_context, [:uint32], :void)
88
+
89
+
90
+ #
91
+ # in C
92
+ #
93
+ # tc_string_data_t tc_read_string(const tc_string_handle_t* string);
94
+ # void tc_destroy_string(const tc_string_handle_t* string);
95
+
96
+ attach_function(:tc_read_string, [:pointer], TcStringData.by_value)
97
+ attach_function(:tc_destroy_string, [:pointer], :void)
98
+
99
+
100
+ #
101
+ # in C
102
+ #
103
+ # void tc_request(
104
+ # uint32_t context,
105
+ # tc_string_data_t function_name,
106
+ # tc_string_data_t function_params_json,
107
+ # uint32_t request_id,
108
+ # tc_response_handler_t response_handler);
109
+
110
+ # typedef void (*tc_response_handler_t)(
111
+ # uint32_t request_id,
112
+ # tc_string_data_t params_json,
113
+ # uint32_t response_type,
114
+ # bool finished);
115
+
116
+ callback(:tc_response_handler, [:uint32, TcStringData.by_value, :uint32, :bool], :void)
117
+ attach_function(:tc_request, [:uint32, TcStringData.by_value, TcStringData.by_value, :uint32, :tc_response_handler], :void) # TODO possibly blocking: true
118
+
119
+ #
120
+ # in C
121
+ #
122
+ # tc_string_handle_t* tc_request_sync(
123
+ # uint32_t context,
124
+ # tc_string_data_t function_name,
125
+ # tc_string_data_t function_params_json);
126
+ attach_function(:tc_request_sync, [:uint32, TcStringData.by_value, TcStringData.by_value], :pointer)
127
+
128
+
129
+
130
+ @@request_counter = Concurrent::AtomicFixnum.new(1)
131
+
132
+ def self.request_to_native_lib(
133
+ ctx,
134
+ function_name,
135
+ function_params = nil,
136
+ client_callback: nil,
137
+ is_single_thread_only: true
138
+ )
139
+ function_name_tc_str = TcStringData.from_string(function_name)
140
+ function_params_json_str = function_params&.to_h&.to_json || ""
141
+ function_params_json_tc_str = TcStringData.from_string(function_params_json_str)
142
+
143
+ @sm = Concurrent::Semaphore.new(1)
144
+ if is_single_thread_only == true
145
+ @sm.acquire()
146
+ end
147
+
148
+
149
+ # using @@request_counter here to pass a @@request_counter and handlers and then retrieve them
150
+ # is probably isn't needed.
151
+ # Thanks to the way Ruby is, the same affect can be achieved by a block which is an easier way.
152
+ # Nonetheless, @@request_counter is incremented with each request and then sent out to a server
153
+ # in order to keep a server happy,
154
+ # because otherwise a server will, probably, reply in a wrong way.
155
+
156
+ self.tc_request(
157
+ ctx,
158
+ function_name_tc_str,
159
+ function_params_json_tc_str,
160
+ @@request_counter.value
161
+ ) do |req_id, params_json, response_type, is_finished|
162
+
163
+ tc_data_json_content = if params_json[:len] > 0
164
+ res = params_json[:content].read_string(params_json[:len])
165
+ JSON.parse(res)
166
+ else
167
+ ''
168
+ end
169
+
170
+ begin
171
+ case response_type
172
+
173
+ when TcResponseCodes::SUCCESS
174
+ if block_given?
175
+ yield NativeLibResponseResult.new(result: tc_data_json_content)
176
+ end
177
+
178
+ when TcResponseCodes::ERROR
179
+ if block_given?
180
+ yield NativeLibResponseResult.new(error: tc_data_json_content)
181
+ end
182
+
183
+ when TcResponseCodes::NOP
184
+ nil
185
+
186
+ when TcResponseCodes::APP_REQUEST
187
+ if !client_callback.nil?
188
+ client_callback.call(:request, tc_data_json_content)
189
+ end
190
+
191
+ when TcResponseCodes::APP_NOTIFY
192
+ if !client_callback.nil?
193
+ client_callback.call(:notify, tc_data_json_content)
194
+ end
195
+
196
+ when TcResponseCodes::CUSTOM..TcResponseCodes::MAX
197
+ if !client_callback.nil?
198
+ client_callback.call(:custom, tc_data_json_content)
199
+ end
200
+
201
+ else
202
+ raise ArgumentError.new("unsupported response type: #{response_type}")
203
+ end
204
+
205
+ rescue => e
206
+ @logger.error(e)
207
+ ensure
208
+ if is_single_thread_only == true
209
+ @sm.release()
210
+ end
211
+ end
212
+ end
213
+
214
+
215
+
216
+ if is_single_thread_only == true
217
+ @sm.acquire()
218
+ end
219
+
220
+ @@request_counter.increment()
221
+ nil
222
+ end
223
+
224
+ def self.request_to_native_lib_sync(ctx, function_name, function_params_json)
225
+ function_name_tc_str = TcStringData.from_string(function_name)
226
+ function_params_json_tc_str = TcStringData.from_string(function_params_json)
227
+ self.tc_request_sync(ctx, function_name_tc_str, function_params_json_tc_str)
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,7 @@
1
+ module EverSdk
2
+ class KwStruct < Struct
3
+ def self.new(*several_variants, keyword_init: true)
4
+ super
5
+ end
6
+ end
7
+ end