etherlite 0.1.4 → 0.1.5

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +92 -7
  3. data/etherlite.gemspec +2 -0
  4. data/lib/etherlite.rb +12 -7
  5. data/lib/etherlite/abi.rb +44 -4
  6. data/lib/etherlite/account/anonymous.rb +9 -0
  7. data/lib/etherlite/account/base.rb +56 -0
  8. data/lib/etherlite/account/local.rb +40 -0
  9. data/lib/etherlite/account/private_key.rb +51 -0
  10. data/lib/etherlite/api/node.rb +15 -12
  11. data/lib/etherlite/api/rpc.rb +55 -0
  12. data/lib/etherlite/commands/abi/load_contract.rb +24 -10
  13. data/lib/etherlite/commands/abi/load_function.rb +5 -5
  14. data/lib/etherlite/commands/abi/load_type.rb +1 -1
  15. data/lib/etherlite/commands/contract/event_base/decode_log_inputs.rb +30 -10
  16. data/lib/etherlite/configuration.rb +2 -1
  17. data/lib/etherlite/connection.rb +8 -4
  18. data/lib/etherlite/contract/base.rb +53 -1
  19. data/lib/etherlite/contract/function.rb +18 -14
  20. data/lib/etherlite/errors.rb +27 -0
  21. data/lib/etherlite/nonce_manager.rb +13 -14
  22. data/lib/etherlite/support/array.rb +43 -0
  23. data/lib/etherlite/transaction.rb +33 -1
  24. data/lib/etherlite/types/array_dynamic.rb +19 -5
  25. data/lib/etherlite/types/array_fixed.rb +19 -10
  26. data/lib/etherlite/types/{bool.rb → boolean.rb} +1 -1
  27. data/lib/etherlite/types/byte_string.rb +6 -0
  28. data/lib/etherlite/types/string.rb +6 -1
  29. data/lib/etherlite/utils.rb +4 -2
  30. data/lib/etherlite/version.rb +1 -1
  31. data/lib/generators/etherlite/templates/etherlite.yml +1 -0
  32. metadata +38 -8
  33. data/lib/etherlite/account.rb +0 -85
  34. data/lib/etherlite/commands/base.rb +0 -0
  35. data/lib/etherlite/commands/contract/function/encode_arguments.rb +0 -32
  36. data/lib/etherlite/pk_account.rb +0 -82
  37. data/lib/etherlite/types/array_base.rb +0 -36
@@ -0,0 +1,43 @@
1
+ module Etherlite::Support
2
+ module Array
3
+ extend self
4
+
5
+ def encode(_types, _values)
6
+ head = []
7
+ tail = []
8
+ tail_offset = _types.sum(0) { |type| type.dynamic? ? 32 : type.size } # type.size is always 32
9
+
10
+ _types.each_with_index do |type, i|
11
+ content = type.encode _values[i]
12
+ if type.dynamic?
13
+ head << Etherlite::Utils.uint_to_hex(tail_offset)
14
+ tail << content
15
+ tail_offset += content.length / 2 # hex string, 2 characters per byte
16
+ else
17
+ head << content
18
+ end
19
+ end
20
+
21
+ head.join + tail.join
22
+ end
23
+
24
+ def decode(_connection, _types, _data)
25
+ words = _data.scan(/.{64}/)
26
+ offset = 0
27
+
28
+ [].tap do |r|
29
+ _types.each do |type|
30
+ if type.dynamic?
31
+ offset_words = Etherlite::Utils.hex_to_uint(words[offset]) / 32
32
+ r << type.decode(_connection, words[offset_words..-1].join)
33
+ offset += 1
34
+ else
35
+ size_in_words = type.size / 32 # type.size is always 32
36
+ r << type.decode(_connection, words.slice(offset, size_in_words).join)
37
+ offset += size_in_words
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,10 +1,42 @@
1
1
  module Etherlite
2
2
  class Transaction
3
- attr_reader :tx_hash
3
+ attr_reader :tx_hash, :receipt
4
4
 
5
5
  def initialize(_connection, _tx_hash)
6
6
  @connection = _connection
7
7
  @tx_hash = _tx_hash
