nanook 2.4.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/nanook/block.rb CHANGED
@@ -1,5 +1,8 @@
1
- class Nanook
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'util'
2
4
 
5
+ class Nanook
3
6
  # The <tt>Nanook::Block</tt> class contains methods to discover
4
7
  # publicly-available information about blocks on the nano network.
5
8
  #
@@ -17,21 +20,38 @@ class Nanook
17
20
  # rpc_conn = Nanook::Rpc.new
18
21
  # block = Nanook::Block.new(rpc_conn, "FBF8B0E...")
19
22
  class Block
23
+ include Nanook::Util
20
24
 
21
25
  def initialize(rpc, block)
22
26
  @rpc = rpc
23
- @block = block
24
- block_required! # All methods expect a block
27
+ @block = block.to_s
25
28
  end
26
29
 
27
- # Returns the {Nanook::Account} of the block.
30
+ # Returns the block hash id.
28
31
  #
29
32
  # ==== Example:
30
- # block.account # => Nanook::Account
31
33
  #
32
- # @return [Nanook::Account] the account of the block
33
- def account
34
- Nanook::Account.new(@rpc, rpc(:block_account, :hash)[:account])
34
+ # block.id #=> "FBF8B0E..."
35
+ #
36
+ # @return [String] the block hash id
37
+ def id
38
+ @block
39
+ end
40
+
41
+ # @param other [Nanook::Block] block to compare
42
+ # @return [Boolean] true if blocks are equal
43
+ def ==(other)
44
+ other.class == self.class &&
45
+ other.id == id
46
+ end
47
+ alias eql? ==
48
+
49
+ # The hash value is used along with #eql? by the Hash class to determine if two objects
50
+ # reference the same hash key.
51
+ #
52
+ # @return [Integer]
53
+ def hash
54
+ id.hash
35
55
  end
36
56
 
37
57
  # Stop generating work for a block.
@@ -45,64 +65,52 @@ class Nanook
45
65
  rpc(:work_cancel, :hash).empty?
46
66
  end
47
67
 
48
- # Returns an Array of block hashes in the account chain starting at
49
- # this block.
68
+ # Returns a consecutive list of block hashes in the account chain
69
+ # from (but not including) block back to +count+ (direction from frontier back to
70
+ # open block, from newer blocks to older). Will list all blocks back
71
+ # to the open block of this chain when count is set to "-1".
50
72
  #
51
- # See also #successors.
73
+ # See also #descendants.
52
74
  #
53
75
  # ==== Example:
54
76
  #
55
- # block.chain(limit: 2)
77
+ # block.ancestors(limit: 2)
56
78
  #
57
79
  # ==== Example reponse:
58
80
  #
59
- # [
60
- # "36A0FB717368BA8CF8D255B63DC207771EABC6C6FFC22A7F455EC2209464897E",
61
- # "FBF8B0E6623A31AB528EBD839EEAA91CAFD25C12294C46754E45FD017F7939EB"
62
- # ]
81
+ # [Nanook::Block, ...]
82
+ #
63
83
  # @param limit [Integer] maximum number of block hashes to return (default is 1000)
64
- def chain(limit: 1000)
65
- response = rpc(:chain, :block, count: limit)[:blocks]
66
- Nanook::Util.coerce_empty_string_to_type(response, Array)
84
+ # @param offset [Integer] return the account chain block hashes offset by the specified number of blocks (default is 0)
85
+ def chain(limit: 1000, offset: 0)
86
+ # The RPC includes this block in its response, and Nanook will remove it from the results.
87
+ # Increment the limit by 1 to account for this (a limit of -1 is valid and means no limit).
88
+ limit += 1 if limit > 0
89
+ params = {
90
+ count: limit,
91
+ offset: offset,
92
+ _access: :blocks,
93
+ _coerce: Array
94
+ }
95
+
96
+ response = rpc(:chain, :block, params)[1..].to_a
97
+ response.map { |block| as_block(block) }
67
98
  end
99
+ alias ancestors chain
68
100
 
69
101
  # Request confirmation for a block from online representative nodes.
70
102
  # Will return immediately with a boolean to indicate if the request for
71
103
  # confirmation was successful. Note that this boolean does not indicate
