eth 0.5.7 → 0.5.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/codeql.yml +1 -1
- data/.github/workflows/docs.yml +2 -2
- data/.github/workflows/spec.yml +3 -3
- data/CHANGELOG.md +33 -0
- data/Gemfile +2 -2
- data/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/abis/ens.json +422 -0
- data/eth.gemspec +2 -2
- data/lib/eth/abi/event.rb +1 -1
- data/lib/eth/abi/type.rb +46 -12
- data/lib/eth/abi.rb +72 -14
- data/lib/eth/address.rb +2 -2
- data/lib/eth/api.rb +1 -1
- data/lib/eth/chain.rb +19 -7
- data/lib/eth/client/http.rb +1 -1
- data/lib/eth/client/http_auth.rb +1 -1
- data/lib/eth/client/ipc.rb +1 -1
- data/lib/eth/client.rb +70 -58
- data/lib/eth/constant.rb +1 -1
- data/lib/eth/contract/event.rb +1 -1
- data/lib/eth/contract/function.rb +2 -2
- data/lib/eth/contract/function_input.rb +7 -2
- data/lib/eth/contract/function_output.rb +1 -1
- data/lib/eth/contract/initializer.rb +1 -1
- data/lib/eth/contract.rb +1 -1
- data/lib/eth/eip712.rb +2 -2
- data/lib/eth/ens/resolver.rb +77 -0
- data/lib/eth/key/decrypter.rb +1 -1
- data/lib/eth/key/encrypter.rb +1 -1
- data/lib/eth/key.rb +5 -5
- data/lib/eth/rlp/decoder.rb +2 -2
- data/lib/eth/rlp/encoder.rb +3 -3
- data/lib/eth/rlp/sedes/big_endian_int.rb +1 -1
- data/lib/eth/rlp/sedes/binary.rb +2 -2
- data/lib/eth/rlp/sedes/list.rb +5 -5
- data/lib/eth/rlp/sedes.rb +4 -4
- data/lib/eth/rlp.rb +1 -1
- data/lib/eth/signature.rb +4 -4
- data/lib/eth/solidity.rb +5 -3
- data/lib/eth/tx/eip1559.rb +3 -3
- data/lib/eth/tx/eip2930.rb +3 -3
- data/lib/eth/tx/legacy.rb +3 -3
- data/lib/eth/tx.rb +5 -5
- data/lib/eth/unit.rb +1 -1
- data/lib/eth/util.rb +14 -14
- data/lib/eth/version.rb +2 -2
- data/lib/eth.rb +2 -1
- metadata +7 -4
data/lib/eth/abi/type.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -35,18 +35,28 @@ module Eth
|
|
35
35
|
# The dimension attribute, e.g., `[10]` for an array of size 10.
|
36
36
|
attr :dimensions
|
37
37
|
|
38
|
+
# The components of a tuple type.
|
39
|
+
attr :components
|
40
|
+
|
41
|
+
# The name of tuple component.
|
42
|
+
attr :name
|
43
|
+
|
38
44
|
# Create a new Type object for base types, sub types, and dimensions.
|
39
45
|
# Should not be used; use {Type.parse} instead.
|
40
46
|
#
|
41
47
|
# @param base_type [String] the base-type attribute.
|
42
48
|
# @param sub_type [String] the sub-type attribute.
|
43
49
|
# @param dimensions [Array] the dimension attribute.
|
50
|
+
# @param components [Array] the components attribute.
|
51
|
+
# @param component_name [String] the tuple component's name.
|
44
52
|
# @return [Eth::Abi::Type] an ABI type object.
|
45
|
-
def initialize(base_type, sub_type, dimensions)
|
53
|
+
def initialize(base_type, sub_type, dimensions, components = nil, component_name = nil)
|
46
54
|
sub_type = sub_type.to_s
|
47
55
|
@base_type = base_type
|
48
56
|
@sub_type = sub_type
|
49
57
|
@dimensions = dimensions
|
58
|
+
@components = components
|
59
|
+
@name = component_name
|
50
60
|
end
|
51
61
|
|
52
62
|
# Converts the self.parse method into a constructor.
|
@@ -56,9 +66,12 @@ module Eth
|
|
56
66
|
# Creates a new Type upon success (using konstructor).
|
57
67
|
#
|
58
68
|
# @param type [String] a common Solidity type.
|
69
|
+
# @param components [Array] the components attribute.
|
70
|
+
# @param component_name [String] the tuple component's name.
|
59
71
|
# @return [Eth::Abi::Type] a parsed Type object.
|
60
72
|
# @raise [ParseError] if it fails to parse the type.
|
61
|
-
def parse(type)
|
73
|
+
def parse(type, components = nil, component_name = nil)
|
74
|
+
return type if type.is_a?(Type)
|
62
75
|
_, base_type, sub_type, dimension = /([a-z]*)([0-9]*x?[0-9]*)((\[[0-9]*\])*)/.match(type).to_a
|
63
76
|
|
64
77
|
# type dimension can only be numeric
|
@@ -73,6 +86,8 @@ module Eth
|
|
73
86
|
@base_type = base_type
|
74
87
|
@sub_type = sub_type
|
75
88
|
@dimensions = dims.map { |x| x[1...-1].to_i }
|
89
|
+
@components = components.map { |component| Eth::Abi::Type.parse(component["type"], component.dig("components"), component.dig("name")) } unless components.nil?
|
90
|
+
@name = component_name
|
76
91
|
end
|
77
92
|
|
78
93
|
# Creates a new uint256 type used for size.
|
@@ -98,15 +113,13 @@ module Eth
|
|
98
113
|
def size
|
99
114
|
s = nil
|
100
115
|
if dimensions.empty?
|
101
|
-
|
116
|
+
if !(["string", "bytes", "tuple"].include?(base_type) and sub_type.empty?)
|
102
117
|
s = 32
|
118
|
+
elsif base_type == "tuple" && components.none?(&:dynamic?)
|
119
|
+
s = components.sum(&:size)
|
103
120
|
end
|
104
|
-
|
105
|
-
|
106
|
-
unless nested_sub.is_dynamic?
|
107
|
-
s = dimensions.last * nested_sub.size
|
108
|
-
end
|
109
|
-
end
|
121
|
+
elsif dimensions.last != 0 && !nested_sub.dynamic?
|
122
|
+
s = dimensions.last * nested_sub.size
|
110
123
|
end
|
111
124
|
@size ||= s
|
112
125
|
end
|
@@ -114,7 +127,7 @@ module Eth
|
|
114
127
|
# Helpes to determine whether array is of dynamic size.
|
115
128
|
#
|
116
129
|
# @return [Boolean] true if array is of dynamic size.
|
117
|
-
def
|
130
|
+
def dynamic?
|
118
131
|
size.nil?
|
119
132
|
end
|
120
133
|
|
@@ -122,7 +135,24 @@ module Eth
|
|
122
135
|
#
|
123
136
|
# @return [Eth::Abi::Type] nested sub-type.
|
124
137
|
def nested_sub
|
125
|
-
@nested_sub ||= self.class.new(base_type, sub_type, dimensions[0...-1])
|
138
|
+
@nested_sub ||= self.class.new(base_type, sub_type, dimensions[0...-1], components, name)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Allows exporting the type as string.
|
142
|
+
#
|
143
|
+
# @return [String] the type string.
|
144
|
+
def to_s
|
145
|
+
if base_type == "tuple"
|
146
|
+
"(" + components.map(&:to_s).join(",") + ")" + (dimensions.size > 0 ? dimensions.map { |x| "[#{x == 0 ? "" : x}]" }.join : "")
|
147
|
+
elsif dimensions.empty?
|
148
|
+
if %w[string bytes].include?(base_type) && sub_type.empty?
|
149
|
+
base_type
|
150
|
+
else
|
151
|
+
"#{base_type}#{sub_type}"
|
152
|
+
end
|
153
|
+
else
|
154
|
+
"#{base_type}#{sub_type}#{dimensions.map { |x| "[#{x == 0 ? "" : x}]" }.join}"
|
155
|
+
end
|
126
156
|
end
|
127
157
|
|
128
158
|
private
|
@@ -138,6 +168,10 @@ module Eth
|
|
138
168
|
|
139
169
|
# bytes can be no longer than 32 bytes
|
140
170
|
raise ParseError, "Maximum 32 bytes for fixed-length string or bytes" unless sub_type.empty? || sub_type.to_i <= 32
|
171
|
+
when "tuple"
|
172
|
+
|
173
|
+
# tuples can not have any suffix
|
174
|
+
raise ParseError, "Tuple type must have no suffix or numerical suffix" unless sub_type.empty?
|
141
175
|
when "uint", "int"
|
142
176
|
|
143
177
|
# integers must have a numerical suffix
|
data/lib/eth/abi.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -45,7 +45,7 @@ module Eth
|
|
45
45
|
def encode(types, args)
|
46
46
|
|
47
47
|
# parse all types
|
48
|
-
parsed_types = types.map { |t| Type.parse(t) }
|
48
|
+
parsed_types = types.map { |t| Type === t ? t : Type.parse(t) }
|
49
49
|
|
50
50
|
# prepare the "head"
|
51
51
|
head_size = (0...args.size)
|
@@ -55,7 +55,7 @@ module Eth
|
|
55
55
|
|
56
56
|
# encode types and arguments
|
57
57
|
args.each_with_index do |arg, i|
|
58
|
-
if parsed_types[i].
|
58
|
+
if parsed_types[i].dynamic?
|
59
59
|
head += encode_type Type.size_type, head_size + tail.size
|
60
60
|
tail += encode_type parsed_types[i], arg
|
61
61
|
else
|
@@ -81,12 +81,16 @@ module Eth
|
|
81
81
|
size = encode_type Type.size_type, arg.size
|
82
82
|
padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
|
83
83
|
return "#{size}#{arg}#{padding}"
|
84
|
-
elsif type.
|
85
|
-
|
84
|
+
elsif type.base_type == "tuple" && type.dimensions.size == 1 && type.dimensions[0] != 0
|
85
|
+
result = ""
|
86
|
+
result += encode_struct_offsets(type.nested_sub, arg)
|
87
|
+
result += arg.map { |x| encode_type(type.nested_sub, x) }.join
|
88
|
+
result
|
89
|
+
elsif type.dynamic? && arg.is_a?(Array)
|
86
90
|
|
87
91
|
# encodes dynamic-sized arrays
|
88
92
|
head, tail = "", ""
|
89
|
-
head += encode_type
|
93
|
+
head += encode_type(Type.size_type, arg.size)
|
90
94
|
nested_sub = type.nested_sub
|
91
95
|
nested_sub_size = type.nested_sub.size
|
92
96
|
|
@@ -102,8 +106,10 @@ module Eth
|
|
102
106
|
offset += total_bytes_length + 32
|
103
107
|
end
|
104
108
|
|
105
|
-
head += encode_type
|
109
|
+
head += encode_type(Type.size_type, offset)
|
106
110
|
end
|
111
|
+
elsif nested_sub.base_type == "tuple" && nested_sub.dynamic?
|
112
|
+
head += encode_struct_offsets(nested_sub, arg)
|
107
113
|
end
|
108
114
|
|
109
115
|
arg.size.times do |i|
|
@@ -145,6 +151,8 @@ module Eth
|
|
145
151
|
return encode_fixed arg, type
|
146
152
|
when "string", "bytes"
|
147
153
|
return encode_bytes arg, type
|
154
|
+
when "tuple"
|
155
|
+
return encode_tuple arg, type
|
148
156
|
when "hash"
|
149
157
|
return encode_hash arg, type
|
150
158
|
when "address"
|
@@ -163,7 +171,7 @@ module Eth
|
|
163
171
|
def decode(types, data)
|
164
172
|
|
165
173
|
# accept hex abi but decode it first
|
166
|
-
data = Util.hex_to_bin data if Util.
|
174
|
+
data = Util.hex_to_bin data if Util.hex? data
|
167
175
|
|
168
176
|
# parse all types
|
169
177
|
parsed_types = types.map { |t| Type.parse(t) }
|
@@ -173,7 +181,7 @@ module Eth
|
|
173
181
|
start_positions = [nil] * types.size + [data.size]
|
174
182
|
pos = 0
|
175
183
|
parsed_types.each_with_index do |t, i|
|
176
|
-
if t.
|
184
|
+
if t.dynamic?
|
177
185
|
|
178
186
|
# record start position for dynamic type
|
179
187
|
start_positions[i] = Util.deserialize_big_endian_to_int(data[pos, 32])
|
@@ -201,7 +209,7 @@ module Eth
|
|
201
209
|
|
202
210
|
# add dynamic types
|
203
211
|
parsed_types.each_with_index do |t, i|
|
204
|
-
if t.
|
212
|
+
if t.dynamic?
|
205
213
|
offset, next_offset = start_positions[i, 2]
|
206
214
|
outputs[i] = data[offset...next_offset]
|
207
215
|
end
|
@@ -225,12 +233,12 @@ module Eth
|
|
225
233
|
|
226
234
|
# decoded strings and bytes
|
227
235
|
return data[0, l]
|
228
|
-
elsif type.
|
236
|
+
elsif type.dynamic?
|
229
237
|
l = Util.deserialize_big_endian_to_int arg[0, 32]
|
230
238
|
nested_sub = type.nested_sub
|
231
239
|
|
232
240
|
# 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.
|
241
|
+
raise NotImplementedError, "Decoding dynamic arrays with nested dynamic sub-types is not implemented for ABI." if nested_sub.dynamic?
|
234
242
|
|
235
243
|
# decoded dynamic-sized arrays
|
236
244
|
return (0...l).map { |i| decode_type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) }
|
@@ -381,6 +389,56 @@ module Eth
|
|
381
389
|
end
|
382
390
|
end
|
383
391
|
|
392
|
+
# Properly encodes tuples.
|
393
|
+
def encode_tuple(arg, type)
|
394
|
+
raise EncodingError, "Expecting Hash: #{arg}" unless arg.instance_of? Hash
|
395
|
+
raise EncodingError, "Expecting #{type.components.size} elements: #{arg}" unless arg.size == type.components.size
|
396
|
+
|
397
|
+
static_size = 0
|
398
|
+
type.components.each_with_index do |component, i|
|
399
|
+
if type.components[i].dynamic?
|
400
|
+
static_size += 32
|
401
|
+
else
|
402
|
+
static_size += Util.ceil32(type.components[i].size || 0)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
dynamic_offset = static_size
|
407
|
+
offsets_and_static_values = []
|
408
|
+
dynamic_values = []
|
409
|
+
|
410
|
+
type.components.each_with_index do |component, i|
|
411
|
+
component_type = type.components[i]
|
412
|
+
if component_type.dynamic?
|
413
|
+
offsets_and_static_values << encode_type(Type.size_type, dynamic_offset)
|
414
|
+
dynamic_value = encode_type(component_type, arg.is_a?(Array) ? arg[i] : arg[component_type.name])
|
415
|
+
dynamic_values << dynamic_value
|
416
|
+
dynamic_offset += dynamic_value.size
|
417
|
+
else
|
418
|
+
offsets_and_static_values << encode_type(component_type, arg.is_a?(Array) ? arg[i] : arg[component_type.name])
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
offsets_and_static_values.join + dynamic_values.join
|
423
|
+
end
|
424
|
+
|
425
|
+
# Properly encode struct offsets.
|
426
|
+
def encode_struct_offsets(type, arg)
|
427
|
+
result = ""
|
428
|
+
offset = arg.size
|
429
|
+
tails_encoding = arg.map { |a| encode_type(type, a) }
|
430
|
+
arg.size.times do |i|
|
431
|
+
if i == 0
|
432
|
+
offset *= 32
|
433
|
+
else
|
434
|
+
offset += tails_encoding[i - 1].size
|
435
|
+
end
|
436
|
+
offset_string = encode_type(Type.size_type, offset)
|
437
|
+
result += offset_string
|
438
|
+
end
|
439
|
+
result
|
440
|
+
end
|
441
|
+
|
384
442
|
# Properly encodes hash-strings.
|
385
443
|
def encode_hash(arg, type)
|
386
444
|
size = type.sub_type.to_i
|
@@ -428,8 +486,8 @@ module Eth
|
|
428
486
|
# The ABI encoder needs to be able to determine between a hex `"123"`
|
429
487
|
# and a binary `"123"` string.
|
430
488
|
def handle_hex_string(arg, type)
|
431
|
-
if Util.
|
432
|
-
(arg.size === type.sub_type.to_i * 2 and Util.
|
489
|
+
if Util.prefixed? arg or
|
490
|
+
(arg.size === type.sub_type.to_i * 2 and Util.hex? arg)
|
433
491
|
|
434
492
|
# There is no way telling whether a string is hex or binary with certainty
|
435
493
|
# in Ruby. Therefore, we assume a `0x` prefix to indicate a hex string.
|
data/lib/eth/address.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -29,7 +29,7 @@ module Eth
|
|
29
29
|
#
|
30
30
|
# @param address [String] hex string representing an ethereum address.
|
31
31
|
def initialize(address)
|
32
|
-
unless Util.
|
32
|
+
unless Util.hex? address
|
33
33
|
raise CheckSumError, "Unknown address type #{address}!"
|
34
34
|
end
|
35
35
|
@address = Util.prefix_hex address
|
data/lib/eth/api.rb
CHANGED
data/lib/eth/chain.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -38,12 +38,18 @@ module Eth
|
|
38
38
|
# Chain ID for POA Network mainnet.
|
39
39
|
POA_NET = 99.freeze
|
40
40
|
|
41
|
-
# Chain ID for Gnosis
|
41
|
+
# Chain ID for xDAI mainnet (now Gnosis Chain).
|
42
42
|
XDAI = 100.freeze
|
43
43
|
|
44
|
-
# Chain ID for
|
44
|
+
# Chain ID for Gnosis mainnet (formerly xDAI).
|
45
|
+
GNOSIS = XDAI.freeze
|
46
|
+
|
47
|
+
# Chain ID for the Matic mainnet (now Polygon).
|
45
48
|
MATIC = 137.freeze
|
46
49
|
|
50
|
+
# Chain ID for the Polygon mainnet (formerly Matic).
|
51
|
+
POLYGON = MATIC.freeze
|
52
|
+
|
47
53
|
# Chain ID for Arbitrum mainnet.
|
48
54
|
ARBITRUM = 42161.freeze
|
49
55
|
|
@@ -86,9 +92,15 @@ module Eth
|
|
86
92
|
# Chain ID for Arbitrum Rinkeby testnet.
|
87
93
|
RINKEBY_ARBITRUM = 421611.freeze
|
88
94
|
|
95
|
+
# Chain ID for Arbitrum Goerli testnet.
|
96
|
+
GOERLI_ARBITRUM = 421613.freeze
|
97
|
+
|
89
98
|
# Chain ID for Sepolia testnet.
|
90
99
|
SEPOLIA = 11155111.freeze
|
91
100
|
|
101
|
+
# Chain ID for Holesovice testnet.
|
102
|
+
HOLESOVICE = 11166111.freeze
|
103
|
+
|
92
104
|
# Chain ID for the geth private network preset.
|
93
105
|
PRIVATE_GETH = 1337.freeze
|
94
106
|
|
@@ -97,7 +109,7 @@ module Eth
|
|
97
109
|
#
|
98
110
|
# @param v [Integer] the signature's `v` value.
|
99
111
|
# @return [Boolean] true if ledger's legacy value.
|
100
|
-
def
|
112
|
+
def ledger?(v)
|
101
113
|
[0, 1].include? v
|
102
114
|
end
|
103
115
|
|
@@ -106,7 +118,7 @@ module Eth
|
|
106
118
|
#
|
107
119
|
# @param v [Integer] the signature's `v` value.
|
108
120
|
# @return [Boolean] true if legacy value.
|
109
|
-
def
|
121
|
+
def legacy?(v)
|
110
122
|
[27, 28].include? v
|
111
123
|
end
|
112
124
|
|
@@ -120,11 +132,11 @@ module Eth
|
|
120
132
|
def to_recovery_id(v, chain_id = ETHEREUM)
|
121
133
|
e = 0 + 2 * chain_id + 35
|
122
134
|
i = 1 + 2 * chain_id + 35
|
123
|
-
if
|
135
|
+
if ledger? v
|
124
136
|
|
125
137
|
# some wallets are using a `v` of 0 or 1 (ledger)
|
126
138
|
return v
|
127
|
-
elsif
|
139
|
+
elsif legacy? v
|
128
140
|
|
129
141
|
# this is the pre-EIP-155 legacy case
|
130
142
|
return v - 27
|
data/lib/eth/client/http.rb
CHANGED
data/lib/eth/client/http_auth.rb
CHANGED
data/lib/eth/client/ipc.rb
CHANGED