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.
- checksums.yaml +4 -4
- data/README.md +92 -7
- data/etherlite.gemspec +2 -0
- data/lib/etherlite.rb +12 -7
- data/lib/etherlite/abi.rb +44 -4
- data/lib/etherlite/account/anonymous.rb +9 -0
- data/lib/etherlite/account/base.rb +56 -0
- data/lib/etherlite/account/local.rb +40 -0
- data/lib/etherlite/account/private_key.rb +51 -0
- data/lib/etherlite/api/node.rb +15 -12
- data/lib/etherlite/api/rpc.rb +55 -0
- data/lib/etherlite/commands/abi/load_contract.rb +24 -10
- data/lib/etherlite/commands/abi/load_function.rb +5 -5
- data/lib/etherlite/commands/abi/load_type.rb +1 -1
- data/lib/etherlite/commands/contract/event_base/decode_log_inputs.rb +30 -10
- data/lib/etherlite/configuration.rb +2 -1
- data/lib/etherlite/connection.rb +8 -4
- data/lib/etherlite/contract/base.rb +53 -1
- data/lib/etherlite/contract/function.rb +18 -14
- data/lib/etherlite/errors.rb +27 -0
- data/lib/etherlite/nonce_manager.rb +13 -14
- data/lib/etherlite/support/array.rb +43 -0
- data/lib/etherlite/transaction.rb +33 -1
- data/lib/etherlite/types/array_dynamic.rb +19 -5
- data/lib/etherlite/types/array_fixed.rb +19 -10
- data/lib/etherlite/types/{bool.rb → boolean.rb} +1 -1
- data/lib/etherlite/types/byte_string.rb +6 -0
- data/lib/etherlite/types/string.rb +6 -1
- data/lib/etherlite/utils.rb +4 -2
- data/lib/etherlite/version.rb +1 -1
- data/lib/generators/etherlite/templates/etherlite.yml +1 -0
- metadata +38 -8
- data/lib/etherlite/account.rb +0 -85
- data/lib/etherlite/commands/base.rb +0 -0
- data/lib/etherlite/commands/contract/function/encode_arguments.rb +0 -32
- data/lib/etherlite/pk_account.rb +0 -82
- 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 <
|
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(
|
8
|
-
raise ArgumentError, "expected an array for #{signature}" unless
|
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
|
-
|
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 <
|
3
|
-
|
4
|
-
|
5
|
-
|
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}[#{@
|
13
|
+
"#{@subtype.signature}[#{@length}]"
|
10
14
|
end
|
11
15
|
|
12
16
|
def size
|
13
|
-
|
17
|
+
return nil if @subtype.dynamic?
|
18
|
+
@subtype.size * @length
|
14
19
|
end
|
15
20
|
|
16
|
-
def encode(
|
17
|
-
raise ArgumentError, "expected an array for #{signature}" unless
|
18
|
-
raise ArgumentError, "expected array of
|
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
|
-
|
28
|
+
def decode(_connection, _data)
|
29
|
+
Etherlite::Support::Array.decode(_connection, [@subtype] * @length, _data)
|
21
30
|
end
|
22
31
|
end
|
23
32
|
end
|
@@ -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 <
|
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
|
data/lib/etherlite/utils.rb
CHANGED
@@ -25,8 +25,10 @@ module Etherlite
|
|
25
25
|
_hex_value.hex
|
26
26
|
end
|
27
27
|
|
28
|
-
def hex_to_int(_hex_value)
|
29
|
-
|
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)
|
data/lib/etherlite/version.rb
CHANGED
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
|
+
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
|
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/
|
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
|
data/lib/etherlite/account.rb
DELETED
@@ -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
|