72
- # the confirmation status of the block. If confirmed, your block should
73
- # appear in {Nanook::Node#confirmation_history} within a short amount of
74
- # time, or you can use the convenience method {Nanook::Block#confirmed_recently?}
104
+ # the confirmation status of the block.
75
105
  #
76
106
  # ==== Example:
77
107
  # block.confirm # => true
78
108
  #
79
109
  # @return [Boolean] if the confirmation request was sent successful
80
110
  def confirm
81
- rpc(:block_confirm, :hash)[:started] == 1
111
+ rpc(:block_confirm, :hash, _access: :started) == 1
82
112
  end
83
113
 
84
- # Check if the block appears in the list of recently confirmed blocks by
85
- # online representatives. The full list of blocks can be queried for with {Nanook::Node#confirmation_history}.
86
- #
87
- # This method can work in conjunction with {Nanook::Block#confirm},
88
- # whereby you can send any block (old or new) out to online representatives to
89
- # confirm. The confirmation process can take up to a couple of minutes.
90
- #
91
- # The method returning +false+ can indicate that the block is still in the process of being
92
- # confirmed and that you should call the method again soon, or that it
93
- # was confirmed earlier than the list available in {Nanook::Node#confirmation_history},
94
- # or that it was not confirmed.
95
- #
96
- # ==== Example:
97
- # block.confirmed_recently? # => true
98
- #
99
- # @return [Boolean] +true+ if the block has been recently confirmed by
100
- # online representatives.
101
- def confirmed_recently?
102
- @rpc.call(:confirmation_history)[:confirmations].map{|h| h[:hash]}.include?(@block)
103
- end
104
- alias_method :recently_confirmed?, :confirmed_recently?
105
-
106
114
  # Generate work for a block.
107
115
  #
108
116
  # ==== Example:
@@ -113,42 +121,7 @@ class Nanook
113
121
  # When +false+, the node will only generate work locally (default is +false+)
114
122
  # @return [String] the work id of the work completed.
115
123
  def generate_work(use_peers: false)
116
- rpc(:work_generate, :hash, use_peers: use_peers)[:work]
117
- end
118
-
119
- # Returns Array of Hashes containing information about a chain of
120
- # send/receive blocks, starting from this block.
121
- #
122
- # ==== Example:
123
- #
124
- # block.history(limit: 1)
125
- #
126
- # ==== Example response:
127
- #
128
- # [
129
- # {
130
- # :account=>"xrb_3x7cjioqahgs5ppheys6prpqtb4rdknked83chf97bot1unrbdkaux37t31b",
131
- # :amount=>539834279601145558517940224,
132
- # :hash=>"36A0FB717368BA8CF8D255B63DC207771EABC6C6FFC22A7F455EC2209464897E",
133
- # :type=>"send"
134
- # }
135
- # ]
136
- #
137
- # @param limit [Integer] maximum number of send/receive block hashes
138
- # to return in the chain (default is 1000)
139
- def history(limit: 1000)
140
- rpc(:history, :hash, count: limit)[:history]
141
- end
142
-
143
- # Returns the block hash id.
144
- #
145
- # ==== Example:
146
- #
147
- # block.id #=> "FBF8B0E..."
148
- #
149
- # @return [String] the block hash id
150
- def id
151
- @block
124
+ rpc(:work_generate, :hash, use_peers: use_peers, _access: :work)
152
125
  end
153
126
 
154
127
  # Returns a Hash of information about the block.
@@ -161,141 +134,399 @@ class Nanook
161
134
  # ==== Example response:
162
135
  #
163
136
  # {
