eth-custom 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +18 -0
  3. data/.github/workflows/codeql.yml +48 -0
  4. data/.github/workflows/docs.yml +26 -0
  5. data/.github/workflows/spec.yml +52 -0
  6. data/.gitignore +43 -0
  7. data/.gitmodules +3 -0
  8. data/.rspec +4 -0
  9. data/.yardopts +1 -0
  10. data/AUTHORS.txt +29 -0
  11. data/CHANGELOG.md +218 -0
  12. data/Gemfile +17 -0
  13. data/LICENSE.txt +202 -0
  14. data/README.md +347 -0
  15. data/Rakefile +6 -0
  16. data/bin/console +10 -0
  17. data/bin/setup +9 -0
  18. data/codecov.yml +6 -0
  19. data/eth.gemspec +51 -0
  20. data/lib/eth/abi/event.rb +137 -0
  21. data/lib/eth/abi/type.rb +178 -0
  22. data/lib/eth/abi.rb +446 -0
  23. data/lib/eth/address.rb +106 -0
  24. data/lib/eth/api.rb +223 -0
  25. data/lib/eth/chain.rb +157 -0
  26. data/lib/eth/client/http.rb +63 -0
  27. data/lib/eth/client/ipc.rb +50 -0
  28. data/lib/eth/client.rb +499 -0
  29. data/lib/eth/constant.rb +71 -0
  30. data/lib/eth/contract/event.rb +42 -0
  31. data/lib/eth/contract/function.rb +57 -0
  32. data/lib/eth/contract/function_input.rb +38 -0
  33. data/lib/eth/contract/function_output.rb +37 -0
  34. data/lib/eth/contract/initializer.rb +47 -0
  35. data/lib/eth/contract.rb +143 -0
  36. data/lib/eth/eip712.rb +184 -0
  37. data/lib/eth/key/decrypter.rb +146 -0
  38. data/lib/eth/key/encrypter.rb +207 -0
  39. data/lib/eth/key.rb +167 -0
  40. data/lib/eth/rlp/decoder.rb +114 -0
  41. data/lib/eth/rlp/encoder.rb +78 -0
  42. data/lib/eth/rlp/sedes/big_endian_int.rb +66 -0
  43. data/lib/eth/rlp/sedes/binary.rb +97 -0
  44. data/lib/eth/rlp/sedes/list.rb +84 -0
  45. data/lib/eth/rlp/sedes.rb +74 -0
  46. data/lib/eth/rlp.rb +63 -0
  47. data/lib/eth/signature.rb +163 -0
  48. data/lib/eth/solidity.rb +75 -0
  49. data/lib/eth/tx/eip1559.rb +337 -0
  50. data/lib/eth/tx/eip2930.rb +329 -0
  51. data/lib/eth/tx/legacy.rb +297 -0
  52. data/lib/eth/tx.rb +322 -0
  53. data/lib/eth/unit.rb +49 -0
  54. data/lib/eth/util.rb +235 -0
  55. data/lib/eth/version.rb +20 -0
  56. data/lib/eth.rb +35 -0
  57. metadata +184 -0
