eth 0.5.10 → 0.5.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fd0b420dd7b4641fe8e87f57f13fdb74b3a23a2c11e7c2e23355f1cc5f6b4a3
4
- data.tar.gz: e0c219b1c827ec4fcd4d730bfbb7d7e819a9d3049df6f01271cb79d7a81191ea
3
+ metadata.gz: a6da48b3e3128860af79efba95ded96258dfe87a8bf0a374a44bff060fc2f9c3
4
+ data.tar.gz: 9ad4cbabd4d5b31419d02b9363cdff915c4e1c61ba95197b42cf2a61ed31ac50
5
5
  SHA512:
6
- metadata.gz: cdfa9a6cc91db7fba7e6ffe02dba0c4ec5a3e93a4948c5080f461f20415a9803b566dbf8049a63951e82c85007b31202d1826bc2261fc0e6d806bc4af0caade9
7
- data.tar.gz: 97d962828b3c98387127ba69505a1170ef43ac8a40ba8aa70dc017cdc9ed91ca790b331fca7ca22eea3ba4d25158fcf3fb9e214bd195472a5b27b4bbc22ad790
6
+ metadata.gz: bbc07ab8bb0fc56f89619037ee16da5eeadb7d0fcde714572bd9b678e8c4da18b70e3fa042207c57195dd7e6e2700ebf5650bf5ca6ddf848da78661a2b8eda16
7
+ data.tar.gz: 51206666cc4a84bd05d0995ab6b7476699b17cc8bb37d3d78f789cf93116844dd3cd0f4e24360b59d7758d3588cc9fbebbe0504cdf1bd7b8a00a67e3600ac0ad
@@ -24,18 +24,18 @@ jobs:
24
24
  - ruby
25
25
  steps:
26
26
  - name: "Checkout repository"
27
- uses: actions/checkout@v3
27
+ uses: actions/checkout@v4
28
28
  - name: "Initialize CodeQL"
29
- uses: github/codeql-action/init@v2
29
+ uses: github/codeql-action/init@v3
30
30
  with:
31
31
  languages: "${{ matrix.language }}"
32
32
  - name: Autobuild
33
- uses: github/codeql-action/autobuild@v2
33
+ uses: github/codeql-action/autobuild@v3
34
34
  - name: "Perform CodeQL Analysis"
35
- uses: github/codeql-action/analyze@v2
35
+ uses: github/codeql-action/analyze@v3
36
36
  - uses: ruby/setup-ruby@v1
37
37
  with:
38
- ruby-version: '3.0'
38
+ ruby-version: '3.3'
39
39
  bundler-cache: true
40
40
  - name: "Run rufo code formatting checks"
41
41
  run: |
@@ -10,17 +10,17 @@ jobs:
10
10
  docs:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
- - uses: actions/checkout@v3
13
+ - uses: actions/checkout@v4
14
14
  - uses: ruby/setup-ruby@v1
15
15
  with:
16
- ruby-version: '3.0'
16
+ ruby-version: '3.3'
17
17
  bundler-cache: true
18
18
  - name: Run Yard Doc
19
19
  run: |
20
20
  gem install yard
21
21
  yard doc
22
22
  - name: Deploy GH Pages
23
- uses: JamesIves/github-pages-deploy-action@v4.4.1
23
+ uses: JamesIves/github-pages-deploy-action@v4.6.1
24
24
  with:
25
25
  branch: gh-pages
26
26
  folder: doc/
@@ -18,10 +18,10 @@ jobs:
18
18
  strategy:
19
19
  fail-fast: false
20
20
  matrix:
21
- os: [ubuntu-22.04, macos-12]
22
- ruby: ['3.0', '3.1']
21
+ os: [ubuntu-latest, macos-latest]
22
+ ruby: ['3.1', '3.2', '3.3']
23
23
  steps:
24
- - uses: actions/checkout@v3
24
+ - uses: actions/checkout@v4
25
25
  - uses: ruby/setup-ruby@v1
26
26
  with:
27
27
  ruby-version: ${{ matrix.ruby }}
@@ -49,5 +49,9 @@ jobs:
49
49
  run: |
50
50
  bundle exec rspec
51
51
  env:
52
- COVERAGE: true
53
52
  INFURA_TOKEN: ${{ secrets.INFURA_TOKEN }}