8
+ @receipt = {}
9
+ end
10
+
11
+ def refresh
12
+ @receipt = @connection.eth_get_transaction_receipt(@tx_hash) || {}
13
+ mined?
14
+ end
15
+
16
+ def mined?
17
+ @receipt.key? 'blockNumber'
18
+ end
19
+
20
+ def gas_used
21
+ Utils.hex_to_uint @receipt['gasUsed']
22
+ end
23
+
24
+ def block_number
25
+ Utils.hex_to_uint @receipt['blockNumber']
26
+ end
27
+
28
+ def contract_address
29
+ @receipt['contractAddress']
30
+ end
31
+
32
+ def wait_for_block(timeout: 120)
33
+ start = Time.now
34
+ while !refresh
35
+ return false if Time.now - start > timeout
36
+ sleep 1
37
+ end
38
+
39
+ true
8
40
  end
9
41
  end
10
42
  end
@@ -1,13 +1,27 @@
1
1
  module Etherlite::Types
2
- class ArrayDynamic < ArrayBase
2
+ class ArrayDynamic < Base
3
+ attr_reader :subtype
4
+
5
+ def initialize(_subtype)
6
+ raise ArgumentError, 'An array can not contain a dynamic type' if _subtype.dynamic?
7
+
8
+ @subtype = _subtype
9
+ end
10
+
3
11
  def signature
4
- "#{subtype.signature}[]"
12
+ "#{@subtype.signature}[]"
5
13
  end
6
14
 
7
- def encode(_value)
8
- raise ArgumentError, "expected an array for #{signature}" unless _value.is_a? Array
15
+ def encode(_values)
16
+ raise ArgumentError, "expected an array for #{signature}" unless _values.is_a? Array
17
+
18
+ encoded_array = Etherlite::Support::Array.encode([@subtype] * _values.length, _values)
19
+ Etherlite::Utils.uint_to_hex(_values.length) + encoded_array
20
+ end
9
21
 
10
- Etherlite::Utils.uint_to_hex(_value.size) + encode_values(_value)
22
+ def decode(_connection, _data)
23
+ length = Etherlite::Utils.hex_to_uint(_data[0..63])
24
+ Etherlite::Support::Array.decode(_connection, [@subtype] * length, _data[64..-1])
11
25
  end
12
26
  end
13
27
  end
@@ -1,23 +1,32 @@
1
1
  module Etherlite::Types
2
- class ArrayFixed < ArrayBase
3
- def initialize(_subtype, _size)
4
- super _subtype
5
- @size = _size
2
+ class ArrayFixed < Base
3
+ attr_reader :length, :subtype
4
+
5
+ def initialize(_subtype, _length)
6
+ raise ArgumentError, 'An array can not contain a dynamic type' if _subtype.dynamic?
7
+
8
+ @subtype = _subtype
9
+ @length = _length
6
10
  end
7
11
 
8
12
  def signature
9
- "#{subtype.signature}[#{@size}]"
13
+ "#{@subtype.signature}[#{@length}]"
10
14
  end
11
15
 
12
16
  def size
13
- subtype.size * @size
17
+ return nil if @subtype.dynamic?
18
+ @subtype.size * @length
14
19
  end
15
20
 
16
- def encode(_value)
17
- raise ArgumentError, "expected an array for #{signature}" unless _value.is_a? Array
18
- raise ArgumentError, "expected array of size #{@size}" unless _value.size == @size
21
+ def encode(_values)
22
+ raise ArgumentError, "expected an array for #{signature}" unless _values.is_a? Array
23
+ raise ArgumentError, "expected array of length #{@length}" unless _values.length == @length
24
+
25
+ Etherlite::Support::Array.encode([@subtype] * @length, _values)
26
+ end
19
27
 
20
- encode_values _value
28
+ def decode(_connection, _data)
29
+ Etherlite::Support::Array.decode(_connection, [@subtype] * @length, _data)
21
30
  end
22
31
  end
23
32
  end
@@ -1,5 +1,5 @@
1
1
  module Etherlite::Types
2
- class Bool < Base
2
+ class Boolean < Base
3
3
  TRUE = '0000000000000000000000000000000000000000000000000000000000000001'.freeze # 32 bytes
4
4
  FALSE = '0000000000000000000000000000000000000000000000000000000000000000'.freeze
5
5
 
@@ -13,5 +13,11 @@ module Etherlite::Types
13
13
 
14
14
  Etherlite::Utils.uint_to_hex(bytes) + bytes_as_hex.ljust(padded_size * 2, '0')
