steem-ruby 0.9.3 → 0.9.4

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +9 -5
  3. data/README.md +10 -10
  4. data/lib/steem.rb +48 -0
  5. data/lib/steem/api.rb +0 -21
  6. data/lib/steem/base_error.rb +5 -3
  7. data/lib/steem/broadcast.rb +21 -4
  8. data/lib/steem/marshal.rb +231 -0
  9. data/lib/steem/mixins/jsonable.rb +37 -0
  10. data/lib/steem/mixins/serializable.rb +45 -0
  11. data/lib/steem/operation.rb +141 -0
  12. data/lib/steem/operation/account_create.rb +10 -0
  13. data/lib/steem/operation/account_create_with_delegation.rb +12 -0
  14. data/lib/steem/operation/account_update.rb +8 -0
  15. data/lib/steem/operation/account_witness_proxy.rb +4 -0
  16. data/lib/steem/operation/account_witness_vote.rb +5 -0
  17. data/lib/steem/operation/cancel_transfer_from_savings.rb +4 -0
  18. data/lib/steem/operation/challenge_authority.rb +5 -0
  19. data/lib/steem/operation/change_recovery_account.rb +5 -0
  20. data/lib/steem/operation/claim_account.rb +5 -0
  21. data/lib/steem/operation/claim_reward_balance.rb +6 -0
  22. data/lib/steem/operation/comment.rb +9 -0
  23. data/lib/steem/operation/comment_options.rb +10 -0
  24. data/lib/steem/operation/convert.rb +5 -0
  25. data/lib/steem/operation/create_claimed_account.rb +10 -0
  26. data/lib/steem/operation/custom.rb +5 -0
  27. data/lib/steem/operation/custom_binary.rb +8 -0
  28. data/lib/steem/operation/custom_json.rb +6 -0
  29. data/lib/steem/operation/decline_voting_rights.rb +4 -0
  30. data/lib/steem/operation/delegate_vesting_shares.rb +5 -0
  31. data/lib/steem/operation/delete_comment.rb +4 -0
  32. data/lib/steem/operation/escrow_approve.rb +8 -0
  33. data/lib/steem/operation/escrow_dispute.rb +7 -0
  34. data/lib/steem/operation/escrow_release.rb +10 -0
  35. data/lib/steem/operation/escrow_transfer.rb +12 -0
  36. data/lib/steem/operation/feed_publish.rb +4 -0
  37. data/lib/steem/operation/limit_order_cancel.rb +4 -0
  38. data/lib/steem/operation/limit_order_create.rb +8 -0
  39. data/lib/steem/operation/limit_order_create2.rb +8 -0
  40. data/lib/steem/operation/prove_authority.rb +4 -0
  41. data/lib/steem/operation/recover_account.rb +6 -0
  42. data/lib/steem/operation/report_over_production.rb +5 -0
  43. data/lib/steem/operation/request_account_recovery.rb +6 -0
  44. data/lib/steem/operation/reset_account.rb +5 -0
  45. data/lib/steem/operation/set_reset_account.rb +5 -0
  46. data/lib/steem/operation/set_withdraw_vesting_route.rb +6 -0
  47. data/lib/steem/operation/transfer.rb +6 -0
  48. data/lib/steem/operation/transfer_from_savings.rb +7 -0
  49. data/lib/steem/operation/transfer_to_savings.rb +6 -0
  50. data/lib/steem/operation/transfer_to_vesting.rb +5 -0
  51. data/lib/steem/operation/vote.rb +6 -0
  52. data/lib/steem/operation/withdraw_vesting.rb +4 -0
  53. data/lib/steem/operation/witness_set_properties.rb +5 -0
  54. data/lib/steem/operation/witness_update.rb +7 -0
  55. data/lib/steem/rpc/base_client.rb +13 -0
  56. data/lib/steem/rpc/http_client.rb +17 -1
  57. data/lib/steem/stream.rb +12 -4
  58. data/lib/steem/transaction.rb +96 -0
  59. data/lib/steem/transaction_builder.rb +69 -69
  60. data/lib/steem/type/amount.rb +2 -0
  61. data/lib/steem/version.rb +1 -1
  62. data/steem-ruby.gemspec +2 -0
  63. metadata +90 -2
