eth 0.5.13 → 0.5.15
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/codeql.yml +1 -1
- data/.github/workflows/docs.yml +2 -2
- data/.github/workflows/spec.yml +1 -3
- data/CHANGELOG.md +33 -0
- data/CODE_OF_CONDUCT.md +3 -5
- data/Gemfile +3 -3
- data/LICENSE.txt +1 -1
- data/README.md +6 -6
- data/SECURITY.md +2 -2
- data/eth.gemspec +4 -1
- data/lib/eth/abi/decoder.rb +18 -7
- data/lib/eth/abi/encoder.rb +14 -26
- data/lib/eth/abi/event.rb +5 -1
- data/lib/eth/abi/function.rb +124 -0
- data/lib/eth/abi/packed/encoder.rb +196 -0
- data/lib/eth/abi/type.rb +77 -16
- data/lib/eth/abi.rb +29 -2
- data/lib/eth/address.rb +3 -1
- data/lib/eth/api.rb +1 -1
- data/lib/eth/chain.rb +9 -1
- data/lib/eth/client/http.rb +7 -3
- data/lib/eth/client/ipc.rb +1 -1
- data/lib/eth/client.rb +38 -37
- data/lib/eth/constant.rb +1 -1
- data/lib/eth/contract/error.rb +62 -0
- data/lib/eth/contract/event.rb +69 -16
- data/lib/eth/contract/function.rb +22 -1
- data/lib/eth/contract/function_input.rb +1 -1
- data/lib/eth/contract/function_output.rb +12 -4
- data/lib/eth/contract/initializer.rb +1 -1
- data/lib/eth/contract.rb +56 -5
- data/lib/eth/eip712.rb +49 -13
- data/lib/eth/ens/coin_type.rb +1 -1
- data/lib/eth/ens/resolver.rb +1 -1
- data/lib/eth/ens.rb +1 -1
- data/lib/eth/key/decrypter.rb +1 -1
- data/lib/eth/key/encrypter.rb +1 -1
- data/lib/eth/key.rb +2 -2
- data/lib/eth/rlp/decoder.rb +1 -1
- data/lib/eth/rlp/encoder.rb +1 -1
- data/lib/eth/rlp/sedes/big_endian_int.rb +1 -1
- data/lib/eth/rlp/sedes/binary.rb +1 -1
- data/lib/eth/rlp/sedes/list.rb +1 -1
- data/lib/eth/rlp/sedes.rb +1 -1
- data/lib/eth/rlp.rb +1 -1
- data/lib/eth/signature.rb +1 -1
- data/lib/eth/solidity.rb +1 -1
- data/lib/eth/tx/eip1559.rb +33 -8
- data/lib/eth/tx/eip2930.rb +32 -7
- data/lib/eth/tx/eip4844.rb +389 -0
- data/lib/eth/tx/eip7702.rb +520 -0
- data/lib/eth/tx/legacy.rb +31 -7
- data/lib/eth/tx.rb +88 -1
- data/lib/eth/unit.rb +1 -1
- data/lib/eth/util.rb +20 -8
- data/lib/eth/version.rb +2 -2
- data/lib/eth.rb +1 -1
- metadata +26 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 58842e5f3990be268758249080b56b70c1f26f8299ef27e7549db6cc2f675e51
|
4
|
+
data.tar.gz: 388a599dde8f50f34d46287e028e4dfe0c855815baa10de4a8c258b549429286
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d4e42a99aebc7a47673b0706ce8476e08beca249ecae812f10c76e4f8767088b6a185c232abf9cc977a6680b08ac7b38de9d211cfc741f3bb2aa5fce9a4730c
|
7
|
+
data.tar.gz: cb29ed0c90b56eeae5ce519a436fada62e47690c1ca447e230d0fa2f3915fe97cd3346506c6b67e9c93a88d333197f0735529dd3c66c764d1895e76b1aa82b57
|
data/.github/workflows/docs.yml
CHANGED
@@ -13,14 +13,14 @@ jobs:
|
|
13
13
|
- uses: actions/checkout@v4
|
14
14
|
- uses: ruby/setup-ruby@v1
|
15
15
|
with:
|
16
|
-
ruby-version: '3.
|
16
|
+
ruby-version: '3.4'
|
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.7.
|
23
|
+
uses: JamesIves/github-pages-deploy-action@v4.7.3
|
24
24
|
with:
|
25
25
|
branch: gh-pages
|
26
26
|
folder: doc/
|
data/.github/workflows/spec.yml
CHANGED
@@ -19,7 +19,7 @@ jobs:
|
|
19
19
|
fail-fast: false
|
20
20
|
matrix:
|
21
21
|
os: [ubuntu-latest, macos-latest]
|
22
|
-
ruby: ['3.
|
22
|
+
ruby: ['3.3', '3.4']
|
23
23
|
steps:
|
24
24
|
- uses: actions/checkout@v4
|
25
25
|
- uses: ruby/setup-ruby@v1
|
@@ -48,8 +48,6 @@ jobs:
|
|
48
48
|
- name: Run Tests
|
49
49
|
run: |
|
50
50
|
bundle exec rspec
|
51
|
-
env:
|
52
|
-
INFURA_TOKEN: ${{ secrets.INFURA_TOKEN }}
|
53
51
|
- name: Upload coverage to Codecov
|
54
52
|
uses: codecov/codecov-action@v5
|
55
53
|
with:
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,39 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
+
## [0.5.15]
|
5
|
+
### Added
|
6
|
+
* Eth/tx: add support for EIP-4844 transactions [#345](https://github.com/q9f/eth.rb/pull/345)
|
7
|
+
* Eth/contract: support solidity custom errors as per ERC-6093 [#344](https://github.com/q9f/eth.rb/pull/344)
|
8
|
+
* Eth/abi: decode transaction input [#354](https://github.com/q9f/eth.rb/pull/354)
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
* Eth/abi: handle tuple type without components [#335](https://github.com/q9f/eth.rb/issues/335)
|
12
|
+
|
13
|
+
## [0.5.14]
|
14
|
+
### Added
|
15
|
+
* Add ability to decode event parameters using ABI reference. [#328](https://github.com/q9f/eth.rb/pull/328)
|
16
|
+
* Add support for EIP-7702 transactions [#320](https://github.com/q9f/eth.rb/pull/320)
|
17
|
+
* Eth/abi: implement packed encoder [#310](https://github.com/q9f/eth.rb/pull/310)
|
18
|
+
|
19
|
+
### Changed
|
20
|
+
* Chore: run rufo, add docs [#332](https://github.com/q9f/eth.rb/pull/332)
|
21
|
+
* Spec/client: fix nonce too low error handling in spec [#331](https://github.com/q9f/eth.rb/pull/331)
|
22
|
+
* Move the tests that are failing due to a geth upgrade to pending [#330](https://github.com/q9f/eth.rb/pull/330)
|
23
|
+
* Spec/client: don't require any rpc api token for tests [#326](https://github.com/q9f/eth.rb/pull/326)
|
24
|
+
* Build(deps): bump JamesIves/github-pages-deploy-action from 4.7.2 to 4.7.3 [#327](https://github.com/q9f/eth.rb/pull/327)
|
25
|
+
* Eth/eip712: prepare tests for packed encoding [#216](https://github.com/q9f/eth.rb/pull/216)
|
26
|
+
* Spec/solidity: mute system call output [#319](https://github.com/q9f/eth.rb/pull/319)
|
27
|
+
* Updated nesting of describe blocks in the EIP-1559 spec. [#318](https://github.com/q9f/eth.rb/pull/318)
|
28
|
+
* Update README.md [#317](https://github.com/q9f/eth.rb/pull/317)
|
29
|
+
* Docs: update README.md [#314](https://github.com/q9f/eth.rb/pull/314)
|
30
|
+
* Gem: update copyright headers [#312](https://github.com/q9f/eth.rb/pull/312)
|
31
|
+
* Build(deps): bump JamesIves/github-pages-deploy-action from 4.7.1 to 4.7.2 [#309](https://github.com/q9f/eth.rb/pull/309)
|
32
|
+
* Spec: switch from infura to drpc [#308](https://github.com/q9f/eth.rb/pull/308)
|
33
|
+
* Ci: update ruby version [#307](https://github.com/q9f/eth.rb/pull/307)
|
34
|
+
* Gem: bump version to 0.5.14 [#305](https://github.com/q9f/eth.rb/pull/305)
|
35
|
+
* Docs: update changelog [#304](https://github.com/q9f/eth.rb/pull/304)
|
36
|
+
|
4
37
|
## [0.5.13]
|
5
38
|
### Changed
|
6
39
|
* Eth/api: update to latest available go-ethereum apis [#301](https://github.com/q9f/eth.rb/pull/301)
|
data/CODE_OF_CONDUCT.md
CHANGED
@@ -115,8 +115,6 @@ community.
|
|
115
115
|
|
116
116
|
## Attribution
|
117
117
|
|
118
|
-
This Code of Conduct is adapted from the [Contributor Covenant]
|
119
|
-
version 2.1, available at
|
120
|
-
|
121
|
-
Community Impact Guidelines were inspired by
|
122
|
-
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
118
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
|
119
|
+
version 2.1, available at [v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html).
|
120
|
+
Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder.
|
data/Gemfile
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
5
|
group :test, :development do
|
6
|
-
gem "bundler", "~> 2.
|
6
|
+
gem "bundler", "~> 2.5"
|
7
7
|
gem "pry", "~> 0.15"
|
8
8
|
gem "rake", "~> 13.2"
|
9
|
-
gem "rdoc", "~> 6.
|
9
|
+
gem "rdoc", "~> 6.13"
|
10
10
|
gem "rspec", "~> 3.13"
|
11
11
|
gem "rufo", "~> 0.18"
|
12
12
|
gem "simplecov", "~> 0.22"
|
13
|
-
gem "simplecov-cobertura", "~>
|
13
|
+
gem "simplecov-cobertura", "~> 3.0"
|
14
14
|
gem "yard", "~> 0.9"
|
15
15
|
end
|
16
16
|
|
data/LICENSE.txt
CHANGED
@@ -187,7 +187,7 @@
|
|
187
187
|
same "printed page" as the copyright notice for easier
|
188
188
|
identification within third-party archives.
|
189
189
|
|
190
|
-
Copyright (c) 2016-
|
190
|
+
Copyright (c) 2016-2025 The Ruby-Eth Contributors
|
191
191
|
|
192
192
|
Licensed under the Apache License, Version 2.0 (the "License");
|
193
193
|
you may not use this file except in compliance with the License.
|
data/README.md
CHANGED
@@ -10,9 +10,7 @@
|
|
10
10
|
[](https://github.com/q9f/eth.rb/releases)
|
11
11
|
[](https://rubygems.org/gems/eth)
|
12
12
|
[](https://rubygems.org/gems/eth)
|
13
|
-
[](https://hits.seeyoufarm.com)
|
14
13
|
[](https://codecov.io/gh/q9f/eth.rb)
|
15
|
-
[](https://codeclimate.com/github/q9f/eth.rb/maintainability)
|
16
14
|
[](https://github.com/q9f/eth.rb/pulse)
|
17
15
|
[](https://q9f.github.io/eth.rb)
|
18
16
|
[](https://github.com/q9f/eth.rb/wiki)
|
@@ -34,11 +32,15 @@ What you get:
|
|
34
32
|
- [x] EIP-2028 Call-data intrinsic gas cost estimates (plus access lists)
|
35
33
|
- [x] EIP-2718 Ethereum Transaction Envelopes (and types)
|
36
34
|
- [x] EIP-2930 Ethereum Type-1 Transactions (with access lists)
|
35
|
+
- [x] EIP-4844 Ethereum Type-3 Transactions (with shard blobs)
|
36
|
+
- [x] EIP-7702 Ethereum Type-4 Transactions (with authorization lists)
|
37
37
|
- [x] ABI-Encoder and Decoder (including type parser)
|
38
|
+
- [x] Packed ABI-Encoder for Solidity smart contracts
|
38
39
|
- [x] RLP-Encoder and Decoder (including sedes)
|
39
40
|
- [x] RPC-Client (IPC/HTTP) for Execution-Layer APIs
|
40
41
|
- [x] Solidity bindings (compile contracts from Ruby)
|
41
42
|
- [x] Full smart-contract support (deploy, transact, and call)
|
43
|
+
- [x] ERC-6093 custom Solidity errors
|
42
44
|
|
43
45
|
## Installation
|
44
46
|
Add this line to your application's Gemfile:
|
@@ -80,8 +82,6 @@ The test suite expects working local HTTP and IPC endpoints with a prefunded dev
|
|
80
82
|
geth --dev --http --ipcpath /tmp/geth.ipc &
|
81
83
|
```
|
82
84
|
|
83
|
-
It also expects an `$INFURA_TOKEN` in environment to test some ENS queries on mainnet.
|
84
|
-
|
85
85
|
To run tests, simply use `rspec`. Note, that the Ethereum test fixtures are also required.
|
86
86
|
|
87
87
|
```shell
|
@@ -90,12 +90,12 @@ bundle install
|
|
90
90
|
rspec
|
91
91
|
```
|
92
92
|
|
93
|
-
The goal is to have 100%
|
93
|
+
The goal is to have 100% unit-test coverage for all code inside this gem.
|
94
94
|
|
95
95
|
## Contributing
|
96
96
|
Pull requests are welcome! To contribute, please consider the following:
|
97
97
|
* Code should be fully documented. Run `yard doc` and make sure it does not yield any warnings or undocumented sets.
|
98
|
-
* Code should be fully covered by tests. Run `rspec` to make sure all tests pass. The CI has an integration that will
|
98
|
+
* Code should be fully covered by tests. Run `rspec` to make sure all tests pass. The CI has an integration that will assist you to identify uncovered lines of code and get coverage up to 100%.
|
99
99
|
* Code should be formatted properly. Try to eliminate the most common issues such as trailing white-spaces or duplicate new-lines. Usage of the `rufo` gem is recommended.
|
100
100
|
* Submit pull requests, questions, or issues to Github: <https://github.com/q9f/eth.rb>
|
101
101
|
|
data/SECURITY.md
CHANGED
@@ -10,8 +10,8 @@ later.
|
|
10
10
|
|
11
11
|
| Gem | Version | Supported |
|
12
12
|
| -------------- | ------- | ------------------ |
|
13
|
-
| `eth` | 0.5
|
14
|
-
| `eth` | <
|
13
|
+
| `eth` | >= 0.5 | :white_check_mark: |
|
14
|
+
| `eth` | < 0.5 | :x: |
|
15
15
|
| `ethereum` | _any_ | :x: |
|
16
16
|
| `ethereum-abi` | _any_ | :x: |
|
17
17
|
| `rlp` | _any_ | :x: |
|
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 = ">= 3.0", "< 4.0"
|
36
36
|
|
37
|
+
# bigdecimal for big decimals ;)
|
38
|
+
spec.add_dependency "bigdecimal", "~> 3.1"
|
39
|
+
|
37
40
|
# forwardable for contracts meta programming
|
38
41
|
spec.add_dependency "forwardable", "~> 1.3"
|
39
42
|
|
@@ -47,7 +50,7 @@ Gem::Specification.new do |spec|
|
|
47
50
|
spec.add_dependency "rbsecp256k1", "~> 6.0"
|
48
51
|
|
49
52
|
# openssl for encrypted key derivation
|
50
|
-
spec.add_dependency "openssl", "
|
53
|
+
spec.add_dependency "openssl", "~> 3.3"
|
51
54
|
|
52
55
|
# scrypt for encrypted key derivation
|
53
56
|
spec.add_dependency "scrypt", "~> 3.0"
|
data/lib/eth/abi/decoder.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2025 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.
|
@@ -43,11 +43,14 @@ module Eth
|
|
43
43
|
# Case: decoding array of string/bytes
|
44
44
|
else
|
45
45
|
l = Util.deserialize_big_endian_to_int arg[0, 32]
|
46
|
+
raise DecodingError, "Wrong data size for dynamic array" unless arg.size >= 32 + 32 * l
|
46
47
|
|
47
48
|
# Decode each element of the array
|
48
49
|
(1..l).map do |i|
|
49
50
|
pointer = Util.deserialize_big_endian_to_int arg[i * 32, 32] # Pointer to the size of the array's element
|
51
|
+
raise DecodingError, "Offset out of bounds" if pointer < 32 * l || pointer > arg.size - 64
|
50
52
|
data_l = Util.deserialize_big_endian_to_int arg[32 + pointer, 32] # length of the element
|
53
|
+
raise DecodingError, "Offset out of bounds" if pointer + 32 + Util.ceil32(data_l) > arg.size
|
51
54
|
type(Type.parse(type.base_type), arg[pointer + 32, Util.ceil32(data_l) + 32])
|
52
55
|
end
|
53
56
|
end
|
@@ -73,11 +76,19 @@ module Eth
|
|
73
76
|
l = Util.deserialize_big_endian_to_int arg[0, 32]
|
74
77
|
nested_sub = type.nested_sub
|
75
78
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
79
|
+
if nested_sub.dynamic?
|
80
|
+
raise DecodingError, "Wrong data size for dynamic array" unless arg.size >= 32 + 32 * l
|
81
|
+
offsets = (0...l).map do |i|
|
82
|
+
off = Util.deserialize_big_endian_to_int arg[32 + 32 * i, 32]
|
83
|
+
raise DecodingError, "Offset out of bounds" if off < 32 * l || off > arg.size - 64
|
84
|
+
off
|
85
|
+
end
|
86
|
+
offsets.map { |off| type(nested_sub, arg[32 + off..]) }
|
87
|
+
else
|
88
|
+
raise DecodingError, "Wrong data size for dynamic array" unless arg.size >= 32 + nested_sub.size * l
|
89
|
+
# decoded dynamic-sized arrays with static sub-types
|
90
|
+
(0...l).map { |i| type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) }
|
91
|
+
end
|
81
92
|
elsif !type.dimensions.empty?
|
82
93
|
l = type.dimensions.first
|
83
94
|
nested_sub = type.nested_sub
|
@@ -102,7 +113,7 @@ module Eth
|
|
102
113
|
when "address"
|
103
114
|
|
104
115
|
# decoded address with 0x-prefix
|
105
|
-
|
116
|
+
Address.new(Util.bin_to_hex data[12..-1]).to_s.downcase
|
106
117
|
when "string", "bytes"
|
107
118
|
if type.sub_type.empty?
|
108
119
|
size = Util.deserialize_big_endian_to_int data[0, 32]
|
data/lib/eth/abi/encoder.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2025 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.
|
@@ -46,33 +46,21 @@ module Eth
|
|
46
46
|
elsif type.dynamic? && arg.is_a?(Array)
|
47
47
|
|
48
48
|
# encodes dynamic-sized arrays
|
49
|
-
head
|
50
|
-
head += type(Type.size_type, arg.size)
|
49
|
+
head = type(Type.size_type, arg.size)
|
51
50
|
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
51
|
|
52
|
+
if nested_sub.dynamic?
|
53
|
+
tails = arg.map { |a| type(nested_sub, a) }
|
54
|
+
offset = arg.size * 32
|
55
|
+
tails.each do |t|
|
66
56
|
head += type(Type.size_type, offset)
|
57
|
+
offset += t.size
|
67
58
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
arg.size.times do |i|
|
73
|
-
head += type nested_sub, arg[i]
|
59
|
+
head + tails.join
|
60
|
+
else
|
61
|
+
arg.each { |a| head += type(nested_sub, a) }
|
62
|
+
head
|
74
63
|
end
|
75
|
-
"#{head}#{tail}"
|
76
64
|
else
|
77
65
|
if type.dimensions.empty?
|
78
66
|
|
@@ -93,15 +81,15 @@ module Eth
|
|
93
81
|
# @return [String] the encoded primitive type.
|
94
82
|
# @raise [EncodingError] if value does not match type.
|
95
83
|
# @raise [ValueOutOfBounds] if value is out of bounds for type.
|
96
|
-
# @raise [
|
84
|
+
# @raise [ArgumentError] if encoding fails for type.
|
97
85
|
def primitive_type(type, arg)
|
98
86
|
case type.base_type
|
99
87
|
when "uint"
|
100
88
|
uint arg, type
|
101
|
-
when "bool"
|
102
|
-
bool arg
|
103
89
|
when "int"
|
104
90
|
int arg, type
|
91
|
+
when "bool"
|
92
|
+
bool arg
|
105
93
|
when "ureal", "ufixed"
|
106
94
|
ufixed arg, type
|
107
95
|
when "real", "fixed"
|
data/lib/eth/abi/event.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2025 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.
|
@@ -44,6 +44,10 @@ module Eth
|
|
44
44
|
"#{name}(#{types.join(",")})"
|
45
45
|
end
|
46
46
|
|
47
|
+
# Gets the input type for events.
|
48
|
+
#
|
49
|
+
# @param input [Hash] events input.
|
50
|
+
# @return [String] input type.
|
47
51
|
def type(input)
|
48
52
|
if input["type"] == "tuple"
|
49
53
|
"(#{input["components"].map { |c| type(c) }.join(",")})"
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# Copyright (c) 2016-2025 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 module to decode transaction input data.
|
24
|
+
module Function
|
25
|
+
extend self
|
26
|
+
|
27
|
+
# Build function signature string from ABI interface.
|
28
|
+
#
|
29
|
+
# @param interface [Hash] ABI function interface.
|
30
|
+
# @return [String] interface signature string.
|
31
|
+
def signature(interface)
|
32
|
+
name = interface.fetch("name")
|
33
|
+
inputs = interface.fetch("inputs", [])
|
34
|
+
types = inputs.map { |i| type(i) }
|
35
|
+
"#{name}(#{types.join(",")})"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Compute selector for ABI function interface.
|
39
|
+
#
|
40
|
+
# @param interface [Hash] ABI function interface.
|
41
|
+
# @return [String] a hex-string selector.
|
42
|
+
def selector(interface)
|
43
|
+
sig = signature(interface)
|
44
|
+
Util.prefix_hex(Util.bin_to_hex(Util.keccak256(sig))[0, 8])
|
45
|
+
end
|
46
|
+
|
47
|
+
# Gets the input type for functions.
|
48
|
+
#
|
49
|
+
# @param input [Hash] function input.
|
50
|
+
# @return [String] input type.
|
51
|
+
def type(input)
|
52
|
+
if input["type"] == "tuple"
|
53
|
+
"(#{input["components"].map { |c| type(c) }.join(",")})"
|
54
|
+
elsif input["type"] == "enum"
|
55
|
+
"uint8"
|
56
|
+
else
|
57
|
+
input["type"]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# A decoded function call.
|
62
|
+
class CallDescription
|
63
|
+
# The function ABI interface used to decode the call.
|
64
|
+
attr_accessor :function_interface
|
65
|
+
|
66
|
+
# The positional arguments of the call.
|
67
|
+
attr_accessor :args
|
68
|
+
|
69
|
+
# The named arguments of the call.
|
70
|
+
attr_accessor :kwargs
|
71
|
+
|
72
|
+
# The function selector.
|
73
|
+
attr_accessor :selector
|
74
|
+
|
75
|
+
# Creates a description object for a decoded function call.
|
76
|
+
#
|
77
|
+
# @param function_interface [Hash] function ABI type.
|
78
|
+
# @param selector [String] function selector hex-string.
|
79
|
+
# @param args [Array] decoded positional arguments.
|
80
|
+
# @param kwargs [Hash] decoded keyword arguments.
|
81
|
+
def initialize(function_interface, selector, args, kwargs)
|
82
|
+
@function_interface = function_interface
|
83
|
+
@selector = selector
|
84
|
+
@args = args
|
85
|
+
@kwargs = kwargs
|
86
|
+
end
|
87
|
+
|
88
|
+
# The function name. (e.g. transfer)
|
89
|
+
def name
|
90
|
+
@name ||= function_interface.fetch("name")
|
91
|
+
end
|
92
|
+
|
93
|
+
# The function signature. (e.g. transfer(address,uint256))
|
94
|
+
def signature
|
95
|
+
@signature ||= Function.signature(function_interface)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Decodes a transaction input with a set of ABI interfaces.
|
100
|
+
#
|
101
|
+
# @param interfaces [Array] function ABI types.
|
102
|
+
# @param data [String] transaction input data.
|
103
|
+
# @return [CallDescription, nil] a CallDescription object or nil if selector unknown.
|
104
|
+
def decode(interfaces, data)
|
105
|
+
data = Util.remove_hex_prefix(data)
|
106
|
+
selector = Util.prefix_hex(data[0, 8])
|
107
|
+
payload = Util.prefix_hex(data[8..] || "")
|
108
|
+
|
109
|
+
selector_to_interfaces = Hash[interfaces.map { |i| [selector(i), i] }]
|
110
|
+
if (interface = selector_to_interfaces[selector])
|
111
|
+
inputs = interface.fetch("inputs", [])
|
112
|
+
types = inputs.map { |i| type(i) }
|
113
|
+
args = Abi.decode(types, payload)
|
114
|
+
kwargs = {}
|
115
|
+
inputs.each_with_index do |input, i|
|
116
|
+
name = input.fetch("name", "")
|
117
|
+
kwargs[name.to_sym] = args[i] unless name.empty?
|
118
|
+
end
|
119
|
+
CallDescription.new(interface, selector, args, kwargs)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|