etherlite 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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