15
15
  end
16
+
17
+ def decode(_connection, _value)
18
+ size = Etherlite::Utils.hex_to_uint _value[0...64]
19
+
20
+ [_value[64...64 + size * 2]].pack('H*')
21
+ end
16
22
  end
17
23
  end
@@ -1,10 +1,15 @@
1
1
  module Etherlite::Types
2
- class String < Base
2
+ class String < ByteString
3
3
  def signature
4
4
  "string"
5
5
  end
6
6
 
7
7
  def encode(_value)
8
+ super _value.encode('UTF-8')
9
+ end
10
+
11
+ def decode(_connection, _value)
12
+ super(_connection, _value).force_encoding("utf-8")
8
13
  end
9
14
  end
10
15
  end
@@ -25,8 +25,10 @@ module Etherlite
25
25
  _hex_value.hex
26
26
  end
27
27
 
28
- def hex_to_int(_hex_value)
29
- # TODO.
28
+ def hex_to_int(_hex_value, bytes: 32)
29
+ value = _hex_value.hex
30
+ top_bit = (1 << (bytes * 8 - 1))
31
+ value & top_bit > 0 ? (value - 2 * top_bit) : value
30
32
  end
31
33
 
32
34
  def valid_address?(_address)
@@ -1,3 +1,3 @@
1
1
  module Etherlite
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.5"
3
3
  end
@@ -8,4 +8,5 @@ test: &defaults
8
8
  url: 'http://127.0.0.1:8545'
9
9
 
10
10
  production: &defaults
11
+ chain_id: 1
11
12
  url: 'http://127.0.0.1:8545'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: etherlite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ignacio Baixas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-01 00:00:00.000000000 Z
11
+ date: 2017-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: digest-sha3
@@ -136,6 +136,34 @@ dependencies:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
138
  version: '4.7'
139
+ - !ruby/object:Gem::Dependency
140
+ name: webmock
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 3.0.1
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 3.0.1
153
+ - !ruby/object:Gem::Dependency
154
+ name: pry
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
139
167
  description: ''
140
168
  email:
141
169
  - ignacio@surbtc.com
@@ -157,17 +185,19 @@ files:
157
185
  - etherlite.gemspec
158
186
  - lib/etherlite.rb
159
187
  - lib/etherlite/abi.rb
160
- - lib/etherlite/account.rb
188
+ - lib/etherlite/account/anonymous.rb
189
+ - lib/etherlite/account/base.rb
190
+ - lib/etherlite/account/local.rb
191
+ - lib/etherlite/account/private_key.rb
161
192
  - lib/etherlite/address.rb
162
193
  - lib/etherlite/api/address.rb
163
194
  - lib/etherlite/api/node.rb
195
+ - lib/etherlite/api/rpc.rb
164
196
  - lib/etherlite/client.rb
165
197
  - lib/etherlite/commands/abi/load_contract.rb
166
198
  - lib/etherlite/commands/abi/load_function.rb
167
199
  - lib/etherlite/commands/abi/load_type.rb
168
- - lib/etherlite/commands/base.rb
169
200
  - lib/etherlite/commands/contract/event_base/decode_log_inputs.rb
170
- - lib/etherlite/commands/contract/function/encode_arguments.rb
171
201
  - lib/etherlite/commands/utils/validate_address.rb
172
202
  - lib/etherlite/configuration.rb
173
203
  - lib/etherlite/connection.rb
@@ -175,18 +205,18 @@ files:
175
205
  - lib/etherlite/contract/event_base.rb
176
206
  - lib/etherlite/contract/event_input.rb
177
207
  - lib/etherlite/contract/function.rb
208
+ - lib/etherlite/errors.rb
178
209
  - lib/etherlite/nonce_manager.rb
179
- - lib/etherlite/pk_account.rb
180
210
  - lib/etherlite/railtie.rb
181
211
  - lib/etherlite/railties/configuration_extensions.rb
182
212
  - lib/etherlite/railties/utils.rb
213
+ - lib/etherlite/support/array.rb
183
214
  - lib/etherlite/transaction.rb
184
215
  - lib/etherlite/types/address.rb
185
- - lib/etherlite/types/array_base.rb
186
216
  - lib/etherlite/types/array_dynamic.rb
187
217
  - lib/etherlite/types/array_fixed.rb