164
- # :id=>"36A0FB717368BA8CF8D255B63DC207771EABC6C6FFC22A7F455EC2209464897E",
165
- # :type=>"send",
166
- # :previous=>"FBF8B0E6623A31AB528EBD839EEAA91CAFD25C12294C46754E45FD017F7939EB",
167
- # :destination=>"xrb_3x7cjioqahgs5ppheys6prpqtb4rdknked83chf97bot1unrbdkaux37t31b",
168
- # :balance=>"00000000000000000000000000000000",
169
- # :work=>"44cc24b60705083a",
170
- # :signature=>"42ADFEFE7C3FFF188AE92A202F8A5734DE91779C454613E446EEC93D001D6C953E9FD16730AF32C891791BA8EDAECEB059A213E2FE1EEB7ADF9D5D0815464D06"
137
+ # "account": Nanook::Account,
138
+ # "amount": 34.2,
139
+ # "balance": 2.3
140
+ # "height": 58,
141
+ # "local_timestamp": Time,
142
+ # "confirmed": true,
143
+ # "type": "send",
144
+ # "account": Nanook::Account,
145
+ # "previous": Nanook::Block,
146
+ # "representative": Nanook::Account,
147
+ # "link": Nanook::Block,
148
+ # "link_as_account": Nanook::Account,
149
+ # "signature": "82D41BC16F313E4B2243D14DFFA2FB04679C540C2095FEE7EAE0F2F26880AD56DD48D87A7CC5DD760C5B2D76EE2C205506AA557BF00B60D8DEE312EC7343A501",
150
+ # "work": "8a142e07a10996d5"
171
151
  # }
172
152
  #
173
153
  # @param allow_unchecked [Boolean] (default is +false+). If +true+,
174
154
  # information can be returned about blocks that are unchecked (unverified).
175
- def info(allow_unchecked: false)
176
- if allow_unchecked
177
- response = rpc(:unchecked_get, :hash)
178
- unless response.has_key?(:error)
179
- return _parse_info_response(response)
180
- end
181
- # If unchecked not found, continue to checked block
155
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
156
+ # @raise [Nanook::NodeRpcError] if block is not found on the node.
157
+ def info(allow_unchecked: false, unit: Nanook.default_unit)
158
+ validate_unit!(unit)
159
+
160
+ # Params for both `unchecked_get` and `block_info` calls
161
+ params = {
162
+ json_block: true,
163
+ _coerce: Hash
164
+ }
165
+
166
+ begin
167
+ response = rpc(:block_info, :hash, params)
168
+ response.merge!(confirmed: true)
169
+ rescue Nanook::NodeRpcError => e
170
+ raise e unless allow_unchecked
171
+
172
+ response = rpc(:unchecked_get, :hash, params)
173
+ response.merge!(confirmed: false)
182
174
  end
183
175
 
184
- response = rpc(:block, :hash)
185
- _parse_info_response(response)
176
+ parse_info_response(response, unit)
186
177
  end
187
178
 
179
+ # Returns true if work is valid for the block.
180
+ #
188
181
  # ==== Example:
189
182
  #
190
- # block.is_valid_work?("2bf29ef00786a6bc") # => true
183
+ # block.valid_work?("2bf29ef00786a6bc") # => true
191
184
  #
192
185
  # @param work [String] the work id to check is valid
193
186
  # @return [Boolean] signalling if work is valid for the block
194
- def is_valid_work?(work)
187
+ def valid_work?(work)
195
188
  response = rpc(:work_validate, :hash, work: work)
196
- !response.empty? && response[:valid] == 1
189
+ response[:valid_all] == 1 || response[:valid_receive] == 1
197
190
  end
198
191
 
199
192
  # Republish blocks starting at this block up the account chain
200
193
  # back to the nano network.
201
194
  #
202
- # @return [Array<String>] block hashes that were republished
195
+ # @return [Array<Nanook::Block>] blocks that were republished
203
196
  #
204
197
  # ==== Example:
205
198
  #
206
- # block.republish
207
- #
208
- # ==== Example response:
209
- #
210
- # ["36A0FB717368BA8CF8D255B63DC207771EABC6C6FFC22A7F455EC2209464897E"]
211
- def republish(destinations:nil, sources:nil)
199
+ # block.republish # => [Nanook::Block, ...]
200
+ def republish(destinations: nil, sources: nil)
212
201
  if !destinations.nil? && !sources.nil?
213
- raise ArgumentError.new("You must provide either destinations or sources but not both")
202
+ raise ArgumentError, 'You must provide either destinations or sources but not both'
214
203
  end
215
204
 
216
- # Add in optional arguments
217
- params = {}
205
+ params = {
206
+ _access: :blocks,
207
+ _coerce: Array
208
+ }
209
+
218
210
  params[:destinations] = destinations unless destinations.nil?
219
211
  params[:sources] = sources unless sources.nil?
220
- params[:count] = 1 unless params.empty?
212
+ params[:count] = 1 if destinations || sources
221
213
 
