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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codeql.yml +1 -1
  3. data/.github/workflows/docs.yml +2 -2
  4. data/.github/workflows/spec.yml +1 -3
  5. data/CHANGELOG.md +33 -0
  6. data/CODE_OF_CONDUCT.md +3 -5
  7. data/Gemfile +3 -3
  8. data/LICENSE.txt +1 -1
  9. data/README.md +6 -6
  10. data/SECURITY.md +2 -2
  11. data/eth.gemspec +4 -1
  12. data/lib/eth/abi/decoder.rb +18 -7
  13. data/lib/eth/abi/encoder.rb +14 -26
  14. data/lib/eth/abi/event.rb +5 -1
  15. data/lib/eth/abi/function.rb +124 -0
  16. data/lib/eth/abi/packed/encoder.rb +196 -0
  17. data/lib/eth/abi/type.rb +77 -16
  18. data/lib/eth/abi.rb +29 -2
  19. data/lib/eth/address.rb +3 -1
  20. data/lib/eth/api.rb +1 -1
  21. data/lib/eth/chain.rb +9 -1
  22. data/lib/eth/client/http.rb +7 -3
  23. data/lib/eth/client/ipc.rb +1 -1
  24. data/lib/eth/client.rb +38 -37
  25. data/lib/eth/constant.rb +1 -1
  26. data/lib/eth/contract/error.rb +62 -0
  27. data/lib/eth/contract/event.rb +69 -16
  28. data/lib/eth/contract/function.rb +22 -1
  29. data/lib/eth/contract/function_input.rb +1 -1
  30. data/lib/eth/contract/function_output.rb +12 -4
  31. data/lib/eth/contract/initializer.rb +1 -1
  32. data/lib/eth/contract.rb +56 -5
  33. data/lib/eth/eip712.rb +49 -13
  34. data/lib/eth/ens/coin_type.rb +1 -1
  35. data/lib/eth/ens/resolver.rb +1 -1
  36. data/lib/eth/ens.rb +1 -1
  37. data/lib/eth/key/decrypter.rb +1 -1
  38. data/lib/eth/key/encrypter.rb +1 -1
  39. data/lib/eth/key.rb +2 -2
  40. data/lib/eth/rlp/decoder.rb +1 -1
  41. data/lib/eth/rlp/encoder.rb +1 -1
  42. data/lib/eth/rlp/sedes/big_endian_int.rb +1 -1
  43. data/lib/eth/rlp/sedes/binary.rb +1 -1
  44. data/lib/eth/rlp/sedes/list.rb +1 -1
  45. data/lib/eth/rlp/sedes.rb +1 -1
  46. data/lib/eth/rlp.rb +1 -1
  47. data/lib/eth/signature.rb +1 -1
  48. data/lib/eth/solidity.rb +1 -1
  49. data/lib/eth/tx/eip1559.rb +33 -8
  50. data/lib/eth/tx/eip2930.rb +32 -7
  51. data/lib/eth/tx/eip4844.rb +389 -0
  52. data/lib/eth/tx/eip7702.rb +520 -0
  53. data/lib/eth/tx/legacy.rb +31 -7
  54. data/lib/eth/tx.rb +88 -1
  55. data/lib/eth/unit.rb +1 -1
  56. data/lib/eth/util.rb +20 -8
  57. data/lib/eth/version.rb +2 -2
  58. data/lib/eth.rb +1 -1
  59. metadata +26 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc5ddb568fad899fb1b19cce7995c8152b799475fbca603932d840d561fa4b4c
4
- data.tar.gz: fc69a4e2c2866a719336cc94a0adb9244383a6c80116ab7da8a1165262743ac9
3
+ metadata.gz: 58842e5f3990be268758249080b56b70c1f26f8299ef27e7549db6cc2f675e51
4
+ data.tar.gz: 388a599dde8f50f34d46287e028e4dfe0c855815baa10de4a8c258b549429286
5
5
  SHA512:
