eth 0.5.10 → 0.5.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/docs.yml +1 -1
- data/.github/workflows/spec.yml +8 -3
- data/CHANGELOG.md +16 -0
- data/eth.gemspec +4 -1
- data/lib/eth/abi/decoder.rb +135 -0
- data/lib/eth/abi/encoder.rb +304 -0
- data/lib/eth/abi/event.rb +13 -2
- data/lib/eth/abi/type.rb +1 -1
- data/lib/eth/abi.rb +10 -385
- data/lib/eth/api.rb +45 -51
- data/lib/eth/chain.rb +9 -0
- data/lib/eth/client/http.rb +18 -4
- data/lib/eth/client/ipc.rb +2 -2
- data/lib/eth/client.rb +59 -125
- data/lib/eth/contract.rb +11 -1
- data/lib/eth/solidity.rb +7 -4
- data/lib/eth/tx/eip1559.rb +10 -5
- data/lib/eth/tx/eip2930.rb +10 -5
- data/lib/eth/tx/legacy.rb +0 -2
- data/lib/eth/tx.rb +9 -2
- data/lib/eth/util.rb +1 -0
- data/lib/eth/version.rb +11 -2
- metadata +20 -5
- data/lib/eth/client/http_auth.rb +0 -73
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ddb9f142d1985c57f8abecee08a3275ed0e2dfd6a008cf608bf8c3ac239335d
|
4
|
+
data.tar.gz: d33f16301d9421c4ae584cc293fe26c18e88e2783d8184a91999833753f6f904
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c32065a3eb99ca9dc142c5acd7de9a2a957e850743afa45f8c2ddb9b95597c55d75063e28dd4cce757b4ac228402fa988a7cd76fdbc6b33e2fa7770038068db7
|
7
|
+
data.tar.gz: 8c44621156a464b8676ca9dd6e1bd92295e84a0c41329687b8a84f5335abc10683a3ae39ecfa548f5f5203f218adb6850aa76f227057e69da47405a55d0d81f3
|
data/.github/workflows/docs.yml
CHANGED
data/.github/workflows/spec.yml
CHANGED
@@ -19,7 +19,7 @@ jobs:
|
|
19
19
|
fail-fast: false
|
20
20
|
matrix:
|
21
21
|
os: [ubuntu-22.04, macos-12]
|
22
|
-
ruby: ['3.0', '3.1']
|
22
|
+
ruby: ['3.0', '3.1', '3.2']
|
23
23
|
steps:
|
24
24
|
- uses: actions/checkout@v3
|
25
25
|
- uses: ruby/setup-ruby@v1
|
@@ -37,10 +37,15 @@ jobs:
|
|
37
37
|
sudo apt-get update
|
38
38
|
sudo apt-get install ethereum solc libyaml-dev
|
39
39
|
if: startsWith(matrix.os, 'Ubuntu')
|
40
|
+
- name: Patch Geth # https://github.com/ethereum/go-ethereum/pull/27360
|
41
|
+
run: |
|
42
|
+
git clone https://github.com/q9f/go-ethereum.git -b q9f/params/shanghai
|
43
|
+
pushd go-ethereum/
|
44
|
+
make geth
|
45
|
+
popd
|
40
46
|
- name: Run Geth
|
41
47
|
run: |
|
42
|
-
geth --dev --http --ipcpath /tmp/geth.ipc &
|
43
|
-
disown &
|
48
|
+
./go-ethereum/build/bin/geth --dev --http --ipcpath /tmp/geth.ipc &
|
44
49
|
- name: Gem Dependencies
|
45
50
|
run: |
|
46
51
|
git submodule update --init
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
+
## [0.5.10]
|
5
|
+
### Added
|
6
|
+
* Eth/client: add transfer_erc20 function [#197](https://github.com/q9f/eth.rb/pull/197)
|
7
|
+
* Eth/client: add resolve_ens function [#192](https://github.com/q9f/eth.rb/pull/192)
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
* Eth/ens: restore docs for normalize [#198](https://github.com/q9f/eth.rb/pull/198)
|
11
|
+
* Docs: update readme [#195](https://github.com/q9f/eth.rb/pull/195)
|
12
|
+
* Eth/contract: ensure address arrays support [#194](https://github.com/q9f/eth.rb/pull/194)
|
13
|
+
* Eth/client: do not allow accessing local accounts on remote connections [#193](https://github.com/q9f/eth.rb/pull/193)
|
14
|
+
* Eth/client: correctly select functions [#191](https://github.com/q9f/eth.rb/pull/191)
|
15
|
+
* Docs: create security policy [#190](https://github.com/q9f/eth.rb/pull/190)
|
16
|
+
* Docs: add contribution guidelines [#189](https://github.com/q9f/eth.rb/pull/189)
|
17
|
+
* Docs: add coc [#188](https://github.com/q9f/eth.rb/pull/188)
|
18
|
+
* Docs: update changelog for 0.5.9 [#187](https://github.com/q9f/eth.rb/pull/187)
|
19
|
+
|
4
20
|
## [0.5.9]
|
5
21
|
### Added
|
6
22
|
* Eth/abi: dynamic struct encoding (#135) [#185](https://github.com/q9f/eth.rb/pull/185)
|
data/eth.gemspec
CHANGED
@@ -34,6 +34,9 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.platform = Gem::Platform::RUBY
|
35
35
|
spec.required_ruby_version = ">= 2.7", "< 4.0"
|
36
36
|
|
37
|
+
# forwardable for contracts meta programming
|
38
|
+
spec.add_dependency "forwardable", "~> 1.3"
|
39
|
+
|
37
40
|
# keccak for hashing everything in ethereum
|
38
41
|
spec.add_dependency "keccak", "~> 1.3"
|
39
42
|
|
@@ -41,7 +44,7 @@ Gem::Specification.new do |spec|
|
|
41
44
|
spec.add_dependency "konstructor", "~> 1.0"
|
42
45
|
|
43
46
|
# rbsecp256k1 for key-pairs and signatures
|
44
|
-
spec.add_dependency "rbsecp256k1", "~>
|
47
|
+
spec.add_dependency "rbsecp256k1", "~> 6.0"
|
45
48
|
|
46
49
|
# openssl for encrypted key derivation
|
47
50
|
spec.add_dependency "openssl", ">= 2.2", "< 4.0"
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# Copyright (c) 2016-2023 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
|
+
# Provides the {Eth} module.
|
18
|
+
module Eth
|
19
|
+
|
20
|
+
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
|
21
|
+
module Abi
|
22
|
+
|
23
|
+
# Provides a utility module to assist decoding ABIs.
|
24
|
+
module Decoder
|
25
|
+
extend self
|
26
|
+
|
27
|
+
# Decodes a specific value, either static or dynamic.
|
28
|
+
#
|
29
|
+
# @param type [Eth::Abi::Type] type to be decoded.
|
30
|
+
# @param arg [String] encoded type data string.
|
31
|
+
# @return [String] the decoded data for the type.
|
32
|
+
# @raise [DecodingError] if decoding fails for type.
|
33
|
+
def type(type, arg)
|
34
|
+
if %w(string bytes).include?(type.base_type) and type.sub_type.empty?
|
35
|
+
# Case: decoding a string/bytes
|
36
|
+
if type.dimensions.empty?
|
37
|
+
l = Util.deserialize_big_endian_to_int arg[0, 32]
|
38
|
+
data = arg[32..-1]
|
39
|
+
raise DecodingError, "Wrong data size for string/bytes object" unless data.size == Util.ceil32(l)
|
40
|
+
|
41
|
+
# decoded strings and bytes
|
42
|
+
data[0, l]
|
43
|
+
# Case: decoding array of string/bytes
|
44
|
+
else
|
45
|
+
l = Util.deserialize_big_endian_to_int arg[0, 32]
|
46
|
+
|
47
|
+
# Decode each element of the array
|
48
|
+
(1..l).map do |i|
|
49
|
+
pointer = Util.deserialize_big_endian_to_int arg[i * 32, 32] # Pointer to the size of the array's element
|
50
|
+
data_l = Util.deserialize_big_endian_to_int arg[32 + pointer, 32] # length of the element
|
51
|
+
type(Type.parse(type.base_type), arg[pointer + 32, Util.ceil32(data_l) + 32])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
elsif type.dynamic?
|
55
|
+
l = Util.deserialize_big_endian_to_int arg[0, 32]
|
56
|
+
nested_sub = type.nested_sub
|
57
|
+
|
58
|
+
# ref https://github.com/ethereum/tests/issues/691
|
59
|
+
raise NotImplementedError, "Decoding dynamic arrays with nested dynamic sub-types is not implemented for ABI." if nested_sub.dynamic?
|
60
|
+
|
61
|
+
# decoded dynamic-sized arrays
|
62
|
+
(0...l).map { |i| type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) }
|
63
|
+
elsif !type.dimensions.empty?
|
64
|
+
l = type.dimensions.first
|
65
|
+
nested_sub = type.nested_sub
|
66
|
+
|
67
|
+
# decoded static-size arrays
|
68
|
+
(0...l).map { |i| type(nested_sub, arg[nested_sub.size * i, nested_sub.size]) }
|
69
|
+
else
|
70
|
+
|
71
|
+
# decoded primitive types
|
72
|
+
primitive_type type, arg
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Decodes primitive types.
|
77
|
+
#
|
78
|
+
# @param type [Eth::Abi::Type] type to be decoded.
|
79
|
+
# @param data [String] encoded primitive type data string.
|
80
|
+
# @return [String] the decoded data for the type.
|
81
|
+
# @raise [DecodingError] if decoding fails for type.
|
82
|
+
def primitive_type(type, data)
|
83
|
+
case type.base_type
|
84
|
+
when "address"
|
85
|
+
|
86
|
+
# decoded address with 0x-prefix
|
87
|
+
"0x#{Util.bin_to_hex data[12..-1]}"
|
88
|
+
when "string", "bytes"
|
89
|
+
if type.sub_type.empty?
|
90
|
+
size = Util.deserialize_big_endian_to_int data[0, 32]
|
91
|
+
|
92
|
+
# decoded dynamic-sized array
|
93
|
+
data[32..-1][0, size]
|
94
|
+
else
|
95
|
+
|
96
|
+
# decoded static-sized array
|
97
|
+
data[0, type.sub_type.to_i]
|
98
|
+
end
|
99
|
+
when "hash"
|
100
|
+
|
101
|
+
# decoded hash
|
102
|
+
data[(32 - type.sub_type.to_i), type.sub_type.to_i]
|
103
|
+
when "uint"
|
104
|
+
|
105
|
+
# decoded unsigned integer
|
106
|
+
Util.deserialize_big_endian_to_int data
|
107
|
+
when "int"
|
108
|
+
u = Util.deserialize_big_endian_to_int data
|
109
|
+
i = u >= 2 ** (type.sub_type.to_i - 1) ? (u - 2 ** type.sub_type.to_i) : u
|
110
|
+
|
111
|
+
# decoded integer
|
112
|
+
i
|
113
|
+
when "ureal", "ufixed"
|
114
|
+
high, low = type.sub_type.split("x").map(&:to_i)
|
115
|
+
|
116
|
+
# decoded unsigned fixed point numeric
|
117
|
+
Util.deserialize_big_endian_to_int(data) * 1.0 / 2 ** low
|
118
|
+
when "real", "fixed"
|
119
|
+
high, low = type.sub_type.split("x").map(&:to_i)
|
120
|
+
u = Util.deserialize_big_endian_to_int data
|
121
|
+
i = u >= 2 ** (high + low - 1) ? (u - 2 ** (high + low)) : u
|
122
|
+
|
123
|
+
# decoded fixed point numeric
|
124
|
+
i * 1.0 / 2 ** low
|
125
|
+
when "bool"
|
126
|
+
|
127
|
+
# decoded boolean
|
128
|
+
data[-1] == Constant::BYTE_ONE
|
129
|
+
else
|
130
|
+
raise DecodingError, "Unknown primitive type: #{type.base_type}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,304 @@
|
|
1
|
+
# Copyright (c) 2016-2023 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
|
+
# Provides the {Eth} module.
|
18
|
+
module Eth
|
19
|
+
|
20
|
+
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
|
21
|
+
module Abi
|
22
|
+
|
23
|
+
# Provides a utility module to assist encoding ABIs.
|
24
|
+
module Encoder
|
25
|
+
extend self
|
26
|
+
|
27
|
+
# Encodes a specific value, either static or dynamic.
|
28
|
+
#
|
29
|
+
# @param type [Eth::Abi::Type] type to be encoded.
|
30
|
+
# @param arg [String|Number] value to be encoded.
|
31
|
+
# @return [String] the encoded type.
|
32
|
+
# @raise [EncodingError] if value does not match type.
|
33
|
+
def type(type, arg)
|
34
|
+
if %w(string bytes).include? type.base_type and type.sub_type.empty? and type.dimensions.empty?
|
35
|
+
raise EncodingError, "Argument must be a String" unless arg.instance_of? String
|
36
|
+
|
37
|
+
# encodes strings and bytes
|
38
|
+
size = type Type.size_type, arg.size
|
39
|
+
padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
|
40
|
+
"#{size}#{arg}#{padding}"
|
41
|
+
elsif type.base_type == "tuple" && type.dimensions.size == 1 && type.dimensions[0] != 0
|
42
|
+
result = ""
|
43
|
+
result += struct_offsets(type.nested_sub, arg)
|
44
|
+
result += arg.map { |x| type(type.nested_sub, x) }.join
|
45
|
+
result
|
46
|
+
elsif type.dynamic? && arg.is_a?(Array)
|
47
|
+
|
48
|
+
# encodes dynamic-sized arrays
|
49
|
+
head, tail = "", ""
|
50
|
+
head += type(Type.size_type, arg.size)
|
51
|
+
nested_sub = type.nested_sub
|
52
|
+
nested_sub_size = type.nested_sub.size
|
53
|
+
|
54
|
+
# calculate offsets
|
55
|
+
if %w(string bytes).include?(type.base_type) && type.sub_type.empty?
|
56
|
+
offset = 0
|
57
|
+
arg.size.times do |i|
|
58
|
+
if i == 0
|
59
|
+
offset = arg.size * 32
|
60
|
+
else
|
61
|
+
number_of_words = ((arg[i - 1].size + 32 - 1) / 32).floor
|
62
|
+
total_bytes_length = number_of_words * 32
|
63
|
+
offset += total_bytes_length + 32
|
64
|
+
end
|
65
|
+
|
66
|
+
head += type(Type.size_type, offset)
|
67
|
+
end
|
68
|
+
elsif nested_sub.base_type == "tuple" && nested_sub.dynamic?
|
69
|
+
head += struct_offsets(nested_sub, arg)
|
70
|
+
end
|
71
|
+
|
72
|
+
arg.size.times do |i|
|
73
|
+
head += type nested_sub, arg[i]
|
74
|
+
end
|
75
|
+
"#{head}#{tail}"
|
76
|
+
else
|
77
|
+
if type.dimensions.empty?
|
78
|
+
|
79
|
+
# encode a primitive type
|
80
|
+
primitive_type type, arg
|
81
|
+
else
|
82
|
+
|
83
|
+
# encode static-size arrays
|
84
|
+
arg.map { |x| type(type.nested_sub, x) }.join
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Encodes primitive types.
|
90
|
+
#
|
91
|
+
# @param type [Eth::Abi::Type] type to be encoded.
|
92
|
+
# @param arg [String|Number] value to be encoded.
|
93
|
+
# @return [String] the encoded primitive type.
|
94
|
+
# @raise [EncodingError] if value does not match type.
|
95
|
+
# @raise [ValueOutOfBounds] if value is out of bounds for type.
|
96
|
+
# @raise [EncodingError] if encoding fails for type.
|
97
|
+
def primitive_type(type, arg)
|
98
|
+
case type.base_type
|
99
|
+
when "uint"
|
100
|
+
uint arg, type
|
101
|
+
when "bool"
|
102
|
+
bool arg
|
103
|
+
when "int"
|
104
|
+
int arg, type
|
105
|
+
when "ureal", "ufixed"
|
106
|
+
ufixed arg, type
|
107
|
+
when "real", "fixed"
|
108
|
+
fixed arg, type
|
109
|
+
when "string", "bytes"
|
110
|
+
bytes arg, type
|
111
|
+
when "tuple"
|
112
|
+
tuple arg, type
|
113
|
+
when "hash"
|
114
|
+
hash arg, type
|
115
|
+
when "address"
|
116
|
+
address arg
|
117
|
+
else
|
118
|
+
raise EncodingError, "Unhandled type: #{type.base_type} #{type.sub_type}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
# Properly encodes unsigned integers.
|
125
|
+
def uint(arg, type)
|
126
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
127
|
+
raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::UINT_MAX or arg < Constant::UINT_MIN
|
128
|
+
real_size = type.sub_type.to_i
|
129
|
+
i = arg.to_i
|
130
|
+
raise ValueOutOfBounds, arg unless i >= 0 and i < 2 ** real_size
|
131
|
+
Util.zpad_int i
|
132
|
+
end
|
133
|
+
|
134
|
+
# Properly encodes signed integers.
|
135
|
+
def int(arg, type)
|
136
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
137
|
+
raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::INT_MAX or arg < Constant::INT_MIN
|
138
|
+
real_size = type.sub_type.to_i
|
139
|
+
i = arg.to_i
|
140
|
+
raise ValueOutOfBounds, arg unless i >= -2 ** (real_size - 1) and i < 2 ** (real_size - 1)
|
141
|
+
Util.zpad_int(i % 2 ** type.sub_type.to_i)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Properly encodes booleans.
|
145
|
+
def bool(arg)
|
146
|
+
raise EncodingError, "Argument is not bool: #{arg}" unless arg.instance_of? TrueClass or arg.instance_of? FalseClass
|
147
|
+
Util.zpad_int(arg ? 1 : 0)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Properly encodes unsigned fixed-point numbers.
|
151
|
+
def ufixed(arg, type)
|
152
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
153
|
+
high, low = type.sub_type.split("x").map(&:to_i)
|
154
|
+
raise ValueOutOfBounds, arg unless arg >= 0 and arg < 2 ** high
|
155
|
+
Util.zpad_int((arg * 2 ** low).to_i)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Properly encodes signed fixed-point numbers.
|
159
|
+
def fixed(arg, type)
|
160
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
161
|
+
high, low = type.sub_type.split("x").map(&:to_i)
|
162
|
+
raise ValueOutOfBounds, arg unless arg >= -2 ** (high - 1) and arg < 2 ** (high - 1)
|
163
|
+
i = (arg * 2 ** low).to_i
|
164
|
+
Util.zpad_int(i % 2 ** (high + low))
|
165
|
+
end
|
166
|
+
|
167
|
+
# Properly encodes byte-strings.
|
168
|
+
def bytes(arg, type)
|
169
|
+
raise EncodingError, "Expecting String: #{arg}" unless arg.instance_of? String
|
170
|
+
arg = handle_hex_string arg, type
|
171
|
+
|
172
|
+
if type.sub_type.empty?
|
173
|
+
size = Util.zpad_int arg.size
|
174
|
+
padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
|
175
|
+
|
176
|
+
# variable length string/bytes
|
177
|
+
"#{size}#{arg}#{padding}"
|
178
|
+
else
|
179
|
+
raise ValueOutOfBounds, arg unless arg.size <= type.sub_type.to_i
|
180
|
+
padding = Constant::BYTE_ZERO * (32 - arg.size)
|
181
|
+
|
182
|
+
# fixed length string/bytes
|
183
|
+
"#{arg}#{padding}"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Properly encodes tuples.
|
188
|
+
def tuple(arg, type)
|
189
|
+
raise EncodingError, "Expecting Hash: #{arg}" unless arg.instance_of? Hash
|
190
|
+
raise EncodingError, "Expecting #{type.components.size} elements: #{arg}" unless arg.size == type.components.size
|
191
|
+
|
192
|
+
static_size = 0
|
193
|
+
type.components.each_with_index do |component, i|
|
194
|
+
if type.components[i].dynamic?
|
195
|
+
static_size += 32
|
196
|
+
else
|
197
|
+
static_size += Util.ceil32(type.components[i].size || 0)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
dynamic_offset = static_size
|
202
|
+
offsets_and_static_values = []
|
203
|
+
dynamic_values = []
|
204
|
+
|
205
|
+
type.components.each_with_index do |component, i|
|
206
|
+
component_type = type.components[i]
|
207
|
+
if component_type.dynamic?
|
208
|
+
offsets_and_static_values << type(Type.size_type, dynamic_offset)
|
209
|
+
dynamic_value = type(component_type, arg.is_a?(Array) ? arg[i] : arg[component_type.name])
|
210
|
+
dynamic_values << dynamic_value
|
211
|
+
dynamic_offset += dynamic_value.size
|
212
|
+
else
|
213
|
+
offsets_and_static_values << type(component_type, arg.is_a?(Array) ? arg[i] : arg[component_type.name])
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
offsets_and_static_values.join + dynamic_values.join
|
218
|
+
end
|
219
|
+
|
220
|
+
# Properly encode struct offsets.
|
221
|
+
def struct_offsets(type, arg)
|
222
|
+
result = ""
|
223
|
+
offset = arg.size
|
224
|
+
tails_encoding = arg.map { |a| type(type, a) }
|
225
|
+
arg.size.times do |i|
|
226
|
+
if i == 0
|
227
|
+
offset *= 32
|
228
|
+
else
|
229
|
+
offset += tails_encoding[i - 1].size
|
230
|
+
end
|
231
|
+
offset_string = type(Type.size_type, offset)
|
232
|
+
result += offset_string
|
233
|
+
end
|
234
|
+
result
|
235
|
+
end
|
236
|
+
|
237
|
+
# Properly encodes hash-strings.
|
238
|
+
def hash(arg, type)
|
239
|
+
size = type.sub_type.to_i
|
240
|
+
raise EncodingError, "Argument too long: #{arg}" unless size > 0 and size <= 32
|
241
|
+
if arg.is_a? Integer
|
242
|
+
|
243
|
+
# hash from integer
|
244
|
+
Util.zpad_int arg
|
245
|
+
elsif arg.size == size
|
246
|
+
|
247
|
+
# hash from encoded hash
|
248
|
+
Util.zpad arg, 32
|
249
|
+
elsif arg.size == size * 2
|
250
|
+
|
251
|
+
# hash from hexadecimal hash
|
252
|
+
Util.zpad_hex arg
|
253
|
+
else
|
254
|
+
raise EncodingError, "Could not parse hash: #{arg}"
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Properly encodes addresses.
|
259
|
+
def address(arg)
|
260
|
+
if arg.is_a? Address
|
261
|
+
|
262
|
+
# from checksummed address with 0x prefix
|
263
|
+
Util.zpad_hex arg.to_s[2..-1]
|
264
|
+
elsif arg.is_a? Integer
|
265
|
+
|
266
|
+
# address from integer
|
267
|
+
Util.zpad_int arg
|
268
|
+
elsif arg.size == 20
|
269
|
+
|
270
|
+
# address from encoded address
|
271
|
+
Util.zpad arg, 32
|
272
|
+
elsif arg.size == 40
|
273
|
+
|
274
|
+
# address from hexadecimal address
|
275
|
+
Util.zpad_hex arg
|
276
|
+
elsif arg.size == 42 and arg[0, 2] == "0x"
|
277
|
+
|
278
|
+
# address from hexadecimal address with 0x prefix
|
279
|
+
Util.zpad_hex arg[2..-1]
|
280
|
+
else
|
281
|
+
raise EncodingError, "Could not parse address: #{arg}"
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# The ABI encoder needs to be able to determine between a hex `"123"`
|
286
|
+
# and a binary `"123"` string.
|
287
|
+
def handle_hex_string(arg, type)
|
288
|
+
if Util.prefixed? arg or
|
289
|
+
(arg.size === type.sub_type.to_i * 2 and Util.hex? arg)
|
290
|
+
|
291
|
+
# There is no way telling whether a string is hex or binary with certainty
|
292
|
+
# in Ruby. Therefore, we assume a `0x` prefix to indicate a hex string.
|
293
|
+
# Additionally, if the string size is exactly the double of the expected
|
294
|
+
# binary size, we can assume a hex value.
|
295
|
+
Util.hex_to_bin arg
|
296
|
+
else
|
297
|
+
|
298
|
+
# Everything else will be assumed binary or raw string.
|
299
|
+
arg.b
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
data/lib/eth/abi/event.rb
CHANGED
@@ -29,10 +29,21 @@ module Eth
|
|
29
29
|
# @param interface [Hash] ABI event interface.
|
30
30
|
# @return [String] a hex-string topic.
|
31
31
|
def compute_topic(interface)
|
32
|
-
sig =
|
32
|
+
sig = signature(interface)
|
33
33
|
Util.prefix_hex(Util.bin_to_hex(Util.keccak256(sig)))
|
34
34
|
end
|
35
35
|
|
36
|
+
# Build event signature string from ABI interface.
|
37
|
+
#
|
38
|
+
# @param interface [Hash] ABI event interface.
|
39
|
+
# @return [String] interface signature string.
|
40
|
+
def signature(interface)
|
41
|
+
name = interface.fetch("name")
|
42
|
+
inputs = interface.fetch("inputs", [])
|
43
|
+
types = inputs.map { |i| i.fetch("type") }
|
44
|
+
"#{name}(#{types.join(",")})"
|
45
|
+
end
|
46
|
+
|
36
47
|
# A decoded event log.
|
37
48
|
class LogDescription
|
38
49
|
# The event ABI interface used to decode the log.
|
@@ -70,7 +81,7 @@ module Eth
|
|
70
81
|
|
71
82
|
# The event signature. (e.g. Transfer(address,address,uint256))
|
72
83
|
def signature
|
73
|
-
@signature ||= Abi.signature(event_interface)
|
84
|
+
@signature ||= Abi::Event.signature(event_interface)
|
74
85
|
end
|
75
86
|
end
|
76
87
|
|
data/lib/eth/abi/type.rb
CHANGED
@@ -86,7 +86,7 @@ module Eth
|
|
86
86
|
@base_type = base_type
|
87
87
|
@sub_type = sub_type
|
88
88
|
@dimensions = dims.map { |x| x[1...-1].to_i }
|
89
|
-
@components = components.map { |component|
|
89
|
+
@components = components.map { |component| Abi::Type.parse(component["type"], component.dig("components"), component.dig("name")) } unless components.nil?
|
90
90
|
@name = component_name
|
91
91
|
end
|
92
92
|
|