@@ -0,0 +1,8 @@
1
+ class Steem::Operation::LimitOrderCreate < Steem::Operation
2
+ def_attr owner: :string
3
+ def_attr orderid: :uint32
4
+ def_attr amount_to_sell: :amount
5
+ def_attr min_to_receive: :amount
6
+ def_attr fill_or_kill: :boolean
7
+ def_attr expiration: :point_in_time
8
+ end
@@ -0,0 +1,8 @@
1
+ class Steem::Operation::LimitOrderCreate2 < Steem::Operation
2
+ def_attr owner: :string
3
+ def_attr orderid: :uint32
4
+ def_attr amount_to_sell: :amount
5
+ def_attr fill_or_kill: :boolean
6
+ def_attr exchange_rate: :price
7
+ def_attr expiration: :point_in_time
8
+ end
@@ -0,0 +1,4 @@
1
+ class Steem::Operation::ProveAuthority < Steem::Operation
2
+ def_attr challenged: :string
3
+ def_attr require_owner: :boolean
4
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::RecoverAccount < Steem::Operation
2
+ def_attr account_to_recover: :string
3
+ def_attr new_owner_authority: :authority
4
+ def_attr recent_owner_authority: :authority
5
+ def_attr extensions: :empty_array
6
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::ReportOverProduction < Steem::Operation
2
+ def_attr reporter: :string
3
+ def_attr first_block: :string # FIXME signed_block_header
4
+ def_attr second_block: :string # FIXME signed_block_header
5
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::RequestAccountRecovery < Steem::Operation
2
+ def_attr recovery_account: :string
3
+ def_attr account_to_recover: :string
4
+ def_attr new_owner_authority: :authority
5
+ def_attr extensions: :empty_array
6
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::ResetAccount < Steem::Operation
2
+ def_attr reset_account: :string
3
+ def_attr account_to_reset: :string
4
+ def_attr new_owner_authority: :authority
5
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::SetResetAccount < Steem::Operation
2
+ def_attr account: :string
3
+ def_attr current_reset_account: :string
4
+ def_attr reset_account: :string
5
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::SetWithdrawVestingRoute < Steem::Operation
2
+ def_attr from: :string
3
+ def_attr to: :string
4
+ def_attr percent: :uint16
5
+ def_attr auto_vest: :boolean
6
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::Transfer < Steem::Operation
2
+ def_attr from: :string
3
+ def_attr to: :string
4
+ def_attr amount: :amount
5
+ def_attr memo: :string
6
+ end
@@ -0,0 +1,7 @@
1
+ class Steem::Operation::TransferFromSavings < Steem::Operation
2
+ def_attr from: :string
3
+ def_attr request_id: :uint32
4
+ def_attr to: :string
5
+ def_attr amount: :amount
6
+ def_attr memo: :string
7
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::TransferToSavings < Steem::Operation
2
+ def_attr from: :string
3
+ def_attr to: :string
4
+ def_attr amount: :amount
5
+ def_attr memo: :string
6
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::TransferToVesting < Steem::Operation
2
+ def_attr from: :string
3
+ def_attr to: :string
4
+ def_attr amount: :amount
5
+ end
@@ -0,0 +1,6 @@
1
+ class Steem::Operation::Vote < Steem::Operation
2
+ def_attr voter: :string
3
+ def_attr author: :string
4
+ def_attr permlink: :string
5
+ def_attr weight: :int16
6
+ end
@@ -0,0 +1,4 @@
1
+ class Steem::Operation::WithdrawVesting < Steem::Operation
2
+ def_attr account: :string
3
+ def_attr vesting_shares: :amount
4
+ end
@@ -0,0 +1,5 @@
1
+ class Steem::Operation::WitnessSetProperties < Steem::Operation
2
+ def_attr owner: :string
3
+ def_attr props: :witness_properties
4
+ def_attr extensions: :empty_array
5
+ end
@@ -0,0 +1,7 @@
1
+ class Steem::Operation::WitnessUpdate < Steem::Operation
2
+ def_attr owner: :string
3
+ def_attr url: :string
4
+ def_attr block_signing_key: :public_key
5
+ def_attr props: :chain_properties
6
+ def_attr fee: :amount
7
+ end
@@ -161,6 +161,19 @@ module Steem
161
161
 
162
162
  sleep @backoff
