nanook 2.4.0 → 3.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.
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