ethlite 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/Manifest.txt +8 -0
- data/Rakefile +2 -0
- data/lib/ethlite/abi/abi_coder.rb +423 -0
- data/lib/ethlite/abi/constant.rb +32 -0
- data/lib/ethlite/abi/exceptions.rb +29 -0
- data/lib/ethlite/abi/type.rb +203 -0
- data/lib/ethlite/abi/utils.rb +223 -0
- data/lib/ethlite/contract.rb +64 -0
- data/lib/ethlite/rpc.rb +48 -0
- data/lib/ethlite/utility.rb +23 -0
- data/lib/ethlite/version.rb +1 -1
- data/lib/ethlite.rb +28 -0
- metadata +37 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ee22cabcf4a413047cd733de839213fefe59ea3a45ddc353f9dd7675f416e28
|
4
|
+
data.tar.gz: c16a6e278a4102061f6efbf7908171ab04653108b2abd4c9d8507ab7bba46324
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 356dd2d7d825fe42d30e94d48b58d8a737ee0cd274e1329c9425341d953e0654ac1a34d789c5f98ca52706b3f1f0702a7d4a418d758a76856410ae72f1bc1b1a
|
7
|
+
data.tar.gz: 0512f202e3b4eefb7a2115158036cd4e11813ef37f0b4df3a9e8269b50f90d8c2fa8d6ae6cade9caf9a49a96b30556ef28e2afa5d8debfe8d2ebf95c221b64a0
|
data/Manifest.txt
CHANGED
@@ -3,4 +3,12 @@ Manifest.txt
|
|
3
3
|
README.md
|
4
4
|
Rakefile
|
5
5
|
lib/ethlite.rb
|
6
|
+
lib/ethlite/abi/abi_coder.rb
|
7
|
+
lib/ethlite/abi/constant.rb
|
8
|
+
lib/ethlite/abi/exceptions.rb
|
9
|
+
lib/ethlite/abi/type.rb
|
10
|
+
lib/ethlite/abi/utils.rb
|
11
|
+
lib/ethlite/contract.rb
|
12
|
+
lib/ethlite/rpc.rb
|
13
|
+
lib/ethlite/utility.rb
|
6
14
|
lib/ethlite/version.rb
|
data/Rakefile
CHANGED
@@ -0,0 +1,423 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Ethlite
|
4
|
+
module Abi
|
5
|
+
|
6
|
+
##
|
7
|
+
# Contract ABI encoding and decoding.
|
8
|
+
#
|
9
|
+
# @see https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
|
10
|
+
#
|
11
|
+
module AbiCoder
|
12
|
+
|
13
|
+
extend self
|
14
|
+
|
15
|
+
include Constant
|
16
|
+
|
17
|
+
class EncodingError < StandardError; end
|
18
|
+
class DecodingError < StandardError; end
|
19
|
+
class ValueOutOfBounds < ValueError; end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Encodes multiple arguments using the head/tail mechanism.
|
23
|
+
#
|
24
|
+
def encode_abi(types, args)
|
25
|
+
parsed_types = types.map {|t| Type.parse(t) }
|
26
|
+
|
27
|
+
head_size = (0...args.size)
|
28
|
+
.map {|i| parsed_types[i].size || 32 }
|
29
|
+
.reduce(0, &:+)
|
30
|
+
|
31
|
+
head, tail = '', ''
|
32
|
+
args.each_with_index do |arg, i|
|
33
|
+
if parsed_types[i].dynamic?
|
34
|
+
head += encode_type(Type.size_type, head_size + tail.size)
|
35
|
+
tail += encode_type(parsed_types[i], arg)
|
36
|
+
else
|
37
|
+
head += encode_type(parsed_types[i], arg)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
"#{head}#{tail}"
|
42
|
+
end
|
43
|
+
alias :encode :encode_abi
|
44
|
+
|
45
|
+
##
|
46
|
+
# Encodes a single value (static or dynamic).
|
47
|
+
#
|
48
|
+
# @param type [Ethereum::ABI::Type] value type
|
49
|
+
# @param arg [Object] value
|
50
|
+
#
|
51
|
+
# @return [String] encoded bytes
|
52
|
+
#
|
53
|
+
def encode_type(type, arg)
|
54
|
+
if %w(string bytes).include?(type.base) && type.sub.empty?
|
55
|
+
encode_primitive_type type, arg
|
56
|
+
elsif type.dynamic?
|
57
|
+
raise ArgumentError, "arg must be an array" unless arg.instance_of?(Array)
|
58
|
+
|
59
|
+
head, tail = '', ''
|
60
|
+
if type.dims.last == 0
|
61
|
+
head += encode_type(Type.size_type, arg.size)
|
62
|
+
else
|
63
|
+
raise ArgumentError, "Wrong array size: found #{arg.size}, expecting #{type.dims.last}" unless arg.size == type.dims.last
|
64
|
+
end
|
65
|
+
|
66
|
+
sub_type = type.subtype
|
67
|
+
sub_size = type.subtype.size
|
68
|
+
arg.size.times do |i|
|
69
|
+
if sub_size.nil?
|
70
|
+
head += encode_type(Type.size_type, 32*arg.size + tail.size)
|
71
|
+
tail += encode_type(sub_type, arg[i])
|
72
|
+
else
|
73
|
+
head += encode_type(sub_type, arg[i])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
"#{head}#{tail}"
|
78
|
+
else # static type
|
79
|
+
if type.dims.empty?
|
80
|
+
encode_primitive_type type, arg
|
81
|
+
else
|
82
|
+
arg.map {|x| encode_type(type.subtype, x) }.join
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def encode_primitive_type(type, arg)
|
88
|
+
case type.base
|
89
|
+
when 'uint'
|
90
|
+
begin
|
91
|
+
real_size = type.sub.to_i
|
92
|
+
i = get_uint arg
|
93
|
+
|
94
|
+
raise ValueOutOfBounds, arg unless i >= 0 && i < 2**real_size
|
95
|
+
Utils.zpad_int i
|
96
|
+
rescue EncodingError
|
97
|
+
raise ValueOutOfBounds, arg
|
98
|
+
end
|
99
|
+
when 'bool'
|
100
|
+
raise ArgumentError, "arg is not bool: #{arg}" unless arg.instance_of?(TrueClass) || arg.instance_of?(FalseClass)
|
101
|
+
Utils.zpad_int(arg ? 1 : 0)
|
102
|
+
when 'int'
|
103
|
+
begin
|
104
|
+
real_size = type.sub.to_i
|
105
|
+
i = get_int arg
|
106
|
+
|
107
|
+
raise ValueOutOfBounds, arg unless i >= -2**(real_size-1) && i < 2**(real_size-1)
|
108
|
+
Utils.zpad_int(i % 2**type.sub.to_i)
|
109
|
+
rescue EncodingError
|
110
|
+
raise ValueOutOfBounds, arg
|
111
|
+
end
|
112
|
+
when 'ufixed'
|
113
|
+
high, low = type.sub.split('x').map(&:to_i)
|
114
|
+
|
115
|
+
raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**high
|
116
|
+
Utils.zpad_int((arg * 2**low).to_i)
|
117
|
+
when 'fixed'
|
118
|
+
high, low = type.sub.split('x').map(&:to_i)
|
119
|
+
|
120
|
+
raise ValueOutOfBounds, arg unless arg >= -2**(high - 1) && arg < 2**(high - 1)
|
121
|
+
|
122
|
+
i = (arg * 2**low).to_i
|
123
|
+
Utils.zpad_int(i % 2**(high+low))
|
124
|
+
when 'string'
|
125
|
+
if arg.encoding.name == 'UTF-8'
|
126
|
+
arg = arg.b
|
127
|
+
else
|
128
|
+
begin
|
129
|
+
arg.unpack('U*')
|
130
|
+
rescue ArgumentError
|
131
|
+
raise ValueError, "string must be UTF-8 encoded"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
if type.sub.empty? # variable length type
|
136
|
+
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size >= TT256
|
137
|
+
size = Utils.zpad_int arg.size
|
138
|
+
value = Utils.rpad arg, BYTE_ZERO, Utils.ceil32(arg.size)
|
139
|
+
"#{size}#{value}"
|
140
|
+
else # fixed length type
|
141
|
+
sub = type.sub.to_i
|
142
|
+
raise ValueOutOfBounds, "invalid string length #{sub}" if arg.size > sub
|
143
|
+
raise ValueOutOfBounds, "invalid string length #{sub}" if sub < 0 || sub > 32
|
144
|
+
Utils.rpad(arg, BYTE_ZERO, 32)
|
145
|
+
end
|
146
|
+
when 'bytes'
|
147
|
+
raise EncodingError, "Expecting string: #{arg}" unless arg.instance_of?(String)
|
148
|
+
arg = arg.b
|
149
|
+
|
150
|
+
if type.sub.empty? # variable length type
|
151
|
+
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size >= TT256
|
152
|
+
size = Utils.zpad_int arg.size
|
153
|
+
value = Utils.rpad arg, BYTE_ZERO, Utils.ceil32(arg.size)
|
154
|
+
"#{size}#{value}"
|
155
|
+
else # fixed length type
|
156
|
+
sub = type.sub.to_i
|
157
|
+
raise ValueOutOfBounds, "invalid bytes length #{sub}" if arg.size > sub
|
158
|
+
raise ValueOutOfBounds, "invalid bytes length #{sub}" if sub < 0 || sub > 32
|
159
|
+
Utils.rpad(arg, BYTE_ZERO, 32)
|
160
|
+
end
|
161
|
+
when 'hash'
|
162
|
+
size = type.sub.to_i
|
163
|
+
raise EncodingError, "too long: #{arg}" unless size > 0 && size <= 32
|
164
|
+
|
165
|
+
if arg.is_a?(Integer)
|
166
|
+
Utils.zpad_int(arg)
|
167
|
+
elsif arg.size == size
|
168
|
+
Utils.zpad arg, 32
|
169
|
+
elsif arg.size == size * 2
|
170
|
+
Utils.zpad_hex arg
|
171
|
+
else
|
172
|
+
raise EncodingError, "Could not parse hash: #{arg}"
|
173
|
+
end
|
174
|
+
when 'address'
|
175
|
+
if arg.is_a?(Integer)
|
176
|
+
Utils.zpad_int arg
|
177
|
+
elsif arg.size == 20
|
178
|
+
Utils.zpad arg, 32
|
179
|
+
elsif arg.size == 40
|
180
|
+
Utils.zpad_hex arg
|
181
|
+
elsif arg.size == 42 && arg[0,2] == '0x'
|
182
|
+
Utils.zpad_hex arg[2..-1]
|
183
|
+
else
|
184
|
+
raise EncodingError, "Could not parse address: #{arg}"
|
185
|
+
end
|
186
|
+
else
|
187
|
+
raise EncodingError, "Unhandled type: #{type.base} #{type.sub}"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
def min_data_size types
|
193
|
+
types.size*32
|
194
|
+
end
|
195
|
+
|
196
|
+
##
|
197
|
+
# Decodes multiple arguments using the head/tail mechanism.
|
198
|
+
#
|
199
|
+
def decode_abi types, data, raise_errors = false
|
200
|
+
parsed_types = types.map {|t| Type.parse(t) }
|
201
|
+
|
202
|
+
outputs = [nil] * types.size
|
203
|
+
start_positions = [nil] * types.size + [data.size]
|
204
|
+
|
205
|
+
# TODO: refactor, a reverse iteration will be better
|
206
|
+
pos = 0
|
207
|
+
parsed_types.each_with_index do |t, i|
|
208
|
+
# If a type is static, grab the data directly, otherwise record its
|
209
|
+
# start position
|
210
|
+
if t.dynamic?
|
211
|
+
|
212
|
+
if raise_errors && pos>data.size-1
|
213
|
+
raise DecodingError, "Position out of bounds #{pos}>#{data.size-1}"
|
214
|
+
end
|
215
|
+
|
216
|
+
start_positions[i] = Utils.big_endian_to_int(data[pos, 32])
|
217
|
+
|
218
|
+
if raise_errors && start_positions[i]>data.size-1
|
219
|
+
raise DecodingError, "Start position out of bounds #{start_positions[i]}>#{data.size-1}"
|
220
|
+
end
|
221
|
+
|
222
|
+
j = i - 1
|
223
|
+
while j >= 0 && start_positions[j].nil?
|
224
|
+
start_positions[j] = start_positions[i]
|
225
|
+
j -= 1
|
226
|
+
end
|
227
|
+
|
228
|
+
pos += 32
|
229
|
+
else
|
230
|
+
outputs[i] = zero_padding data, pos, t.size, start_positions
|
231
|
+
pos += t.size
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# We add a start position equal to the length of the entire data for
|
236
|
+
# convenience.
|
237
|
+
j = types.size - 1
|
238
|
+
while j >= 0 && start_positions[j].nil?
|
239
|
+
start_positions[j] = start_positions[types.size]
|
240
|
+
j -= 1
|
241
|
+
end
|
242
|
+
|
243
|
+
if raise_errors && pos > data.size
|
244
|
+
raise DecodingError, "Not enough data for head"
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
parsed_types.each_with_index do |t, i|
|
249
|
+
if t.dynamic?
|
250
|
+
offset, next_offset = start_positions[i, 2]
|
251
|
+
if offset<=data.size && next_offset<=data.size
|
252
|
+
outputs[i] = data[offset...next_offset]
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
if raise_errors && outputs.include?(nil)
|
258
|
+
raise DecodingError, "Not all data can be parsed"
|
259
|
+
end
|
260
|
+
|
261
|
+
parsed_types.zip(outputs).map {|(type, out)| decode_type(type, out) }
|
262
|
+
end
|
263
|
+
alias :decode :decode_abi
|
264
|
+
|
265
|
+
def zero_padding data, pos, count, start_positions
|
266
|
+
if pos >= data.size
|
267
|
+
start_positions[start_positions.size-1] += count
|
268
|
+
"\x00"*count
|
269
|
+
elsif pos + count > data.size
|
270
|
+
start_positions[start_positions.size-1] += ( count - (data.size-pos))
|
271
|
+
data[pos,data.size-pos] + "\x00"*( count - (data.size-pos))
|
272
|
+
else
|
273
|
+
data[pos, count]
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def decode_typed_data type_name, data
|
278
|
+
decode_primitive_type Type.parse(type_name), data
|
279
|
+
end
|
280
|
+
|
281
|
+
def decode_type(type, arg)
|
282
|
+
return nil if arg.nil? || arg.empty?
|
283
|
+
if type.kind_of?(Tuple) && type.dims.empty?
|
284
|
+
arg ? decode_abi(type.types, arg) : []
|
285
|
+
elsif %w(string bytes).include?(type.base) && type.sub.empty?
|
286
|
+
l = Utils.big_endian_to_int arg[0,32]
|
287
|
+
data = arg[32..-1]
|
288
|
+
data[0, l]
|
289
|
+
elsif !type.dims.empty? && (l = type.dims.last)>0 # static-sized arrays
|
290
|
+
subtype = type.subtype
|
291
|
+
if subtype.dynamic?
|
292
|
+
start_positions = (0...l).map {|i| Utils.big_endian_to_int(arg[32*i, 32]) }
|
293
|
+
start_positions.push arg.size
|
294
|
+
|
295
|
+
outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] }
|
296
|
+
|
297
|
+
outputs.map {|out| decode_type(subtype, out) }
|
298
|
+
else
|
299
|
+
(0...l).map {|i| decode_type(subtype, arg[subtype.size*i, subtype.size]) }
|
300
|
+
end
|
301
|
+
|
302
|
+
elsif type.dynamic?
|
303
|
+
l = Utils.big_endian_to_int arg[0,32]
|
304
|
+
raise DecodingError, "Too long length: #{l}" if l>100000
|
305
|
+
subtype = type.subtype
|
306
|
+
|
307
|
+
if subtype.dynamic?
|
308
|
+
raise DecodingError, "Not enough data for head" unless arg.size >= 32 + 32*l
|
309
|
+
|
310
|
+
start_positions = (1..l).map {|i| 32+Utils.big_endian_to_int(arg[32*i, 32]) }
|
311
|
+
start_positions.push arg.size
|
312
|
+
|
313
|
+
outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] }
|
314
|
+
|
315
|
+
outputs.map {|out| decode_type(subtype, out) }
|
316
|
+
else
|
317
|
+
(0...l).map {|i| decode_type(subtype, arg[32 + subtype.size*i, subtype.size]) }
|
318
|
+
end
|
319
|
+
|
320
|
+
else
|
321
|
+
decode_primitive_type type, arg
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def decode_primitive_type(type, data)
|
326
|
+
case type.base
|
327
|
+
when 'address'
|
328
|
+
Utils.encode_hex data[12..-1]
|
329
|
+
when 'string', 'bytes'
|
330
|
+
if type.sub.empty? # dynamic
|
331
|
+
if data.length==32
|
332
|
+
data[0..32]
|
333
|
+
else
|
334
|
+
size = Utils.big_endian_to_int data[0,32]
|
335
|
+
data[32..-1][0,size]
|
336
|
+
end
|
337
|
+
else # fixed
|
338
|
+
data[0, type.sub.to_i]
|
339
|
+
end
|
340
|
+
when 'hash'
|
341
|
+
data[(32 - type.sub.to_i), type.sub.to_i]
|
342
|
+
when 'uint'
|
343
|
+
Utils.big_endian_to_int data
|
344
|
+
when 'int'
|
345
|
+
u = Utils.big_endian_to_int data
|
346
|
+
u >= 2**(type.sub.to_i-1) ? (u - 2**type.sub.to_i) : u
|
347
|
+
when 'ufixed'
|
348
|
+
high, low = type.sub.split('x').map(&:to_i)
|
349
|
+
Utils.big_endian_to_int(data) * 1.0 / 2**low
|
350
|
+
when 'fixed'
|
351
|
+
high, low = type.sub.split('x').map(&:to_i)
|
352
|
+
u = Utils.big_endian_to_int data
|
353
|
+
i = u >= 2**(high+low-1) ? (u - 2**(high+low)) : u
|
354
|
+
i * 1.0 / 2**low
|
355
|
+
when 'bool'
|
356
|
+
data[-1] == BYTE_ONE
|
357
|
+
else
|
358
|
+
raise DecodingError, "Unknown primitive type: #{type.base}"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
private
|
363
|
+
|
364
|
+
def get_uint(n)
|
365
|
+
case n
|
366
|
+
when Integer
|
367
|
+
raise EncodingError, "Number out of range: #{n}" if n > UINT_MAX || n < UINT_MIN
|
368
|
+
n
|
369
|
+
when String
|
370
|
+
i = if n.size == 40
|
371
|
+
Utils.decode_hex(n)
|
372
|
+
elsif n.size <= 32
|
373
|
+
n
|
374
|
+
else
|
375
|
+
raise EncodingError, "String too long: #{n}"
|
376
|
+
end
|
377
|
+
i = Utils.big_endian_to_int i
|
378
|
+
|
379
|
+
raise EncodingError, "Number out of range: #{i}" if i > UINT_MAX || i < UINT_MIN
|
380
|
+
i
|
381
|
+
when true
|
382
|
+
1
|
383
|
+
when false, nil
|
384
|
+
0
|
385
|
+
else
|
386
|
+
raise EncodingError, "Cannot decode uint: #{n}"
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
def get_int(n)
|
391
|
+
case n
|
392
|
+
when Integer
|
393
|
+
raise EncodingError, "Number out of range: #{n}" if n > INT_MAX || n < INT_MIN
|
394
|
+
n
|
395
|
+
when String
|
396
|
+
i = if n.size == 40
|
397
|
+
Utils.decode_hex(n)
|
398
|
+
elsif n.size <= 32
|
399
|
+
n
|
400
|
+
else
|
401
|
+
raise EncodingError, "String too long: #{n}"
|
402
|
+
end
|
403
|
+
i = Utils.big_endian_to_int i
|
404
|
+
|
405
|
+
i = i > INT_MAX ? (i-TT256) : i
|
406
|
+
raise EncodingError, "Number out of range: #{i}" if i > INT_MAX || i < INT_MIN
|
407
|
+
i
|
408
|
+
when true
|
409
|
+
1
|
410
|
+
when false, nil
|
411
|
+
0
|
412
|
+
else
|
413
|
+
raise EncodingError, "Cannot decode int: #{n}"
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
end
|
418
|
+
|
419
|
+
end # module Abi
|
420
|
+
end # module Ethlite
|
421
|
+
|
422
|
+
|
423
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
module Ethlite
|
3
|
+
module Abi
|
4
|
+
module Constant
|
5
|
+
|
6
|
+
BYTE_EMPTY = "".freeze
|
7
|
+
BYTE_ZERO = "\x00".freeze
|
8
|
+
BYTE_ONE = "\x01".freeze
|
9
|
+
|
10
|
+
TT32 = 2**32
|
11
|
+
TT40 = 2**40
|
12
|
+
TT160 = 2**160
|
13
|
+
TT256 = 2**256
|
14
|
+
TT64M1 = 2**64 - 1
|
15
|
+
|
16
|
+
UINT_MAX = 2**256 - 1
|
17
|
+
UINT_MIN = 0
|
18
|
+
INT_MAX = 2**255 - 1
|
19
|
+
INT_MIN = -2**255
|
20
|
+
|
21
|
+
HASH_ZERO = ("\x00"*32).freeze
|
22
|
+
|
23
|
+
PUBKEY_ZERO = ("\x00"*32).freeze
|
24
|
+
PRIVKEY_ZERO = ("\x00"*32).freeze
|
25
|
+
PRIVKEY_ZERO_HEX = ('0'*64).freeze
|
26
|
+
|
27
|
+
CONTRACT_CODE_SIZE_LIMIT = 0x6000
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end # module Abi
|
32
|
+
end # module Ethlite
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module Ethlite
|
3
|
+
module Abi
|
4
|
+
|
5
|
+
class DeprecatedError < StandardError; end
|
6
|
+
class ChecksumError < StandardError; end
|
7
|
+
class FormatError < StandardError; end
|
8
|
+
class ValidationError < StandardError; end
|
9
|
+
class ValueError < StandardError; end
|
10
|
+
class AssertError < StandardError; end
|
11
|
+
|
12
|
+
class UnknownParentError < StandardError; end
|
13
|
+
class InvalidBlock < ValidationError; end
|
14
|
+
class InvalidUncles < ValidationError; end
|
15
|
+
|
16
|
+
class InvalidTransaction < ValidationError; end
|
17
|
+
class UnsignedTransactionError < InvalidTransaction; end
|
18
|
+
class InvalidNonce < InvalidTransaction; end
|
19
|
+
class InsufficientStartGas < InvalidTransaction; end
|
20
|
+
class InsufficientBalance < InvalidTransaction; end
|
21
|
+
class BlockGasLimitReached < InvalidTransaction; end
|
22
|
+
|
23
|
+
class InvalidSPVProof < ValidationError; end
|
24
|
+
|
25
|
+
class ContractCreationFailed < StandardError; end
|
26
|
+
class TransactionFailed < StandardError; end
|
27
|
+
|
28
|
+
end # module Abi
|
29
|
+
end # module Ethlite
|
@@ -0,0 +1,203 @@
|
|
1
|
+
module Ethlite
|
2
|
+
module Abi
|
3
|
+
class Type
|
4
|
+
|
5
|
+
class ParseError < StandardError;
|
6
|
+
end
|
7
|
+
|
8
|
+
class <<self
|
9
|
+
##
|
10
|
+
# Crazy regexp to seperate out base type component (eg. uint), size (eg.
|
11
|
+
# 256, 128x128, nil), array component (eg. [], [45], nil)
|
12
|
+
#
|
13
|
+
def parse(type)
|
14
|
+
|
15
|
+
return parse('uint256') if type=='trcToken'
|
16
|
+
|
17
|
+
if type =~ /^\((.*)\)((\[[0-9]*\])*)/
|
18
|
+
return Tuple.parse $1, $2.scan(/\[[0-9]*\]/)
|
19
|
+
end
|
20
|
+
|
21
|
+
_, base, sub, dimension = /([a-z]*)([0-9]*x?[0-9]*)((\[[0-9]*\])*)/.match(type).to_a
|
22
|
+
|
23
|
+
dims = dimension.scan(/\[[0-9]*\]/)
|
24
|
+
raise ParseError, "Unknown characters found in array declaration" if dims.join != dimension
|
25
|
+
|
26
|
+
case base
|
27
|
+
when ''
|
28
|
+
return parse 'address'
|
29
|
+
when 'bytes', 'string'
|
30
|
+
raise ParseError, "Maximum 32 bytes for fixed-length string or bytes" unless sub.empty? || sub.to_i <= 32
|
31
|
+
when 'uint', 'int'
|
32
|
+
raise ParseError, "Integer type must have numerical suffix" unless sub =~ /\A[0-9]+\z/
|
33
|
+
|
34
|
+
size = sub.to_i
|
35
|
+
raise ParseError, "Integer size out of bounds" unless size >= 8 && size <= 256
|
36
|
+
raise ParseError, "Integer size must be multiple of 8" unless size % 8 == 0
|
37
|
+
when 'fixed', 'ufixed'
|
38
|
+
raise ParseError, "Fixed type must have suffix of form <high>x<low>, e.g. 128x128" unless sub =~ /\A[0-9]+x[0-9]+\z/
|
39
|
+
|
40
|
+
high, low = sub.split('x').map(&:to_i)
|
41
|
+
total = high + low
|
42
|
+
|
43
|
+
raise ParseError, "Fixed size out of bounds (max 32 bytes)" unless total >= 8 && total <= 256
|
44
|
+
raise ParseError, "Fixed high size must be multiple of 8" unless high % 8 == 0
|
45
|
+
raise ParseError, "Low sizes must be 0 to 80" unless low>0 && low<=80
|
46
|
+
when 'hash'
|
47
|
+
raise ParseError, "Hash type must have numerical suffix" unless sub =~ /\A[0-9]+\z/
|
48
|
+
when 'address'
|
49
|
+
raise ParseError, "Address cannot have suffix" unless sub.empty?
|
50
|
+
when 'bool'
|
51
|
+
raise ParseError, "Bool cannot have suffix" unless sub.empty?
|
52
|
+
else
|
53
|
+
raise ParseError, "Unrecognized type base: #{base}"
|
54
|
+
end
|
55
|
+
|
56
|
+
new(base, sub, dims.map {|x| x[1...-1].to_i})
|
57
|
+
end
|
58
|
+
|
59
|
+
def size_type
|
60
|
+
@size_type ||= new('uint', 256, [])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
attr :base, :sub, :dims
|
65
|
+
|
66
|
+
##
|
67
|
+
# @param base [String] base name of type, e.g. uint for uint256[4]
|
68
|
+
# @param sub [String] subscript of type, e.g. 256 for uint256[4]
|
69
|
+
# @param dims [Array[Integer]] dimensions of array type, e.g. [1,2,0]
|
70
|
+
# for uint256[1][2][], [] for non-array type
|
71
|
+
#
|
72
|
+
def initialize(base, sub, dims)
|
73
|
+
@base = base
|
74
|
+
@sub = sub
|
75
|
+
@dims = dims
|
76
|
+
end
|
77
|
+
|
78
|
+
def ==(another_type)
|
79
|
+
base == another_type.base &&
|
80
|
+
sub == another_type.sub &&
|
81
|
+
dims == another_type.dims
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Get the static size of a type, or nil if dynamic.
|
86
|
+
#
|
87
|
+
# @return [Integer, NilClass] size of static type, or nil for dynamic
|
88
|
+
# type
|
89
|
+
#
|
90
|
+
def size
|
91
|
+
@size ||= if dims.empty?
|
92
|
+
if %w(string bytes).include?(base) && sub.empty?
|
93
|
+
nil
|
94
|
+
else
|
95
|
+
32
|
96
|
+
end
|
97
|
+
else
|
98
|
+
if dims.last == 0 # 0 for dynamic array []
|
99
|
+
nil
|
100
|
+
else
|
101
|
+
subtype.dynamic? ? nil : dims.last * subtype.size
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def dynamic?
|
107
|
+
size.nil?
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Type with one dimension lesser.
|
112
|
+
#
|
113
|
+
# @example
|
114
|
+
# Type.parse("uint256[2][]").subtype # => Type.new('uint', 256, [2])
|
115
|
+
#
|
116
|
+
# @return [Ethereum::ABI::Type]
|
117
|
+
#
|
118
|
+
def subtype
|
119
|
+
@subtype ||= self.class.new(base, sub, dims[0...-1])
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
class Tuple < Type
|
125
|
+
|
126
|
+
def self.parse types, dims
|
127
|
+
|
128
|
+
depth = 0
|
129
|
+
collected = []
|
130
|
+
current = ''
|
131
|
+
|
132
|
+
types.split('').each do |c|
|
133
|
+
case c
|
134
|
+
when ',' then
|
135
|
+
if depth==0
|
136
|
+
collected << current
|
137
|
+
current = ''
|
138
|
+
else
|
139
|
+
current += c
|
140
|
+
end
|
141
|
+
when '(' then
|
142
|
+
depth += 1
|
143
|
+
current += c
|
144
|
+
when ')' then
|
145
|
+
depth -= 1
|
146
|
+
current += c
|
147
|
+
else
|
148
|
+
current += c
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
collected << current unless current.empty?
|
153
|
+
|
154
|
+
Tuple.new collected, dims.map {|x| x[1...-1].to_i}
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
attr_reader :types, :parsed_types
|
159
|
+
def initialize types, dims
|
160
|
+
super('tuple', '', dims)
|
161
|
+
@types = types
|
162
|
+
@parsed_types = types.map{|t| Type.parse t}
|
163
|
+
end
|
164
|
+
|
165
|
+
def ==(another_type)
|
166
|
+
another_type.kind_of?(Tuple) &&
|
167
|
+
another_type.types == types &&
|
168
|
+
another_type.dims == dims
|
169
|
+
end
|
170
|
+
|
171
|
+
def size
|
172
|
+
@size ||= calculate_size
|
173
|
+
end
|
174
|
+
|
175
|
+
def calculate_size
|
176
|
+
if dims.empty?
|
177
|
+
s = 0
|
178
|
+
parsed_types.each do |type|
|
179
|
+
ts = type.size
|
180
|
+
return nil if ts.nil?
|
181
|
+
s += ts
|
182
|
+
end
|
183
|
+
s
|
184
|
+
else
|
185
|
+
if dims.last == 0 # 0 for dynamic array []
|
186
|
+
nil
|
187
|
+
else
|
188
|
+
subtype.dynamic? ? nil : dims.last * subtype.size
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
def subtype
|
196
|
+
@subtype ||= Tuple.new(types, dims[0...-1])
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
end # module Abi
|
203
|
+
end # module Ethlite
|
@@ -0,0 +1,223 @@
|
|
1
|
+
module Ethlite
|
2
|
+
module Abi
|
3
|
+
module Utils
|
4
|
+
|
5
|
+
extend self
|
6
|
+
|
7
|
+
include Constant
|
8
|
+
|
9
|
+
##
|
10
|
+
# Not the keccak in sha3, although it's underlying lib named SHA3
|
11
|
+
#
|
12
|
+
def keccak256(x)
|
13
|
+
# Digest::SHA3.new(256).digest(x)
|
14
|
+
Digest::Keccak.digest(x, 256)
|
15
|
+
end
|
16
|
+
|
17
|
+
def keccak512(x)
|
18
|
+
# Digest::SHA3.new(512).digest(x)
|
19
|
+
Digest::Keccak.digest(x, 512)
|
20
|
+
end
|
21
|
+
|
22
|
+
def keccak256_rlp(x)
|
23
|
+
keccak256 RLP.encode(x)
|
24
|
+
end
|
25
|
+
|
26
|
+
def sha256(x)
|
27
|
+
Digest::SHA256.digest x
|
28
|
+
end
|
29
|
+
|
30
|
+
def double_sha256(x)
|
31
|
+
sha256 sha256(x)
|
32
|
+
end
|
33
|
+
|
34
|
+
def ripemd160(x)
|
35
|
+
Digest::RMD160.digest x
|
36
|
+
end
|
37
|
+
|
38
|
+
def hash160(x)
|
39
|
+
ripemd160 sha256(x)
|
40
|
+
end
|
41
|
+
|
42
|
+
def hash160_hex(x)
|
43
|
+
encode_hex hash160(x)
|
44
|
+
end
|
45
|
+
|
46
|
+
def mod_exp(x, y, n)
|
47
|
+
x.to_bn.mod_exp(y, n).to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
def mod_mul(x, y, n)
|
51
|
+
x.to_bn.mod_mul(y, n).to_i
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_signed(i)
|
55
|
+
i > Constant::INT_MAX ? (i-Constant::TT256) : i
|
56
|
+
end
|
57
|
+
|
58
|
+
def base58_check_to_bytes(s)
|
59
|
+
leadingzbytes = s.match(/\A1*/)[0]
|
60
|
+
data = Constant::BYTE_ZERO * leadingzbytes.size + BaseConvert.convert(s, 58, 256)
|
61
|
+
|
62
|
+
raise ChecksumError, "double sha256 checksum doesn't match" unless double_sha256(data[0...-4])[0,4] == data[-4..-1]
|
63
|
+
data[1...-4]
|
64
|
+
end
|
65
|
+
|
66
|
+
def bytes_to_base58_check(bytes, magicbyte=0)
|
67
|
+
bs = "#{magicbyte.chr}#{bytes}"
|
68
|
+
leadingzbytes = bs.match(/\A#{Constant::BYTE_ZERO}*/)[0]
|
69
|
+
checksum = double_sha256(bs)[0,4]
|
70
|
+
'1'*leadingzbytes.size + BaseConvert.convert("#{bs}#{checksum}", 256, 58)
|
71
|
+
end
|
72
|
+
|
73
|
+
def ceil32(x)
|
74
|
+
x % 32 == 0 ? x : (x + 32 - x%32)
|
75
|
+
end
|
76
|
+
|
77
|
+
def encode_hex(b)
|
78
|
+
RLP::Utils.encode_hex b
|
79
|
+
end
|
80
|
+
|
81
|
+
def decode_hex(s)
|
82
|
+
RLP::Utils.decode_hex s
|
83
|
+
end
|
84
|
+
|
85
|
+
def big_endian_to_int(s)
|
86
|
+
RLP::Sedes.big_endian_int.deserialize s.sub(/\A(\x00)+/, '')
|
87
|
+
end
|
88
|
+
|
89
|
+
def int_to_big_endian(n)
|
90
|
+
RLP::Sedes.big_endian_int.serialize n
|
91
|
+
end
|
92
|
+
|
93
|
+
def lpad(x, symbol, l)
|
94
|
+
return x if x.size >= l
|
95
|
+
symbol * (l - x.size) + x
|
96
|
+
end
|
97
|
+
|
98
|
+
def rpad(x, symbol, l)
|
99
|
+
return x if x.size >= l
|
100
|
+
x + symbol * (l - x.size)
|
101
|
+
end
|
102
|
+
|
103
|
+
def zpad(x, l)
|
104
|
+
lpad x, BYTE_ZERO, l
|
105
|
+
end
|
106
|
+
|
107
|
+
def zunpad(x)
|
108
|
+
x.sub /\A\x00+/, ''
|
109
|
+
end
|
110
|
+
|
111
|
+
def zpad_int(n, l=32)
|
112
|
+
zpad encode_int(n), l
|
113
|
+
end
|
114
|
+
|
115
|
+
def zpad_hex(s, l=32)
|
116
|
+
zpad decode_hex(s), l
|
117
|
+
end
|
118
|
+
|
119
|
+
def int_to_addr(x)
|
120
|
+
zpad_int x, 20
|
121
|
+
end
|
122
|
+
|
123
|
+
def encode_int(n)
|
124
|
+
raise ArgumentError, "Integer invalid or out of range: #{n}" unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
|
125
|
+
int_to_big_endian n
|
126
|
+
end
|
127
|
+
|
128
|
+
def decode_int(v)
|
129
|
+
raise ArgumentError, "No leading zero bytes allowed for integers" if v.size > 0 && (v[0] == Constant::BYTE_ZERO || v[0] == 0)
|
130
|
+
big_endian_to_int v
|
131
|
+
end
|
132
|
+
|
133
|
+
def bytearray_to_int(arr)
|
134
|
+
o = 0
|
135
|
+
arr.each {|x| o = (o << 8) + x }
|
136
|
+
o
|
137
|
+
end
|
138
|
+
|
139
|
+
def int_array_to_bytes(arr)
|
140
|
+
arr.pack('C*')
|
141
|
+
end
|
142
|
+
|
143
|
+
def bytes_to_int_array(bytes)
|
144
|
+
bytes.unpack('C*')
|
145
|
+
end
|
146
|
+
|
147
|
+
def coerce_to_int(x)
|
148
|
+
if x.is_a?(Numeric)
|
149
|
+
x
|
150
|
+
elsif x.size == 40
|
151
|
+
big_endian_to_int decode_hex(x)
|
152
|
+
else
|
153
|
+
big_endian_to_int x
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def coerce_to_bytes(x)
|
158
|
+
if x.is_a?(Numeric)
|
159
|
+
int_to_big_endian x
|
160
|
+
elsif x.size == 40
|
161
|
+
decode_hex(x)
|
162
|
+
else
|
163
|
+
x
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def coerce_addr_to_hex(x)
|
168
|
+
if x.is_a?(Numeric)
|
169
|
+
encode_hex zpad(int_to_big_endian(x), 20)
|
170
|
+
elsif x.size == 40 || x.size == 0
|
171
|
+
x
|
172
|
+
else
|
173
|
+
encode_hex zpad(x, 20)[-20..-1]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def normalize_address(x, allow_blank: false)
|
178
|
+
address = Address.new(x)
|
179
|
+
raise ValueError, "address is blank" if !allow_blank && address.blank?
|
180
|
+
address.to_bytes
|
181
|
+
end
|
182
|
+
|
183
|
+
def mk_contract_address(sender, nonce)
|
184
|
+
keccak256_rlp([normalize_address(sender), nonce])[12..-1]
|
185
|
+
end
|
186
|
+
|
187
|
+
def mk_metropolis_contract_address(sender, initcode)
|
188
|
+
keccak256(normalize_address(sender) + initcode)[12..-1]
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
def parse_int_or_hex(s)
|
193
|
+
if s.is_a?(Numeric)
|
194
|
+
s
|
195
|
+
elsif s[0,2] == '0x'
|
196
|
+
big_endian_to_int decode_hex(normalize_hex_without_prefix(s))
|
197
|
+
else
|
198
|
+
s.to_i
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def normalize_hex_without_prefix(s)
|
203
|
+
if s[0,2] == '0x'
|
204
|
+
(s.size % 2 == 1 ? '0' : '') + s[2..-1]
|
205
|
+
else
|
206
|
+
s
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def function_signature method_name, arg_types
|
211
|
+
"#{method_name}(#{arg_types.join(',')})"
|
212
|
+
end
|
213
|
+
|
214
|
+
def signature_hash signature, length=64
|
215
|
+
encode_hex(keccak256(signature))[0...length]
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
end # module Abi
|
222
|
+
end # module Ethlite
|
223
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Ethlite
|
2
|
+
class ContractMethod
|
3
|
+
include Utility
|
4
|
+
|
5
|
+
attr_reader :abi,
|
6
|
+
:signature,
|
7
|
+
:name,
|
8
|
+
:signature_hash,
|
9
|
+
:input_types,
|
10
|
+
:output_types,
|
11
|
+
:constant
|
12
|
+
|
13
|
+
def initialize( abi )
|
14
|
+
## convenience helper - auto-convert to json if string passed in
|
15
|
+
abi = JSON.parse( abi ) if abi.is_a?( String )
|
16
|
+
|
17
|
+
@abi = abi
|
18
|
+
@name = abi['name']
|
19
|
+
@constant = !!abi['constant'] || abi['stateMutability']=='view'
|
20
|
+
@input_types = abi['inputs'] ? abi['inputs'].map{|a| parse_component_type a } : []
|
21
|
+
@output_types = abi['outputs'] ? abi['outputs'].map{|a| parse_component_type a } : nil
|
22
|
+
@signature = Abi::Utils.function_signature( @name, @input_types )
|
23
|
+
@signature_hash = Abi::Utils.signature_hash( @signature, abi['type']=='event' ? 64 : 8)
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_component_type( argument )
|
27
|
+
if argument['type'] =~ /^tuple((\[[0-9]*\])*)/
|
28
|
+
argument['components'] ? "(#{argument['components'].collect{|c| parse_component_type( c ) }.join(',')})#{$1}"
|
29
|
+
: "()#{$1}"
|
30
|
+
else
|
31
|
+
argument['type']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def do_call( rpc, contract_address, args )
|
37
|
+
data = '0x' + @signature_hash + Abi::Utils.encode_hex(
|
38
|
+
Abi::AbiCoder.encode_abi(@input_types, args) )
|
39
|
+
|
40
|
+
method = 'eth_call'
|
41
|
+
params = [{ to: contract_address,
|
42
|
+
data: data},
|
43
|
+
'latest']
|
44
|
+
response = rpc.request( method, params )
|
45
|
+
|
46
|
+
|
47
|
+
puts "response:"
|
48
|
+
pp response
|
49
|
+
|
50
|
+
string_data = [remove_0x_head(response)].pack('H*')
|
51
|
+
return nil if string_data.empty?
|
52
|
+
|
53
|
+
result = Abi::AbiCoder.decode_abi( @output_types, string_data )
|
54
|
+
puts
|
55
|
+
puts "result decode_abi:"
|
56
|
+
pp result
|
57
|
+
|
58
|
+
|
59
|
+
result.length==1 ? result.first : result
|
60
|
+
end
|
61
|
+
|
62
|
+
end # class ContractMethod
|
63
|
+
end # module Ethlite
|
64
|
+
|
data/lib/ethlite/rpc.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Ethlite
|
2
|
+
|
3
|
+
class Rpc
|
4
|
+
def initialize( uri )
|
5
|
+
@client_id = Random.rand( 10000000 )
|
6
|
+
|
7
|
+
@uri = URI.parse( uri )
|
8
|
+
end
|
9
|
+
|
10
|
+
def request( method, params=[] )
|
11
|
+
opts = {}
|
12
|
+
if @uri.instance_of?( URI::HTTPS )
|
13
|
+
opts[:use_ssl] = true
|
14
|
+
opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
|
15
|
+
end
|
16
|
+
|
17
|
+
Net::HTTP.start( @uri.host, @uri.port, **opts ) do |http|
|
18
|
+
headers = {"Content-Type" => "application/json"}
|
19
|
+
request = Net::HTTP::Post.new( @uri.request_uri, headers )
|
20
|
+
|
21
|
+
json = { jsonrpc: '2.0',
|
22
|
+
method: method,
|
23
|
+
params: params,
|
24
|
+
id: @client_id }.to_json
|
25
|
+
|
26
|
+
puts "json POST payload:"
|
27
|
+
puts json
|
28
|
+
|
29
|
+
request.body = json
|
30
|
+
response = http.request( request )
|
31
|
+
|
32
|
+
raise "Error code #{response.code} on request #{@uri.to_s} #{request.body}" unless response.kind_of?( Net::HTTPOK )
|
33
|
+
|
34
|
+
body = JSON.parse(response.body, max_nesting: 1500)
|
35
|
+
|
36
|
+
if body['result']
|
37
|
+
body['result']
|
38
|
+
elsif body['error']
|
39
|
+
raise "Error #{@uri.to_s} #{body['error']} on request #{@uri.to_s} #{request.body}"
|
40
|
+
else
|
41
|
+
raise "No response on request #{@uri.to_s} #{request.body}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end # class Rpc
|
46
|
+
|
47
|
+
end # module Ethlite
|
48
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Ethlite
|
2
|
+
|
3
|
+
module Utility
|
4
|
+
|
5
|
+
def hex( num )
|
6
|
+
'0x' + num.to_s(16)
|
7
|
+
end
|
8
|
+
|
9
|
+
def from_hex( h )
|
10
|
+
h.nil? ? 0 : (h.kind_of?(String) ? h.to_i(16) : h)
|
11
|
+
end
|
12
|
+
|
13
|
+
def remove_0x_head( s )
|
14
|
+
return s if !s || s.length<2
|
15
|
+
s[0,2] == '0x' ? s[2..-1] : s
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def wei_to_ether( wei )
|
20
|
+
1.0 * wei / 10**18
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end # module Ethlite
|
data/lib/ethlite/version.rb
CHANGED
data/lib/ethlite.rb
CHANGED
@@ -1,9 +1,37 @@
|
|
1
1
|
require 'cocos'
|
2
2
|
|
3
|
+
require 'uri'
|
4
|
+
require 'net/http'
|
5
|
+
require 'net/https'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
|
9
|
+
require 'openssl'
|
10
|
+
require 'digest'
|
11
|
+
|
12
|
+
## 3rd party gems
|
13
|
+
require 'digest/keccak' ## gem keccak - see https://rubygems.org/gems/keccak
|
14
|
+
require 'rlp' ## gem rlp - see https://rubygems.org/gems/rlp
|
15
|
+
|
16
|
+
|
17
|
+
|
3
18
|
## our own code
|
4
19
|
require_relative 'ethlite/version' # note: let version always go first
|
5
20
|
|
6
21
|
|
22
|
+
require_relative 'ethlite/abi/type'
|
23
|
+
require_relative 'ethlite/abi/constant'
|
24
|
+
require_relative 'ethlite/abi/exceptions'
|
25
|
+
require_relative 'ethlite/abi/utils'
|
26
|
+
require_relative 'ethlite/abi/abi_coder'
|
27
|
+
|
28
|
+
|
29
|
+
require_relative 'ethlite/rpc'
|
30
|
+
|
31
|
+
require_relative 'ethlite/utility'
|
32
|
+
require_relative 'ethlite/contract'
|
33
|
+
|
34
|
+
|
7
35
|
|
8
36
|
## add convenience alternate spelling
|
9
37
|
EthLite = Ethlite
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ethlite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: keccak
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rlp
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: rdoc
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -73,6 +101,14 @@ files:
|
|
73
101
|
- README.md
|
74
102
|
- Rakefile
|
75
103
|
- lib/ethlite.rb
|
104
|
+
- lib/ethlite/abi/abi_coder.rb
|
105
|
+
- lib/ethlite/abi/constant.rb
|
106
|
+
- lib/ethlite/abi/exceptions.rb
|
107
|
+
- lib/ethlite/abi/type.rb
|
108
|
+
- lib/ethlite/abi/utils.rb
|
109
|
+
- lib/ethlite/contract.rb
|
110
|
+
- lib/ethlite/rpc.rb
|
111
|
+
- lib/ethlite/utility.rb
|
76
112
|
- lib/ethlite/version.rb
|
77
113
|
homepage: https://github.com/pixelartexchange/artbase
|
78
114
|
licenses:
|