163
163
  end
164
+
165
+ # @private
166
+ def raise_error_response(rpc_method_name, rpc_args, response)
167
+ raise UnknownError, "#{rpc_method_name}: #{response}" if response.error.nil?
168
+
169
+ error = response.error
170
+
171
+ if error.message == 'Invalid Request'
172
+ raise Steem::ArgumentError, "Unexpected arguments: #{rpc_args.inspect}. Expected: #{rpc_method_name} (#{args_keys_to_s(rpc_method_name)})"
173
+ end
174
+
175
+ BaseError.build_error(error, rpc_method_name)
176
+ end
164
177
  end
165
178
  end
166
179
  end
@@ -17,7 +17,7 @@ module Steem
17
17
  #
18
18
  # @private
19
19
  TIMEOUT_ERRORS = [Net::OpenTimeout, JSON::ParserError, Net::ReadTimeout,
20
- Errno::EBADF, IOError, Errno::ENETDOWN]
20
+ Errno::EBADF, IOError, Errno::ENETDOWN, Steem::RemoteDatabaseLockError]
21
21
 
22
22
  # @private
23
23
  POST_HEADERS = {
@@ -108,6 +108,22 @@ module Steem
108
108
  else; response
109
109
  end
110
110
 
111
+ [response].flatten.each_with_index do |r, i|
112
+ if defined?(r.error) && !!r.error
113
+ if !!r.error.message
114
+ begin
115
+ rpc_method_name = "#{api_name}.#{api_method}"
116
+ rpc_args = [request_object].flatten[i]
117
+ raise_error_response rpc_method_name, rpc_args, r
118
+ rescue *TIMEOUT_ERRORS => e
119
+ throw retry_timeout(:tota_cera_pila, e)
120
+ end
121
+ else
122
+ raise Steem::ArgumentError, r.error.inspect
123
+ end
124
+ end
125
+ end
126
+
111
127
  yield_response response, &block
112
128
  when '504' # Gateway Timeout
113
129
  throw retry_timeout(:tota_cera_pila, response.body)
@@ -91,6 +91,14 @@ module Steem
91
91
  # @option options [Integer] :until_block_num Ends the stream at the given block number. Default: nil.
92
92
  def transactions(options = {}, &block)
93
93
  blocks(options) do |block, block_num|
94
+ if block.nil?
95
+ warn "Batch missing block_num: #{block_num}, retrying ..."
96
+
97
+ block = block_api.get_block(block_num: block_num) do |result|
98
+ result.block
99
+ end
100
+ end
101
+
94
102
  block.transactions.each_with_index do |transaction, index|
95
103
  trx_id = block.transaction_ids[index]
96
104
 
@@ -332,9 +340,8 @@ module Steem
332
340
  end
333
341
 
334
342
  response = account_history_api.get_ops_in_block(*get_ops_in_block_options)
335
- result = response.result
336
343
 
337
- if result.nil?
344
+ if response.nil? || (result = response.result).nil?
338
345
  if retries < MAX_RETRY_COUNT
339
346
  warn "Retrying get_ops_in_block on block #{block_num}" unless @no_warn
340
347
  retries = retries + 1
@@ -360,7 +367,8 @@ module Steem
360
367
  retries = retries + 1
361
368
  redo
362
369
  else
363
- raise TooManyRetriesError, "unable to find virtual operations for block: #{block_num}"
370
+ warn "unable to find virtual operations for block: #{block_num}"
371
+ # raise TooManyRetriesError, "unable to find virtual operations for block: #{block_num}"
364
372
  end
365
373
  end
366
374
 
@@ -374,4 +382,4 @@ module Steem
374
382
  end
375
383
  end
376
384
  end
377
- end
385
+ end
@@ -0,0 +1,96 @@
1
+ module Steem
2
+ class Transaction
3
+ include JSONable
4
+ include Utils
5
+
6
+ ATTRIBUTES = %i(id ref_block_num ref_block_prefix expiration operations
7
+ extensions signatures)
8
+
9
+ attr_accessor *ATTRIBUTES
10
+
11
+ def initialize(options = {})
12
+ if !!(hex = options.delete(:hex))
13
+ marshal = Marshal.new(hex: hex)
14
+ marshal.transaction(trx: self)
15
+ end
16
+
17
+ options.each do |k, v|
18
+ raise Steem::ArgumentError, "Invalid option specified: #{k}" unless ATTRIBUTES.include?(k.to_sym)
19
+
20
+ send("#{k}=", v)
21
+ end
22
+
23
+ self.operations ||= []
24
+ self.extensions ||= []
25
+ self.signatures ||= []
26
+
27
+ self.expiration = case @expiration
28
+ when String then Time.parse(@expiration + 'Z')
29
+ else; @expiration
30
+ end
31
+ end
32
+
33
+ def inspect
34
+ properties = ATTRIBUTES.map do |prop|
35
+ unless (v = instance_variable_get("@#{prop}")).nil?
36
+ v = if v.respond_to? :strftime
37
+ v.strftime('%Y-%m-%dT%H:%M:%S')
38
+ else
39
+ v
40
+ end
41
+
42
+ "@#{prop}=#{v}"
43
+ end
44
+ end.compact.join(', ')
45
+
46
+ "#<#{self.class.name} [#{properties}]>"
47
+ end
48
+
49
+ def expiration
50
+ if @expiration.respond_to? :strftime
51
+ @expiration.strftime('%Y-%m-%dT%H:%M:%S')
52
+ else
53
+ @expiration
54
+ end
55
+ end
56
+
57
+ def expired?
58
+ @expiration.nil? || @expiration < Time.now
59
+ end
60
+
61
+ def [](key)
62
+ key = key.to_sym
63
+ send(key) if self.class.attributes.include?(key)
64
+ end
65
+
66
+ def []=(key, value)
67
+ key = key.to_sym
68
+ send("#{key}=", value) if self.class.attributes.include?(key)
69
+ end
70
+
71
+ def ==(other_trx)
72
+ return true if self.equal? other_trx
73
+ return false unless self.class == other_trx.class
74
+
75
+ begin
76
+ return false if self[:ref_block_num].to_i != other_trx[:ref_block_num].to_i
77
+ return false if self[:ref_block_prefix].to_i != other_trx[:ref_block_prefix].to_i
78
+ return false if self[:expiration].to_i != other_trx[:expiration].to_i
79
+ return false if self[:operations].size != other_trx[:operations].size
80
+
81
+ op_values = self[:operations].map do |type, value|
82
+ [type.to_s, value.values.map{|v| v.to_s.gsub(/[^a-zA-Z0-9-]/, '')}]
83
+ end.flatten.sort
84
+
85
+ other_op_values = other_trx[:operations].map do |type, value|
86
+ [type.to_s, value.values.map{|v| v.to_s.gsub(/[^a-zA-Z0-9-]/, '')}]
87
+ end.flatten.sort
88
+ # binding.pry unless op_values == other_op_values
89
+ op_values == other_op_values
90
+ rescue => e
91
+ # binding.pry
92
+ false
93
+ end
94
+ end
95
+ end
96
+ end
@@ -28,10 +28,11 @@ module Steem
28
28
 
29
29
  attr_accessor :app_base, :database_api, :block_api, :expiration, :operations
30
30
  attr_writer :wif
31
- attr_reader :signed, :testnet
31
+ attr_reader :signed, :testnet, :force_serialize
32
32
 
33
33
  alias app_base? app_base
34
34
  alias testnet? testnet
35
+ alias force_serialize? force_serialize
35
36
 
36
37
  def initialize(options = {})
37
38
  @app_base = !!options[:app_base] # default false
@@ -49,6 +50,7 @@ module Steem
49
50
  @wif = [options[:wif]].flatten
50
51
  @signed = false
51
52
  @testnet = !!options[:testnet]
53
+ @force_serialize = !!options[:force_serialize]
52
54
 
53
55
  if !!(trx = options[:trx])
54
56
  trx = case trx
@@ -56,28 +58,10 @@ module Steem
56
58
  else; trx
57
59
  end
58
60
 
59
- trx_options = {
60
- ref_block_num: trx['ref_block_num'],
61
- ref_block_prefix: trx['ref_block_prefix'],
62
- extensions: (trx['extensions']),
63
- operations: trx['operations'],
64
- signatures: (trx['signatures']),
65
- }
66
-
67
- trx_options[:expiration] = case trx['expiration']
68
- when String then Time.parse(trx['expiration'] + 'Z')
69
- else; trx['expiration']
70
- end
71
-
72
- options = options.merge(trx_options)
61
+ @trx = Transaction.new(trx)
73
62
  end
74
63
 
75
- @ref_block_num = options[:ref_block_num]
76
- @ref_block_prefix = options[:ref_block_prefix]
77
- @operations = options[:operations] || []
78
- @expiration = options[:expiration]
79
- @extensions = options[:extensions] || []
80
- @signatures = options[:signatures] || []
64
+ @trx ||= Transaction.new
81
65
  @chain = options[:chain] || :steem
82
66
  @error_pipe = options[:error_pipe] || STDERR
83
67
  @chain_id = options[:chain_id]
@@ -93,12 +77,9 @@ module Steem
93
77
  end
94
78
 
95
79
  def inspect
96
- properties = %w(
97
- ref_block_num ref_block_prefix expiration operations extensions
98
- signatures
99
- ).map do |prop|
80
+ properties = %w(trx).map do |prop|
100
81
  if !!(v = instance_variable_get("@#{prop}"))
101
- "@#{prop}=#{v}"
82
+ "@#{prop}=#{v.inspect}"
102
83
  end
103
84
  end.compact.join(', ')
104
85
 
@@ -106,21 +87,12 @@ module Steem
106
87
  end
107
88
 
108
89
  def reset
109
- @ref_block_num = nil
110
- @ref_block_prefix = nil
111
- @expiration = nil
112
- @operations = []
113
- @extensions = []
114
- @signatures = []
90
+ @trx = Transaction.new
115
91
  @signed = false
116
92
 
117
93
  self
118
94
  end
119
95
 
120
- def expired?
121
- @expiration.nil? || @expiration < Time.now
122
- end
123
-
124
96
  # If the transaction can be prepared, this method will do so and set the
125
97
  # expiration. Once the expiration is set, it will not re-prepare. If you
126
98
  # call {#put}, the expiration is set {::Nil} so that it can be re-prepared.
@@ -129,7 +101,7 @@ module Steem
129
101
  #
130
102
  # @return {TransactionBuilder}
131
103
  def prepare
132
- if expired?
104
+ if @trx.expired?
133
105
  catch :prepare_header do; begin
134
106
  @database_api.get_dynamic_global_properties do |properties|
135
107
  block_number = properties.last_irreversible_block_num
@@ -146,9 +118,9 @@ module Steem
146
118
  result
147
119
  end
148
120
 
149
- @ref_block_num = (block_number - 1) & 0xFFFF
150
- @ref_block_prefix = unhexlify(header.previous[8..-1]).unpack('V*')[0]
151
- @expiration ||= (Time.parse(properties.time + 'Z') + EXPIRE_IN_SECS).utc
121
+ @trx.ref_block_num = (block_number - 1) & 0xFFFF
122
+ @trx.ref_block_prefix = unhexlify(header.previous[8..-1]).unpack('V*')[0]
123
+ @trx.expiration ||= (Time.parse(properties.time + 'Z') + EXPIRE_IN_SECS).utc
152
124
  end
153
125
  end
154
126
  rescue => e
@@ -166,9 +138,9 @@ module Steem
166
138
 
167
139
  # Sets operations all at once, then prepares.
168
140
  def operations=(operations)
169
- @operations = operations.map{ |op| normalize_operation(op) }
141
+ @trx.operations = operations.map{ |op| normalize_operation(op) }
170
142
  prepare
171
- @operations
143
+ @trx.operations
172
144
  end
173
145
 
174
146
  # A quick and flexible way to append a new operation to the transaction.
@@ -194,8 +166,8 @@ module Steem
194
166
  # builder.put(vote: vote1).put(vote: vote2)
195
167
  # @return {TransactionBuilder}
196
168
  def put(type, op = nil)
197
- @expiration = nil
198
- @operations << normalize_operation(type, op)
169
+ @trx.expiration = nil
170
+ @trx.operations << normalize_operation(type, op)
199
171
  prepare
200
172
  self
201
173
  end
@@ -218,9 +190,17 @@ module Steem
218
190
  # ]],
219
191
  # :signatures => ["1c45b65740b4b2c17c4bcf6bcc3f8d90ddab827d50532729fc3b8f163f2c465a532b0112ae4bf388ccc97b7c2e0bc570caadda78af48cf3c261037e65eefcd941e"]
220
192
  # }
221
- def transaction
222
- prepare
223
- sign
193
+ def transaction(options = {prepare: true, sign: true})
194
+ options[:prepare] = true unless options.has_key? :prepare
195
+ options[:sign] = true unless options.has_key? :sign
196
+
197
+ prepare if !!options[:prepare]
198
+
199
+ if !!options[:sign]
200
+ sign
201
+ else
202
+ @trx
203
+ end
224
204
  end
225
205
 
226
206
  # Appends to the `signatures` array of the transaction, built from a
@@ -229,39 +209,42 @@ module Steem
229
209
  # @return {Hash | TransactionBuilder} The fully signed transaction if a `wif` is provided or the instance of the {TransactionBuilder} if a `wif` has not yet been provided.
230
210
  def sign
231
211
  return self if @wif.empty?
232
- return self if expired?
233
-
234
- trx = {
235
- ref_block_num: @ref_block_num,
236
- ref_block_prefix: @ref_block_prefix,
237
- expiration: @expiration.strftime('%Y-%m-%dT%H:%M:%S'),
238
- operations: @operations,
239
- extensions: @extensions,
240
- signatures: @signatures
241
- }
212
+ return self if @trx.expired?
242
213
 
243
214
  unless @signed
244
215
  catch :serialize do; begin
245
- transaction_hex_args = if app_base?
246
- {trx: trx}
247
- else
248
- trx
249
- end
250
-
251
- @database_api.get_transaction_hex(transaction_hex_args) do |result|
216
+ transaction_hex.tap do |result|
252
217
  hex = if app_base?
253
218
  result.hex
254
219
  else
255
220
  result
256
221
  end
222
+
223
+ unless force_serialize?
224
+ derrived_trx = Transaction.new(hex: hex)
225
+ derrived_ops = derrived_trx.operations
226
+ derrived_trx.operations = derrived_ops.map do |op|
227
+ op_name = if app_base?
228
+ op[:type].to_sym
229
+ else
230
+ op[:type].to_s.sub(/_operation$/, '').to_sym
231
+ end
232
+
233
+ normalize_operation op_name, JSON[op[:value].to_json]
234
+ end
235
+
236
+ raise SerializationMismatchError unless @trx == derrived_trx
237
+ end
238
+
239
+ hex = hex[0..-4] # drop empty signature array
240
+ @trx.id = Digest::SHA256.hexdigest(unhexlify(hex))[0..39]
257
241
 
258
- hex = @chain_id + hex[0..-4] # Why do we have to chop the last two bytes?
242
+ hex = @chain_id + hex
259
243
  digest = unhexlify(hex)
260
244
  digest_hex = Digest::SHA256.digest(digest)
261
245
  private_keys = @wif.map{ |wif| Bitcoin::Key.from_base58 wif }
262
246
  ec = Bitcoin::OpenSSL_EC
263
247
  count = 0
264
- sigs = []
265
248
 
266
249
  private_keys.each do |private_key|
267
250
  sig = nil
@@ -276,11 +259,10 @@ module Steem
276
259
  break if canonical? sig
277
260
  end
278
261
 
279
- @signatures << hexlify(sig)
262
+ @trx.signatures << hexlify(sig)
280
263
  end
281
264
 
282
265
  @signed = true
283
- trx[:signatures] = @signatures
284
266
  end
285
267
  rescue => e
286
268
  if can_retry? e
@@ -292,7 +274,25 @@ module Steem
292
274
  end; end
293
275
  end
294
276
 
295
- Hashie::Mash.new trx
277
+ @trx
278
+ end
279
+
280
+ def transaction_hex
281
+ trx = transaction(prepare: true, sign: false)
282
+
283
+ transaction_hex_args = if app_base?
284
+ {trx: trx}
285
+ else
286
+ trx
287
+ end
288
+
289
+ @database_api.get_transaction_hex(transaction_hex_args) do |result|
290
+ if app_base?
291
+ result[:hex]
292
+ else
293
+ result
294
+ end
295
+ end
296
296
  end
297
297
 
298
298
  # @return [Array] All public keys that could possibly sign for a given transaction.