6
- metadata.gz: 35062c2f2b04723b2889195a5a16c662e2f2913fed4ecc3d5903a6167ed125d23a3b066125756023b2b71451f5899a58f5a36e61049f0e744b6a24583a6eff9e
7
- data.tar.gz: 51732510c1e64a48d3095fb6a6502038e493ace3390da31cde1006f67f4b7a4e130f78de2ab667ae235ce4285c2bb986a3720be8b6648e91d845de5bdb6f1a8f
6
+ metadata.gz: 3d4e42a99aebc7a47673b0706ce8476e08beca249ecae812f10c76e4f8767088b6a185c232abf9cc977a6680b08ac7b38de9d211cfc741f3bb2aa5fce9a4730c
7
+ data.tar.gz: cb29ed0c90b56eeae5ce519a436fada62e47690c1ca447e230d0fa2f3915fe97cd3346506c6b67e9c93a88d333197f0735529dd3c66c764d1895e76b1aa82b57
@@ -35,7 +35,7 @@ jobs:
35
35
  uses: github/codeql-action/analyze@v3
36
36
  - uses: ruby/setup-ruby@v1
37
37
  with:
38
- ruby-version: '3.3'
38
+ ruby-version: '3.4'
39
39
  bundler-cache: true
40
40
  - name: "Run rufo code formatting checks"
41
41
  run: |
@@ -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.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.1
23
+ uses: JamesIves/github-pages-deploy-action@v4.7.3
24
24
  with:
25
25
  branch: gh-pages
26
26
  folder: doc/
@@ -19,7 +19,7 @@ jobs:
19
19
  fail-fast: false
20
20
  matrix:
21
21
  os: [ubuntu-latest, macos-latest]
22
- ruby: ['3.2', '3.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][homepage],
119
- version 2.1, available at
120
- [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
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.4"
6
+ gem "bundler", "~> 2.5"
7
7
  gem "pry", "~> 0.15"
8
8
  gem "rake", "~> 13.2"
9
- gem "rdoc", "~> 6.9"
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", "~> 2.1"
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-2023 The Ruby-Eth Contributors
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
  [![GitHub release (latest by date)](https://img.shields.io/github/v/release/q9f/eth.rb)](https://github.com/q9f/eth.rb/releases)
11
11
  [![Gem](https://img.shields.io/gem/v/eth)](https://rubygems.org/gems/eth)
12
12
  [![Gem](https://img.shields.io/gem/dt/eth)](https://rubygems.org/gems/eth)
13
- [![Visitors](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fq9f%2Feth.rb&count_bg=%2379C83D&title_bg=%23555555&icon=rubygems.svg&icon_color=%23FF0000&title=visitors&edge_flat=false)](https://hits.seeyoufarm.com)
14
13
  [![codecov](https://codecov.io/gh/q9f/eth.rb/branch/main/graph/badge.svg?token=IK7USBPBZY)](https://codecov.io/gh/q9f/eth.rb)
15
- [![Maintainability](https://api.codeclimate.com/v1/badges/469e6f66425198ad7614/maintainability)](https://codeclimate.com/github/q9f/eth.rb/maintainability)
16
14
  [![Top Language](https://img.shields.io/github/languages/top/q9f/eth.rb?color=red)](https://github.com/q9f/eth.rb/pulse)
17
15
  [![Yard Doc API](https://img.shields.io/badge/documentation-API-blue)](https://q9f.github.io/eth.rb)
18
16
  [![Usage Wiki](https://img.shields.io/badge/usage-WIKI-blue)](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% specification coverage for all code inside this gem.
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 assis you to identify uncovered lines of code and get coverage up to 100%.
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.x | :white_check_mark: |
14
- | `eth` | < 0.5 | :x: |
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", ">= 2.2", "< 4.0"
53
+ spec.add_dependency "openssl", "~> 3.3"
51
54
 
52
55
  # scrypt for encrypted key derivation
53
56
  spec.add_dependency "scrypt", "~> 3.0"
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2023 The Ruby-Eth Contributors
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
- # ref https://github.com/ethereum/tests/issues/691
77
- raise NotImplementedError, "Decoding dynamic arrays with nested dynamic sub-types is not implemented for ABI." if nested_sub.dynamic?
78
-
79
- # decoded dynamic-sized arrays
80
- (0...l).map { |i| type(nested_sub, arg[32 + nested_sub.size * i, nested_sub.size]) }
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
- "0x#{Util.bin_to_hex data[12..-1]}"
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]
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2023 The Ruby-Eth Contributors
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, tail = "", ""
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
- 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]
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 [EncodingError] if encoding fails for type.
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-2023 The Ruby-Eth Contributors
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