eth 0.5.10 → 0.5.11
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/.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
|
|