data/lib/eth/abi.rb ADDED
@@ -0,0 +1,446 @@
1
+ # Copyright (c) 2016-2022 The Ruby-Eth Contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # -*- encoding : ascii-8bit -*-
16
+
17
+ require "konstructor"
18
+
19
+ require "eth/abi/event"
20
+ require "eth/abi/type"
21
+
22
+ # Provides the {Eth} module.
23
+ module Eth
24
+
25
+ # Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
26
+ # ref: https://docs.soliditylang.org/en/develop/abi-spec.html
27
+ module Abi
28
+ extend self
29
+
30
+ # Provides a special encoding error if anything fails to encode.
31
+ class EncodingError < StandardError; end
32
+
33
+ # Provides a special decoding error if anything fails to decode.
34
+ class DecodingError < StandardError; end
35
+
36
+ # Provides a special out-of-bounds error for values.
37
+ class ValueOutOfBounds < StandardError; end
38
+
39
+ # Encodes Application Binary Interface (ABI) data. It accepts multiple
40
+ # arguments and encodes using the head/tail mechanism.
41
+ #
42
+ # @param types [Array] types to be ABI-encoded.
43
+ # @param args [Array] values to be ABI-encoded.
44
+ # @return [String] the encoded ABI data.
45
+ def encode(types, args)
46
+
47
+ # parse all types
48
+ parsed_types = types.map { |t| Type.parse(t) }
49
+
50
+ # prepare the "head"
51
+ head_size = (0...args.size)
52
+ .map { |i| parsed_types[i].size or 32 }
53
+ .reduce(0, &:+)
54
+ head, tail = "", ""
55
+
56
+ # encode types and arguments
57
+ args.each_with_index do |arg, i|
58
+ if parsed_types[i].is_dynamic?
59
+ head += encode_type Type.size_type, head_size + tail.size
60
+ tail += encode_type parsed_types[i], arg
61
+ else
62
+ head += encode_type parsed_types[i], arg
63
+ end
64
+ end
65
+
66
+ # return the encoded ABI blob
67
+ return "#{head}#{tail}"
68
+ end
69
+
70
+ # Encodes a specific value, either static or dynamic.
71
+ #
72
+ # @param type [Eth::Abi::Type] type to be encoded.
73
+ # @param arg [String|Number] value to be encoded.
74
+ # @return [String] the encoded type.
75
+ # @raise [EncodingError] if value does not match type.
76
+ def encode_type(type, arg)
77
+ if %w(string bytes).include? type.base_type and type.sub_type.empty? and type.dimensions.empty?
78
+ raise EncodingError, "Argument must be a String" unless arg.instance_of? String
79
+
80
+ # encodes strings and bytes
81
+ size = encode_type Type.size_type, arg.size
82
+ padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
83
+ return "#{size}#{arg}#{padding}"
84
+ elsif type.is_dynamic?
85
+ raise EncodingError, "Argument must be an Array" unless arg.instance_of? Array
86
+
87
+ # encodes dynamic-sized arrays
88
+ head, tail = "", ""
89
+ head += encode_type Type.size_type, arg.size
90
+ nested_sub = type.nested_sub
91
+ nested_sub_size = type.nested_sub.size
92
+
93
+ # calculate offsets
94
+ if %w(string bytes).include?(type.base_type) && type.sub_type.empty?
95
+ offset = 0
96
+ arg.size.times do |i|
97
+ if i == 0
98
+ offset = arg.size * 32
99
+ else
100
+ number_of_words = ((arg[i - 1].size + 32 - 1) / 32).floor
101
+ total_bytes_length = number_of_words * 32
102
+ offset += total_bytes_length + 32
103
+ end
104
+
105
+ head += encode_type Type.size_type, offset
106
+ end
107
+ end
108
+
109
+ arg.size.times do |i|
110
+ head += encode_type nested_sub, arg[i]
111
+ end
112
+ return "#{head}#{tail}"
113
+ else
114
+ if type.dimensions.empty?
115
+
116
+ # encode a primitive type
117
+ return encode_primitive_type type, arg
118
+ else
119
+
120
+ # encode static-size arrays
121
+ return arg.map { |x| encode_type(type.nested_sub, x) }.join
122
+ end
123
+ end
124
+ end
125
+
126
+ # Encodes primitive types.
127
+ #
128
+ # @param type [Eth::Abi::Type] type to be encoded.
129
+ # @param arg [String|Number] value to be encoded.
130
+ # @return [String] the encoded primitive type.
131
+ # @raise [EncodingError] if value does not match type.
132
+ # @raise [ValueOutOfBounds] if value is out of bounds for type.
133
+ # @raise [EncodingError] if encoding fails for type.
134
+ def encode_primitive_type(type, arg)
135
+ case type.base_type
136
+ when "uint"
137
+ return encode_uint arg, type
138
+ when "bool"
139
+ return encode_bool arg
140
+ when "int"
141
+ return encode_int arg, type
142
+ when "ureal", "ufixed"
143
+ return encode_ufixed arg, type
144
+ when "real", "fixed"
145
+ return encode_fixed arg, type
146
+ when "string", "bytes"
147
+ return encode_bytes arg, type
148
+ when "hash"
149
+ return encode_hash arg, type
150
+ when "address"
151
+ return encode_address arg
152
+ else
153
+ raise EncodingError, "Unhandled type: #{type.base_type} #{type.sub_type}"
154
+ end
155
+ end
156
+
157
+ # Decodes Application Binary Interface (ABI) data. It accepts multiple
158
+ # arguments and decodes using the head/tail mechanism.
159
+ #
160
+ # @param types [Array] the ABI to be decoded.
161
+ # @param data [String] ABI data to be decoded.
162
+ # @return [Array] the decoded ABI data.
163
+ def decode(types, data)
164
+
165
+ # accept hex abi but decode it first
166
+ data = Util.hex_to_bin data if Util.is_hex? data
167
+
168
+ # parse all types
169
+ parsed_types = types.map { |t| Type.parse(t) }
170
+
171
+ # prepare output data
172
+ outputs = [nil] * types.size
173
+ start_positions = [nil] * types.size + [data.size]
174
+ pos = 0
175
+ parsed_types.each_with_index do |t, i|
176
+ if t.is_dynamic?
177
+
178
+ # record start position for dynamic type
179
+ start_positions[i] = Util.deserialize_big_endian_to_int(data[pos, 32])
180
+ j = i - 1
181
+ while j >= 0 and start_positions[j].nil?
182
+ start_positions[j] = start_positions[i]
183
+ j -= 1
184
+ end
185
+ pos += 32
186
+ else
187
+
188
+ # get data directly for static types
189
+ outputs[i] = data[pos, t.size]
190
+ pos += t.size
191
+ end
192
+ end
193
+
194
+ # add start position equal the length of the entire data
195
+ j = types.size - 1
196
+ while j >= 0 and start_positions[j].nil?
197
+ start_positions[j] = start_positions[types.size]
198
+ j -= 1
199
+ end
200
+ raise DecodingError, "Not enough data for head" unless pos <= data.size
201
+
202
+ # add dynamic types
203
+ parsed_types.each_with_index do |t, i|
204
+ if t.is_dynamic?
205
+ offset, next_offset = start_positions[i, 2]
206
+ outputs[i] = data[offset...next_offset]
207
+ end
208
+ end
209
+
210
+ # return the decoded ABI types and data
211
+ return parsed_types.zip(outputs).map { |(type, out)| decode_type(type, out) }
212
+ end
213
+
214
+ # Decodes a specific value, either static or dynamic.
215
+ #
216
+ # @param type [Eth::Abi::Type] type to be decoded.
217
+ # @param arg [String] encoded type data string.
218
+ # @return [String] the decoded data for the type.
219
+ # @raise [DecodingError] if decoding fails for type.
220
+ def decode_type(type, arg)
221
+ if %w(string bytes).include?(type.base_type) and type.sub_type.empty?
222
+ l = Util.deserialize_big_endian_to_int arg[0, 32]
223
+ data = arg[32..-1]
224
+ raise DecodingError, "Wrong data size for string/bytes object" unless data.size == Util.ceil32(l)
225
+
226
+ # decoded strings and bytes
227
+ return data[0, l]
228
+ elsif type.is_dynamic?
229
+ l = Util.deserialize_big_endian_to_int arg[0, 32]
230
+ nested_sub = type.nested_sub
231
+
232
+ # ref https://github.com/ethereum/tests/issues/691
233
+ raise NotImplementedError, "Decoding dynamic arrays with nested dynamic sub-types is not implemented for ABI." if nested_sub.is_dynamic?
234
+
235
+ # decoded dynamic-sized arrays
236
+ return (0...l).map { |i| decode_type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) }
237
+ elsif !type.dimensions.empty?
238
+ l = type.dimensions.last[0]
239
+ nested_sub = type.nested_sub
240
+
241
+ # decoded static-size arrays
242
+ return (0...l).map { |i| decode_type(nested_sub, arg[nested_sub.size * i, nested_sub.size]) }
243
+ else
244
+
245
+ # decoded primitive types
246
+ return decode_primitive_type type, arg
247
+ end
248
+ end
249
+
250
+ # Decodes primitive types.
251
+ #
252
+ # @param type [Eth::Abi::Type] type to be decoded.
253
+ # @param data [String] encoded primitive type data string.
254
+ # @return [String] the decoded data for the type.
255
+ # @raise [DecodingError] if decoding fails for type.
256
+ def decode_primitive_type(type, data)
257
+ case type.base_type
258
+ when "address"
259
+
260
+ # decoded address with 0x-prefix
261
+ return "0x#{Util.bin_to_hex data[12..-1]}"
262
+ when "string", "bytes"
263
+ if type.sub_type.empty?
264
+ size = Util.deserialize_big_endian_to_int data[0, 32]
265
+
266
+ # decoded dynamic-sized array
267
+ return data[32..-1][0, size]
268
+ else
269
+
270
+ # decoded static-sized array
271
+ return data[0, type.sub_type.to_i]
272
+ end
273
+ when "hash"
274
+
275
+ # decoded hash
276
+ return data[(32 - type.sub_type.to_i), type.sub_type.to_i]
277
+ when "uint"
278
+
279
+ # decoded unsigned integer
280
+ return Util.deserialize_big_endian_to_int data
281
+ when "int"
282
+ u = Util.deserialize_big_endian_to_int data
283
+ i = u >= 2 ** (type.sub_type.to_i - 1) ? (u - 2 ** type.sub_type.to_i) : u
284
+
285
+ # decoded integer
286
+ return i
287
+ when "ureal", "ufixed"
288
+ high, low = type.sub_type.split("x").map(&:to_i)
289
+
290
+ # decoded unsigned fixed point numeric
291
+ return Util.deserialize_big_endian_to_int(data) * 1.0 / 2 ** low
292
+ when "real", "fixed"
293
+ high, low = type.sub_type.split("x").map(&:to_i)
294
+ u = Util.deserialize_big_endian_to_int data
295
+ i = u >= 2 ** (high + low - 1) ? (u - 2 ** (high + low)) : u
296
+
297
+ # decoded fixed point numeric
298
+ return i * 1.0 / 2 ** low
299
+ when "bool"
300
+
301
+ # decoded boolean
302
+ return data[-1] == Constant::BYTE_ONE
303
+ else
304
+ raise DecodingError, "Unknown primitive type: #{type.base_type}"
305
+ end
306
+ end
307
+
308
+ # Build event signature string from ABI interface.
309
+ #
310
+ # @param interface [Hash] ABI event interface.
311
+ # @return [String] interface signature string.
312
+ def signature(interface)
313
+ name = interface.fetch("name")
314
+ inputs = interface.fetch("inputs", [])
315
+ types = inputs.map { |i| i.fetch("type") }
316
+ "#{name}(#{types.join(",")})"
317
+ end
318
+
319
+ private
320
+
321
+ # Properly encodes unsigned integers.
322
+ def encode_uint(arg, type)
323
+ raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
324
+ raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::UINT_MAX or arg < Constant::UINT_MIN
325
+ real_size = type.sub_type.to_i
326
+ i = arg.to_i
327
+ raise ValueOutOfBounds, arg unless i >= 0 and i < 2 ** real_size
328
+ return Util.zpad_int i
329
+ end
330
+
331
+ # Properly encodes signed integers.
332
+ def encode_int(arg, type)
333
+ raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
334
+ raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::INT_MAX or arg < Constant::INT_MIN
335
+ real_size = type.sub_type.to_i
336
+ i = arg.to_i
337
+ raise ValueOutOfBounds, arg unless i >= -2 ** (real_size - 1) and i < 2 ** (real_size - 1)
338
+ return Util.zpad_int(i % 2 ** type.sub_type.to_i)
339
+ end
340
+
341
+ # Properly encodes booleans.
342
+ def encode_bool(arg)
343
+ raise EncodingError, "Argument is not bool: #{arg}" unless arg.instance_of? TrueClass or arg.instance_of? FalseClass
344
+ return Util.zpad_int(arg ? 1 : 0)
345
+ end
346
+
347
+ # Properly encodes unsigned fixed-point numbers.
348
+ def encode_ufixed(arg, type)
349
+ raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
350
+ high, low = type.sub_type.split("x").map(&:to_i)
351
+ raise ValueOutOfBounds, arg unless arg >= 0 and arg < 2 ** high
352
+ return Util.zpad_int((arg * 2 ** low).to_i)
353
+ end
354
+
355
+ # Properly encodes signed fixed-point numbers.
356
+ def encode_fixed(arg, type)
357
+ raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
358
+ high, low = type.sub_type.split("x").map(&:to_i)
359
+ raise ValueOutOfBounds, arg unless arg >= -2 ** (high - 1) and arg < 2 ** (high - 1)
360
+ i = (arg * 2 ** low).to_i
361
+ return Util.zpad_int(i % 2 ** (high + low))
362
+ end
363
+
364
+ # Properly encodes byte-strings.
365
+ def encode_bytes(arg, type)
366
+ raise EncodingError, "Expecting String: #{arg}" unless arg.instance_of? String
367
+ arg = handle_hex_string arg, type
368
+
369
+ if type.sub_type.empty?
370
+ size = Util.zpad_int arg.size
371
+ padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
372
+
373
+ # variable length string/bytes
374
+ return "#{size}#{arg}#{padding}"
375
+ else
376
+ raise ValueOutOfBounds, arg unless arg.size <= type.sub_type.to_i
377
+ padding = Constant::BYTE_ZERO * (32 - arg.size)
378
+
379
+ # fixed length string/bytes
380
+ return "#{arg}#{padding}"
381
+ end
382
+ end
383
+
384
+ # Properly encodes hash-strings.
385
+ def encode_hash(arg, type)
386
+ size = type.sub_type.to_i
387
+ raise EncodingError, "Argument too long: #{arg}" unless size > 0 and size <= 32
388
+ if arg.is_a? Integer
389
+
390
+ # hash from integer
391
+ return Util.zpad_int arg
392
+ elsif arg.size == size
393
+
394
+ # hash from encoded hash
395
+ return Util.zpad arg, 32
396
+ elsif arg.size == size * 2
397
+
398
+ # hash from hexa-decimal hash
399
+ return Util.zpad_hex arg
400
+ else
401
+ raise EncodingError, "Could not parse hash: #{arg}"
402
+ end
403
+ end
404
+
405
+ # Properly encodes addresses.
406
+ def encode_address(arg)
407
+ if arg.is_a? Integer
408
+
409
+ # address from integer
410
+ return Util.zpad_int arg
411
+ elsif arg.size == 20
412
+
413
+ # address from encoded address
414
+ return Util.zpad arg, 32
415
+ elsif arg.size == 40
416
+
417
+ # address from hexa-decimal address with 0x prefix
418
+ return Util.zpad_hex arg
419
+ elsif arg.size == 42 and arg[0, 2] == "0x"
420
+
421
+ # address from hexa-decimal address
422
+ return Util.zpad_hex arg[2..-1]
423
+ else
424
+ raise EncodingError, "Could not parse address: #{arg}"
425
+ end
426
+ end
427
+
428
+ # The ABI encoder needs to be able to determine between a hex `"123"`
429
+ # and a binary `"123"` string.
430
+ def handle_hex_string(arg, type)
431
+ if Util.is_prefixed? arg or
432
+ (arg.size === type.sub_type.to_i * 2 and Util.is_hex? arg)
433
+
434
+ # There is no way telling whether a string is hex or binary with certainty
435
+ # in Ruby. Therefore, we assume a `0x` prefix to indicate a hex string.
436
+ # Additionally, if the string size is exactly the double of the expected
437
+ # binary size, we can assume a hex value.
438
+ return Util.hex_to_bin arg
439
+ else
440
+
441
+ # Everything else will be assumed binary or raw string.
442
+ return arg.b
443
+ end
444
+ end
445
+ end
446
+ end
@@ -0,0 +1,106 @@
1
+ # Copyright (c) 2016-2022 The Ruby-Eth Contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Provides the {Eth} module.
16
+ module Eth
17
+
18
+ # The {Eth::Address} class to handle checksummed Ethereum addresses.
19
+ class Address
20
+
21
+ # Provides a special checksum error if EIP-55 is violated.
22
+ class CheckSumError < StandardError; end
23
+
24
+ # The prefixed and checksummed Ethereum address.
25
+ attr_reader :address
26
+
27
+ # Constructor of the {Eth::Address} class. Creates a new hex
28
+ # prefixed address.
29
+ #
30
+ # @param address [String] hex string representing an ethereum address.
31
+ def initialize(address)
32
+ unless Util.is_hex? address
33
+ raise CheckSumError, "Unknown address type #{address}!"
34
+ end
35
+ @address = Util.prefix_hex address
36
+ unless self.valid?
37
+ raise CheckSumError, "Invalid address provided #{address}"
38
+ end
39
+ end
40
+
41
+ # Checks that the address is valid.
42
+ #
43
+ # @return [Boolean] true if valid address.
44
+ def valid?
45
+ if !matches_any_format?
46
+ false
47
+ elsif not_checksummed?
48
+ true
49
+ else
50
+ checksum_matches?
51
+ end
52
+ end
53
+
54
+ # Generate a checksummed address.
55
+ #
56
+ # @return [String] prefixed hexstring representing an checksummed address.
57
+ def checksummed
58
+ raise CheckSumError, "Invalid address: #{address}" unless matches_any_format?
59
+
60
+ cased = unprefixed.chars.zip(checksum.chars).map do |char, check|
61
+ check.match(/[0-7]/) ? char.downcase : char.upcase
62
+ end
63
+
64
+ Util.prefix_hex cased.join
65
+ end
66
+
67
+ alias :to_s :checksummed
68
+
69
+ private
70
+
71
+ # Checks whether the address checksum matches.
72
+ def checksum_matches?
73
+ address == checksummed
74
+ end
75
+
76
+ # Checks whether the address is not checksummed.
77
+ def not_checksummed?
78
+ all_uppercase? || all_lowercase?
79
+ end
80
+
81
+ # Checks whether the address is all upper-case.
82
+ def all_uppercase?
83
+ address.match /(?:0[xX])[A-F0-9]{40}/
84
+ end
85
+
86
+ # Checks whether the address is all lower-case.
87
+ def all_lowercase?
88
+ address.match /(?:0[xX])[a-f0-9]{40}/
89
+ end
90
+
91
+ # Checks whether the address matches any known format.
92
+ def matches_any_format?
93
+ address.match /\A(?:0[xX])[a-fA-F0-9]{40}\z/
94
+ end
95
+
96
+ # Computes the checksum of the address.
97
+ def checksum
98
+ Util.bin_to_hex Util.keccak256 unprefixed.downcase
99
+ end
100
+
101
+ # Removes the hex prefix.
102
+ def unprefixed
103
+ Util.remove_hex_prefix address
104
+ end
105
+ end
106
+ end