222
- rpc(:republish, :hash, params)[:blocks]
214
+ rpc(:republish, :hash, params).map do |block|
215
+ as_block(block)
216
+ end
223
217
  end
224
218
 
219
+ # Returns true if block is a pending block.
220
+ #
225
221
  # ==== Example:
226
222
  #
227
223
  # block.pending? #=> false
228
224
  #
229
225
  # @return [Boolean] signalling if the block is a pending block.
230
226
  def pending?
231
- response = rpc(:pending_exists, :hash)
232
- !response.empty? && response[:exists] == 1
227
+ rpc(:pending_exists, :hash, _access: :exists) == 1
233
228
  end
234
229
 
235
- # Publish the block to the nano network.
230
+ # Returns an Array of block hashes in the account chain from (but not including) this block up to +count+
231
+ # (direction from open block up to frontier, from older blocks to newer). Will list all
232
+ # blocks up to frontier (latest block) of this chain when +count+ is set to +-1+.
236
233
  #
237
- # Note, if block has previously been published, use #republish instead.
234
+ # See also #ancestors.
238
235
  #
239
- # ==== Examples:
236
+ # ==== Example:
240
237
  #
241
- # block.publish # => "FBF8B0E..."
238
+ # block.descendants # => [Nanook::Block, .. ]
242
239
  #
243
- # @return [String] the block hash, or false.
244
- def publish
245
- rpc(:process, :block)[:hash] || false
240
+ # @param limit [Integer] maximum number of send/receive block hashes
241
+ # to return in the chain (default is 1000)
242
+ # @param offset [Integer] return the account chain block hashes offset
243
+ # by the specified number of blocks (default is 0)
244
+ # @return [Array<Nanook::Block>] blocks in the account chain ending at this block
245
+ def successors(limit: 1000, offset: 0)
246
+ # The RPC includes this block in its response, and Nanook will remove it from the results.
247
+ # Increment the limit by 1 to account for this (a limit of -1 is valid and means no limit).
248
+ limit += 1 if limit > 0
249
+
250
+ params = {
251
+ count: limit,
252
+ offset: offset,
253
+ _access: :blocks,
254
+ _coerce: Array
255
+ }
256
+
257
+ response = rpc(:successors, :block, params)[1..].to_a
258
+ response.map { |block| as_block(block) }
246
259
  end
247
- alias_method :process, :publish
260
+ alias descendants successors
248
261
 
249
- # Returns an Array of block hashes in the account chain ending at
250
- # this block.
262
+ # Returns the {Nanook::Account} of the block representative.
251
263
  #
252
- # See also #chain.
264
+ # ==== Example:
265
+ # block.representative # => Nanook::Account
266
+ #
267
+ # @return [Nanook::Account] representative account of the block. Can be nil.
268
+ def representative
269
+ memoized_info[:representative]
270
+ end
271
+
272
+ # Returns the {Nanook::Account} of the block.
253
273
  #
254
274
  # ==== Example:
275
+ # block.account # => Nanook::Account
255
276
  #
256
- # block.successors
277
+ # @return [Nanook::Account] the account of the block. Can be nil.
278
+ def account
279
+ memoized_info[:account]
280
+ end
281
+
282
+ # Returns the amount of the block.
257
283
  #
258
- # ==== Example response:
284
+ # ==== Example:
285
+ # block.amount # => 3.01
259
286
  #
260
- # ["36A0FB717368BA8CF8D255B63DC207771EABC6C6FFC22A7F455EC2209464897E"]
287
+ # @param unit (see Nanook::Account#balance)
288
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
289
+ # @return [Float]
290
+ def amount(unit: Nanook.default_unit)
291
+ validate_unit!(unit)
292
+
293
+ amount = memoized_info[:amount]
294
+ return amount unless unit == :nano
295
+
296
+ raw_to_NANO(amount)
297
+ end
298
+
299
+ # Returns the balance of the account at the time the block was created.
261
300
  #