53
+ - name: Upload coverage to Codecov
54
+ uses: codecov/codecov-action@v4
55
+ with:
56
+ fail_ci_if_error: false
57
+ token: ${{ secrets.CODECOV_TOKEN }}
data/CHANGELOG.md CHANGED
@@ -1,6 +1,58 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [0.5.12]
5
+ ### Added
6
+ ### Changed
7
+
8
+ ## [0.5.11]
9
+ ### Added
10
+ * Eth/abi: allow encoding address types [#242](https://github.com/q9f/eth.rb/pull/242)
11
+ * Eth/solidity: enable --via-ir [#232](https://github.com/q9f/eth.rb/pull/232)
12
+ * Checking userinfo with the uri method [#233](https://github.com/q9f/eth.rb/pull/233)
13
+ * Eth/abi: add abicoder gem tests collection [#218](https://github.com/q9f/eth.rb/pull/218)
14
+ * Manual default_account [#215](https://github.com/q9f/eth.rb/pull/215)
15
+ * Add moonbeam networks in [#209](https://github.com/q9f/eth.rb/pull/209)
16
+
17
+ ### Changed
18
+ * Spec: run rufo [#245](https://github.com/q9f/eth.rb/pull/245)
19
+ * Fix the decoding of unsigned transactions [#243](https://github.com/q9f/eth.rb/pull/243)
20
+ * Build(deps): bump JamesIves/github-pages-deploy-action from 4.4.2 to 4.4.3 [#244](https://github.com/q9f/eth.rb/pull/244)
21
+ * Build(deps): bump JamesIves/github-pages-deploy-action from 4.4.1 to 4.4.2 [#240](https://github.com/q9f/eth.rb/pull/240)
22
+ * Eth/tx: update tx initcode cost for shanghai [#237](https://github.com/q9f/eth.rb/pull/237)
23
+ * Eth/client: remove default gas limit attribute [#235](https://github.com/q9f/eth.rb/pull/235)
24
+ * Docs: minor fixups [#229](https://github.com/q9f/eth.rb/pull/229)
25
+ * Eth/contract: ensure contract name is title case [#228](https://github.com/q9f/eth.rb/pull/228)
26
+ * Deps: require forwardable for contracts [#227](https://github.com/q9f/eth.rb/pull/227)
27
+ * Ens/resolver: remove pending for etc coin type [#219](https://github.com/q9f/eth.rb/pull/219)
28
+ * Deps: update secp256k1 to 6 [#214](https://github.com/q9f/eth.rb/pull/214)
29
+ * Eth/solidity: add docs for solc path override [#213](https://github.com/q9f/eth.rb/pull/213)
30
+ * Manually overwrite solc path [#212](https://github.com/q9f/eth.rb/pull/212)
31
+ * Abi.decoder handles arrays of string and bytes [#207](https://github.com/q9f/eth.rb/pull/207)
32
+ * Eth/util: fix compressed public key to address in [#206](https://github.com/q9f/eth.rb/pull/206)
33
+ * Eth/api: update execution apis to latest spec [#204](https://github.com/q9f/eth.rb/pull/204)
34
+ * Eth/abi: split abi class into encoder and decoder [#203](https://github.com/q9f/eth.rb/pull/203)
35
+ * Eth/client: deduplicate code [#202](https://github.com/q9f/eth.rb/pull/202)
36
+ * Eth/client: rewrite send to send_request [#201](https://github.com/q9f/eth.rb/pull/201)
37
+ * Docs: update changelog for 0.5.10 [#200](https://github.com/q9f/eth.rb/pull/200)
38
+ * Tested with Ruby 3.2 [#199](https://github.com/q9f/eth.rb/pull/199)
39
+
40
+ ## [0.5.10]
41
+ ### Added
42
+ * Eth/client: add transfer_erc20 function [#197](https://github.com/q9f/eth.rb/pull/197)
43
+ * Eth/client: add resolve_ens function [#192](https://github.com/q9f/eth.rb/pull/192)
44
+
45
+ ### Changed
46
+ * Eth/ens: restore docs for normalize [#198](https://github.com/q9f/eth.rb/pull/198)
47
+ * Docs: update readme [#195](https://github.com/q9f/eth.rb/pull/195)
48
+ * Eth/contract: ensure address arrays support [#194](https://github.com/q9f/eth.rb/pull/194)
49
+ * Eth/client: do not allow accessing local accounts on remote connections [#193](https://github.com/q9f/eth.rb/pull/193)
50
+ * Eth/client: correctly select functions [#191](https://github.com/q9f/eth.rb/pull/191)
51
+ * Docs: create security policy [#190](https://github.com/q9f/eth.rb/pull/190)
52
+ * Docs: add contribution guidelines [#189](https://github.com/q9f/eth.rb/pull/189)
53
+ * Docs: add coc [#188](https://github.com/q9f/eth.rb/pull/188)
54
+ * Docs: update changelog for 0.5.9 [#187](https://github.com/q9f/eth.rb/pull/187)
55
+
4
56
  ## [0.5.9]
5
57
  ### Added
6
58
  * 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", "~> 5.1"
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,154 @@
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.base_type == "tuple"
55
+ offset = 0
56
+ data = {}
57
+ type.components.each do |c|
58
+ if c.dynamic?
59
+ pointer = Util.deserialize_big_endian_to_int arg[offset, 32] # Pointer to the size of the array's element
60
+ data_len = Util.deserialize_big_endian_to_int arg[pointer, 32] # length of the element
61
+
62
+ data[c.name] = type(c, arg[pointer, Util.ceil32(data_len) + 32])
63
+ # puts data
64
+ offset += 32
65
+ else
66
+ size = c.size
67
+ data[c.name] = type(c, arg[offset, size])
68
+ offset += size
69
+ # puts data
70
+ end
71
+ end
72
+ data
73
+ elsif type.dynamic?
74
+ l = Util.deserialize_big_endian_to_int arg[0, 32]
75
+ nested_sub = type.nested_sub
76
+
77
+ # ref https://github.com/ethereum/tests/issues/691
78
+ raise NotImplementedError, "Decoding dynamic arrays with nested dynamic sub-types is not implemented for ABI." if nested_sub.dynamic?
79
+
80
+ # decoded dynamic-sized arrays
81
+ (0...l).map { |i| type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) }
82
+ elsif !type.dimensions.empty?
83
+ l = type.dimensions.first
84
+ nested_sub = type.nested_sub
85
+
86
+ # decoded static-size arrays
87
+ (0...l).map { |i| type(nested_sub, arg[nested_sub.size * i, nested_sub.size]) }
88
+ else
89
+
90
+ # decoded primitive types
91
+ primitive_type type, arg
92
+ end
93
+ end
94
+
95
+ # Decodes primitive types.
96
+ #
97
+ # @param type [Eth::Abi::Type] type to be decoded.
98
+ # @param data [String] encoded primitive type data string.
99
+ # @return [String] the decoded data for the type.
100
+ # @raise [DecodingError] if decoding fails for type.
101
+ def primitive_type(type, data)
102
+ case type.base_type
103
+ when "address"
104
+
105
+ # decoded address with 0x-prefix
106
+ "0x#{Util.bin_to_hex data[12..-1]}"
107
+ when "string", "bytes"
108
+ if type.sub_type.empty?
109
+ size = Util.deserialize_big_endian_to_int data[0, 32]
110
+
111
+ # decoded dynamic-sized array
112
+ data[32..-1][0, size]
113
+ else
114
+
115
+ # decoded static-sized array
116
+ data[0, type.sub_type.to_i]
117
+ end
118
+ when "hash"
119
+
120
+ # decoded hash
121
+ data[(32 - type.sub_type.to_i), type.sub_type.to_i]
122
+ when "uint"
123
+
124
+ # decoded unsigned integer
125
+ Util.deserialize_big_endian_to_int data
126
+ when "int"
127
+ u = Util.deserialize_big_endian_to_int data
128
+ i = u >= 2 ** (type.sub_type.to_i - 1) ? (u - 2 ** 256) : u
129
+
130
+ # decoded integer
131
+ i
132
+ when "ureal", "ufixed"
133
+ high, low = type.sub_type.split("x").map(&:to_i)
134
+
135
+ # decoded unsigned fixed point numeric
136
+ Util.deserialize_big_endian_to_int(data) * 1.0 / 2 ** low
137
+ when "real", "fixed"
138
+ high, low = type.sub_type.split("x").map(&:to_i)
139
+ u = Util.deserialize_big_endian_to_int data
140
+ i = u >= 2 ** (high + low - 1) ? (u - 2 ** (high + low)) : u
141
+
142
+ # decoded fixed point numeric
143
+ i * 1.0 / 2 ** low
144
+ when "bool"
145
+
146
+ # decoded boolean
147
+ data[-1] == Constant::BYTE_ONE
148
+ else
149
+ raise DecodingError, "Unknown primitive type: #{type.base_type}"
150
+ end
151
+ end
152
+ end
153
+ end
154
+ 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 ** 256)
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,31 @@ 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 = Abi.signature(interface)
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| type(i) }
44
+ "#{name}(#{types.join(",")})"
45
+ end
46
+
47
+ def type(input)
48
+ if input["type"] == "tuple"
49
+ "(#{input["components"].map { |c| type(c) }.join(",")})"
50
+ elsif input["type"] == "enum"
51
+ "uint8"
52
+ else
53
+ input["type"]
54
+ end
55
+ end
56
+
36
57
  # A decoded event log.
37
58
  class LogDescription
38
59
  # The event ABI interface used to decode the log.
@@ -70,7 +91,7 @@ module Eth
70
91
 
71
92
  # The event signature. (e.g. Transfer(address,address,uint256))
72
93
  def signature
73
- @signature ||= Abi.signature(event_interface)
94
+ @signature ||= Abi::Event.signature(event_interface)
74
95
  end
75
96
  end
76
97
 
@@ -105,9 +126,20 @@ module Eth
105
126
  def decode_log(inputs, data, topics, anonymous = false)
106
127
  topic_inputs, data_inputs = inputs.partition { |i| i["indexed"] }
107
128
 
108
- topic_types = topic_inputs.map { |i| i["type"] }
109
- data_types = data_inputs.map { |i| i["type"] }
110
-
129
+ topic_types = topic_inputs.map do |i|
130
+ if i["type"] == "tuple"
131
+ Type.parse(i["type"], i["components"], i["name"])
132
+ else
133
+ i["type"]
134
+ end
135
+ end
136
+ data_types = data_inputs.map do |i|
137
+ if i["type"] == "tuple"
138
+ Type.parse(i["type"], i["components"], i["name"])
139
+ else
140
+ i["type"]
141
+ end
142
+ end
111
143
  # If event is anonymous, all topics are arguments. Otherwise, the first
112
144
  # topic will be the event signature.
113
145
  if anonymous == false