188
218
  - lib/etherlite/types/base.rb
189
- - lib/etherlite/types/bool.rb
219
+ - lib/etherlite/types/boolean.rb
190
220
  - lib/etherlite/types/byte_string.rb
191
221
  - lib/etherlite/types/bytes.rb
192
222
  - lib/etherlite/types/fixed.rb
@@ -1,85 +0,0 @@
1
- module Etherlite
2
- class Account
3
- include Etherlite::Api::Address
4
-
5
- attr_reader :connection, :normalized_address
6
-
7
- def initialize(_connection, _normalized_address)
8
- @connection = _connection
9
- @normalized_address = _normalized_address
10
- end
11
-
12
- def unlock(_passphrase)
13
- @passphrase = _passphrase
14
- end
15
-
16
- def lock
17
- @passphrase = nil
18
- end
19
-
20
- def transfer_to(_target, _options = {})
21
- params = {
22
- from: json_encoded_address,
23
- to: Utils.encode_address_param(_target),
24
- value: Utils.encode_quantity_param(_options.fetch(:amount, 0))
25
- }
26
-
27
- send_transaction params, _options
28
- end
29
-
30
- def call(_target, _function, *_params)
31
- _function = Utils.parse_function(_function) unless _function.is_a? Contract::Function
32
- options = _params.last.is_a?(Hash) ? _params.pop : {}
33
-
34
- if _function.constant?
35
- call_constant _target, _function, _params, options
36
- else
37
- send_function_transaction _target, _function, _params, options
38
- end
39
- end
40
-
41
- private
42
-
43
- attr_reader :normalized_address
44
-
45
- def call_constant(_target, _function, _params, _options)
46
- ipc_params = {
47
- from: json_encoded_address,
48
- to: Utils.encode_address_param(_target),
49
- data: _function.encode(_params)
50
- }
51
-
52
- block = Utils.encode_block_param _options.fetch(:block, :latest)
53
-
54
- _function.decode @connection, @connection.ipc_call(:eth_call, ipc_params, block)
55
- end
56
-
57
- def send_function_transaction(_target, _function, _params, _options)
58
- ipc_params = {
59
- from: json_encoded_address,
60
- to: Utils.encode_address_param(_target),
61
- data: _function.encode(_params)
62
- }
63
-
64
- value = _options.fetch(:pay, 0)
65
- raise 'function is not payable' if value > 0 && !_function.payable?
66
- ipc_params[:value] = value
67
-
68
- send_transaction(ipc_params, _options)
69
- end
70
-
71
- def send_transaction(_params, _opt)
72
- _params[:gas] = Utils.encode_quantity_param(_opt[:gas]) if _opt.key? :gas
73
- _params[:gasPrice] = Utils.encode_quantity_param(_opt[:gas_price]) if _opt.key? :gas_price
74
-
75
- passphrase = _opt.fetch(:passphrase, @passphrase)
76
- tx_hash = if passphrase.nil?
77
- @connection.ipc_call(:eth_sendTransaction, _params)
78
- else
79
- @connection.ipc_call(:personal_sendTransaction, _params, passphrase)
80
- end
81
-
82
- Transaction.new @connection, tx_hash
83
- end
84
- end
85
- end
File without changes
@@ -1,32 +0,0 @@
1
- class Etherlite::Contract::Function
2
- class EncodeArguments < PowerTypes::Command.new(:subtypes, :values)
3
- def perform # rubocop:disable Metrics/MethodLength
4
- if @values.length != @subtypes.count
5
- raise ArgumentError, "Expected #{@subtypes.count} arguments, got #{@values.length} "
6
- end
7
-
8
- head = []
9
- tail = []
10
- tail_offset = calculate_head_offset
11
-
12
- @subtypes.each_with_index do |type, i|
13
- content = type.encode @values[i]
14
- if type.dynamic?
15
- head << Etherlite::Utils.uint_to_hex(tail_offset)
16
- tail << content
17
- tail_offset += content.length / 2 # hex string, 2 characters per byte
18
- else
19
- head << content
20
- end
21
- end
22
-
23
- head.join + tail.join
24
- end
25
-
26
- private
27
-
28
- def calculate_head_offset
29
- @subtypes.inject(0) { |r, type| r + (type.dynamic? ? 32 : type.size) }
30
- end
31
- end
32
- end