262
- # @param limit [Integer] maximum number of send/receive block hashes
263
- # to return in the chain (default is 1000)
264
- # @return [Array<String>] block hashes in the account chain ending at this block
265
- def successors(limit: 1000)
266
- response = rpc(:successors, :block, count: limit)[:blocks]
267
- Nanook::Util.coerce_empty_string_to_type(response, Array)
301
+ # ==== Example:
302
+ # block.balance # => 3.01
303
+ #
304
+ # @param unit (see Nanook::Account#balance)
305
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
306
+ # @return [Float]
307
+ def balance(unit: Nanook.default_unit)
308
+ validate_unit!(unit)
309
+
310
+ balance = memoized_info[:balance]
311
+ return balance unless unit == :nano
312
+
313
+ raw_to_NANO(balance)
268
314
  end
269
315
 
270
- def inspect
271
- "#{self.class.name}(id: \"#{id}\", object_id: \"#{"0x00%x" % (object_id << 1)}\")"
316
+ # Returns true if block is confirmed.
317
+ #
318
+ # ==== Example:
319
+ # block.confirmed # => true
320
+ #
321
+ # @return [Boolean]
322
+ def confirmed?
323
+ memoized_info[:confirmed]
324
+ end
325
+ alias checked? confirmed?
326
+
327
+ # Returns true if block is unconfirmed.
328
+ #
329
+ # ==== Example:
330
+ # block.unconfirmed? # => true
331
+ #
332
+ # @return [Boolean]
333
+ def unconfirmed?
334
+ !confirmed?
335
+ end
336
+ alias unchecked? unconfirmed?
337
+
338
+ # Returns true if block exists in the node's ledger. This will return
339
+ # false for blocks that exist on the nano ledger but have not yet
340
+ # synchronized to the node.
341
+ #
342
+ # ==== Example:
343
+ #
344
+ # block.exists? # => false
345
+ # block.exists?(allow_unchecked: true) # => true
346
+ #
347
+ # @param allow_unchecked [Boolean] defaults to +false+
348
+ # @return [Boolean]
349
+ def exists?(allow_unchecked: false)
350
+ begin
351
+ allow_unchecked ? memoized_info : info
352
+ rescue Nanook::NodeRpcError
353
+ return false
354
+ end
355
+
356
+ true
357
+ end
358
+
359
+ # Returns the {Nanook::Block} of the next (newer) block in the account chain.
360
+ #
361
+ # ==== Example:
362
+ # block.next # => Nanook::Block
363
+ #
364
+ # @return [Nanook::Block] next (newer) block in the account chain. Can be nil.
365
+ def next
366
+ successors(limit: 1).first
367
+ end
368
+
369
+ # Returns the height of the block.
370
+ #
371
+ # ==== Example:
372
+ # block.height # => 5
373
+ #
374
+ # @return [Integer]
375
+ def height
376
+ memoized_info[:height]
377
+ end
378
+
379
+ # Returns the block work.
380
+ #
381
+ # ==== Example:
382
+ # block.work # => "8a142e07a10996d5"
383
+ #
384
+ # @return [String]
385
+ def work
386
+ memoized_info[:work]
387
+ end
388
+
389
+ # Returns the block signature.
390
+ #
391
+ # ==== Example:
392
+ # block.signature # => "82D41BC16F313E4B2243D14DFFA2FB04679C540C2095FEE7EAE0F2F26880AD56DD48D87A7CC5DD760C5B2D76EE2C205506AA557BF00B60D8DEE312EC7343A501"
393
+ #
394
+ # @return [String]
395
+ def signature
396
+ memoized_info[:signature]
397
+ end
398
+
399
+ # Returns the timestamp of when the node saw the block.
400
+ #
401
+ # ==== Example:
402
+ # block.timestamp # => 2018-05-30 16:41:48 UTC
403
+ #
404
+ # @return [Time] Time in UTC of when the node saw the block. Can be nil.
405
+ def timestamp
406
+ memoized_info[:local_timestamp]
407
+ end
408
+
409
+ # Returns the {Nanook::Block} of the previous (older) block in the account chain.
410
+ #
411
+ # ==== Example:
412
+ # block.previous # => Nanook::Block
413
+ #
414
+ # @return [Nanook::Block] previous (older) block in the account chain. Can be nil.
415
+ def previous
416
+ memoized_info[:previous]
417
+ end
418
+
419
+ # Returns the type of the block. One of "open", "send", "receive", "change", "epoch".
420
+ #
421
+ # ==== Example:
422
+ # block.type # => "open"
423
+ #
424
+ # @return [String] type of block. Returns nil for unconfirmed blocks.
425
+ def type
426
+ memoized_info[:type]
427
+ end
428
+
429
+ # Returns true if block is type "send".
430
+ #
431
+ # ==== Example:
432
+ # block.send? # => true
433
+ #
434
+ # @return [Boolean]
435
+ def send?
436
+ type == 'send'
437
+ end
438
+
439
+ # Returns true if block is type "open".
440
+ #
441
+ # ==== Example:
442
+ # block.open? # => true
443
+ #
444
+ # @return [Boolean]
445
+ def open?
446
+ type == 'open'
447
+ end
448
+
449
+ # Returns true if block is type "receive".
450
+ #
451
+ # ==== Example:
452
+ # block.receive? # => true
453
+ #
454
+ # @return [Boolean]
455
+ def receive?
456
+ type == 'receive'
272
457
  end
273
458
 
459
+ # Returns true if block is type "change" (change of representative).
460
+ #
461
+ # ==== Example:
462
+ # block.change? # => true
463
+ #
464
+ # @return [Boolean]
465
+ def change?
466
+ type == 'change'
467
+ end
468
+
469
+ # Returns true if block is type "epoch".
470
+ #
471
+ # ==== Example:
472
+ # block.epoch? # => true
473
+ #
474
+ # @return [Boolean]
475
+ def epoch?
476
+ type == 'epoch'
477
+ end
478
+
479
+ # @return [String]
480
+ def to_s
481
+ "#{self.class.name}(id: \"#{short_id}\")"
482
+ end
483
+ alias inspect to_s
484
+
274
485
  private
275
486
 
276
487
  # Some RPC calls expect the param that represents the block to be named
277
488
  # "hash", and others "block".
278
489
  # The param_name argument allows us to specify which it should be for this call.
279
- def rpc(action, param_name, params={})
280
- p = @block.nil? ? {} : { param_name.to_sym => @block }
490
+ def rpc(action, param_name, params = {})
491
+ p = { param_name.to_sym => @block }
281
492
  @rpc.call(action, p.merge(params))
282
493
  end
283
494
 
284
- def block_required!
285
- if @block.nil?
286
- raise ArgumentError.new("Block must be present")
287
- end
495
+ # Memoize the `#info` response as we can refer to it for other methods (`type`, `#open?`, `#send?` etc.)
496
+ def memoized_info
497
+ @memoized_info ||= info(allow_unchecked: true, unit: :raw)
288
498
  end
289
499
 
290
- def _parse_info_response(response)
291
- # The contents is a stringified JSON
292
- if response[:contents]
293
- r = JSON.parse(response[:contents]).to_symbolized_hash
294
- return r.merge(id: id)
500
+ def parse_info_response(response, unit)
501
+ response.merge!(id: id)
502
+ contents = response.delete(:contents)
503
+ response.merge!(contents) if contents
504
+
505
+ response.delete(:block_account) # duplicate of contents.account
506
+ response[:last_modified_at] = response.delete(:modified_timestamp) # rename key
507
+
508
+ # `type` can be "open", "send", "receive", "change", "epoch" or "state".
509
+ # blocks with `type` == "state" may have a `subtype` that gives more information
510
+ # about the block ("send", "receive", "change", "epoch"), in which case replace
511
+ # the `type` with this value.
512
+ if response[:type] == 'state' && (subtype = response.delete(:subtype))
513
+ response[:type] = subtype
295
514
  end
296
515
 
297
- response
298
- end
516
+ response[:account] = as_account(response[:account]) if response[:account]
517
+ response[:representative] = as_account(response[:representative]) if response[:representative]
518
+ response[:previous] = as_block(response[:previous]) if response[:previous]
519
+ response[:link] = as_block(response[:link]) if response[:link]
520
+ response[:link_as_account] = as_account(response[:link_as_account]) if response[:link_as_account]
521
+ response[:local_timestamp] = as_time(response[:local_timestamp])
522
+ response[:last_modified_at] = as_time(response[:last_modified_at])
299
523
 
524
+ if unit == :nano
525
+ response[:amount] = raw_to_NANO(response[:amount])
526
+ response[:balance] = raw_to_NANO(response[:balance])
527
+ end
528
+
529
+ response.compact
530
+ end
300
531
  end
301
532
  end