eth 0.5.2 → 0.5.5

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: 16b9900b6e0bd1a0bb51d055b545980685416a1e079590196fa9637e1c3e758e
4
- data.tar.gz: db3a4a72a0b14458dee3f4af836ac32bfc6ccf987b22e9f965551ddacecb09de
3
+ metadata.gz: 62da01e731dd3a73a356760ac971250f193142090876e62567d73cbb5664213d
4
+ data.tar.gz: 8a951b1b585acd7b58dc327f122dbdd4787145fefbe09d71f1aa4d127787e99e
5
5
  SHA512:
6
- metadata.gz: 90a00773ef20cdf765c19da284b09f20577fbaf26707d7b425f68109be9d559805603eabfd699c7ff68a56ac8d7619ecdaa841233a0d6af9bbd14521238a18e0
7
- data.tar.gz: 80127ab21325d386fbef1e57b13a1a5e769882d174114255b938babfd35c9e84ae3515ae28ace04300dfa38eb2059cb4eb529699378e9c354c5ed73d2ae0c6ce
6
+ metadata.gz: fd1ca2d9f89a545f20cf23a9cff62a83bfe20514b69cb14aac88f08cd101a8bb9e26ba700539259c16d64ee2a3cbae46adf7bedd18b66835785ad701fd515681
7
+ data.tar.gz: a1f51f0dac47d8ce7a4ccd3df0d3decfdb0847df36a4b16587b5dd9ae217aee7375e952207d00611bcb487647ff152d958d4709ebf8b429ec0235220f45d35bd
@@ -0,0 +1,18 @@
1
+ ---
2
+ updates:
3
+ -
4
+ directory: /
5
+ labels:
6
+ - dependencies
7
+ package-ecosystem: bundler
8
+ schedule:
9
+ interval: weekly
10
+ versioning-strategy: increase
11
+ -
12
+ directory: /
13
+ labels:
14
+ - operations
15
+ package-ecosystem: github-actions
16
+ schedule:
17
+ interval: monthly
18
+ version: 2
@@ -24,15 +24,15 @@ jobs:
24
24
  - ruby
25
25
  steps:
26
26
  - name: "Checkout repository"
27
- uses: actions/checkout@v2
27
+ uses: actions/checkout@v3
28
28
  - name: "Initialize CodeQL"
29
- uses: github/codeql-action/init@v1
29
+ uses: github/codeql-action/init@v2
30
30
  with:
31
31
  languages: "${{ matrix.language }}"
32
32
  - name: Autobuild
33
- uses: github/codeql-action/autobuild@v1
33
+ uses: github/codeql-action/autobuild@v2
34
34
  - name: "Perform CodeQL Analysis"
35
- uses: github/codeql-action/analyze@v1
35
+ uses: github/codeql-action/analyze@v2
36
36
  - uses: ruby/setup-ruby@v1
37
37
  with:
38
38
  ruby-version: '2.7'
@@ -10,7 +10,7 @@ jobs:
10
10
  docs:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
- - uses: actions/checkout@v2
13
+ - uses: actions/checkout@v3
14
14
  - uses: ruby/setup-ruby@v1
15
15
  with:
16
16
  ruby-version: '2.7'
@@ -20,7 +20,7 @@ jobs:
20
20
  gem install yard
21
21
  yard doc
22
22
  - name: Deploy GH Pages
23
- uses: JamesIves/github-pages-deploy-action@4.1.7
23
+ uses: JamesIves/github-pages-deploy-action@v4.3.3
24
24
  with:
25
25
  branch: gh-pages
26
26
  folder: doc/
@@ -19,9 +19,9 @@ jobs:
19
19
  fail-fast: false
20
20
  matrix:
21
21
  os: [ubuntu-latest, macos-latest]
22
- ruby: ['2.7', '3.0']
22
+ ruby: ['2.7', '3.1']
23
23
  steps:
24
- - uses: actions/checkout@v2
24
+ - uses: actions/checkout@v3
25
25
  - uses: ruby/setup-ruby@v1
26
26
  with:
27
27
  ruby-version: ${{ matrix.ruby }}
data/AUTHORS.txt CHANGED
@@ -2,6 +2,8 @@ The Ruby-Eth Contributors are:
2
2
  * Steve Ellis @se3000
3
3
  * Afri Schoedon @q9f
4
4
  * John Omar @chainoperator
5
+ * Joshua Peek @josh
6
+ * Yuta Kurotaki @kurotaky
5
7
 
6
8
  See also:
7
9
  * https://github.com/q9f/eth.rb/graphs/contributors
data/CHANGELOG.md CHANGED
@@ -1,20 +1,60 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [0.5.4]
5
+ ### Added
6
+ - Eth/client: method for eip-1271 ([#80](https://github.com/q9f/eth.rb/pull/80))
7
+
8
+ ### Changed
9
+ - Docs: update changelog ([#77](https://github.com/q9f/eth.rb/pull/77))
10
+ - Gem: bump version to 0.5.4 ([#78](https://github.com/q9f/eth.rb/pull/78))
11
+ - Ci: bump ruby version to 3.1 on ci ([#79](https://github.com/q9f/eth.rb/pull/79))
12
+ - Fix typos ([#81](https://github.com/q9f/eth.rb/pull/81))
13
+ - Eth/contract: allow creating from file, abi, bin ([#83](https://github.com/q9f/eth.rb/pull/83))
14
+ - Eth/client: fix account requirement for client.call() ([#85](https://github.com/q9f/eth.rb/pull/85))
15
+ - Add dependency support for openssl 2.2 and greater, including 3.x ([#88](https://github.com/q9f/eth.rb/pull/88))
16
+
17
+ ## [0.5.3]
18
+ ### Added
19
+ - Smart contract support ([#68](https://github.com/q9f/eth.rb/pull/68))
20
+
21
+ ### Changed
22
+ - Eth/abi: decode event log ([#69](https://github.com/q9f/eth.rb/pull/69))
23
+ - Gem: bump version ([#70](https://github.com/q9f/eth.rb/pull/70))
24
+ - Eth/abi/event: batch log decoder ([#71](https://github.com/q9f/eth.rb/pull/71))
25
+
26
+ ## [0.5.2]
27
+ ### Added
28
+ - Eth/solidity: add solidity compiler bindings ([#66](https://github.com/q9f/eth.rb/pull/66))
29
+
30
+ ### Changed
31
+ - Eth: remove duplicated code ([#62](https://github.com/q9f/eth.rb/pull/62))
32
+ - Ci: allow coverage to drop to 99% without failing ([#63](https://github.com/q9f/eth.rb/pull/63))
33
+ - Docs: update readme ([#64](https://github.com/q9f/eth.rb/pull/64))
34
+ - Docs: add wiki to readme ([#65](https://github.com/q9f/eth.rb/pull/65))
35
+
4
36
  ## [0.5.1]
37
+ ### Added
38
+ - Add eth::rlp module ([#52](https://github.com/q9f/eth.rb/pull/52))
39
+ - Eth/client: implement http/ipc ([#37](https://github.com/q9f/eth.rb/pull/37))
40
+
5
41
  ### Changed
6
42
  - Docs: update changelog ([#61](https://github.com/q9f/eth.rb/pull/61))
7
43
  - Eth/chain: add sepolia chain id; docs ([#60](https://github.com/q9f/eth.rb/pull/60))
8
44
  - Eth/rlp: cleanup ([#59](https://github.com/q9f/eth.rb/pull/59))
9
- - Add eth::rlp module ([#52](https://github.com/q9f/eth.rb/pull/52))
10
45
  - Eth/tx: properly serialize signatures ([#58](https://github.com/q9f/eth.rb/pull/58))
11
46
  - Eth/client: fix legacy transfer ([#57](https://github.com/q9f/eth.rb/pull/57))
12
47
  - Gem: relax openssl requirement ([#56](https://github.com/q9f/eth.rb/pull/56))
13
- - Eth/client: implement http/ipc ([#37](https://github.com/q9f/eth.rb/pull/37))
14
48
  - Docs: update changelog ([#53](https://github.com/q9f/eth.rb/pull/53))
15
49
  - Spec: add upstream test fixtures for keystore ([#50](https://github.com/q9f/eth.rb/pull/50))
16
50
 
17
51
  ## [0.5.0]
52
+ ### Added
53
+ - Eth/tx: create legacy, type-1, and type-2 transactions [#33](https://github.com/q9f/eth.rb/pull/33)
54
+ - Signature: implement eip 712 typed structured data signing [#27](https://github.com/q9f/eth.rb/pull/27)
55
+ - Lib: import ABI to eth/abi [#29](https://github.com/q9f/eth.rb/pull/29)
56
+ - Eth/chains: implement eip 155 for replay protection [#20](https://github.com/q9f/eth.rb/pull/20)
57
+
18
58
  ### Changed
19
59
  - Docs: update readme with features [#49](https://github.com/q9f/eth.rb/pull/49)
20
60
  - Eth/tx: add method to estimate intrinsic gas costs [#48](https://github.com/q9f/eth.rb/pull/48)
@@ -26,16 +66,12 @@ All notable changes to this project will be documented in this file.
26
66
  - Lib: improve error handling [#39](https://github.com/q9f/eth.rb/pull/39)
27
67
  - Docs: update readme for tx and keys [#40](https://github.com/q9f/eth.rb/pull/40)
28
68
  - Implement encrypt/decrypt [#22](https://github.com/q9f/eth.rb/pull/22)
29
- - Eth/tx: create legacy, type-1, and type-2 transactions [#33](https://github.com/q9f/eth.rb/pull/33)
30
69
  - Gem: clean up some docs and scripts [#32](https://github.com/q9f/eth.rb/pull/32)
31
- - Signature: implement eip 712 typed structured data signing [#27](https://github.com/q9f/eth.rb/pull/27)
32
- - Lib: import ABI to eth/abi [#29](https://github.com/q9f/eth.rb/pull/29)
33
70
  - Rename util and chain to singular [#26](https://github.com/q9f/eth.rb/pull/26)
34
71
  - Docs: add some examples to readme [#25](https://github.com/q9f/eth.rb/pull/25)
35
72
  - Key/signature: personal sign and verify [#24](https://github.com/q9f/eth.rb/pull/24)
36
73
  - Ci: only run coverage on CI [#23](https://github.com/q9f/eth.rb/pull/23)
37
74
  - Lib/signature: implement personal_recover (eip 191 [#21](https://github.com/q9f/eth.rb/pull/21)
38
- - Eth/chains: implement eip 155 for replay protection [#20](https://github.com/q9f/eth.rb/pull/20)
39
75
  - Eth/util: public_key_to_address should return an eth::address [#19](https://github.com/q9f/eth.rb/pull/19)
40
76
  - Ci: add docs workflow [#18](https://github.com/q9f/eth.rb/pull/18)
41
77
  - Address class implementation and tests [#13](https://github.com/q9f/eth.rb/pull/13)
data/README.md CHANGED
@@ -21,6 +21,7 @@ What you get:
21
21
  - [x] EIP-155 Replay protection with Chain IDs (with presets)
22
22
  - [x] EIP-191 Ethereum Signed Messages (with prefix and type)
23
23
  - [x] EIP-712 Ethereum Signed Type Data
24
+ - [x] EIP-1271 Smart-Contract Authentification
24
25
  - [x] EIP-1559 Ethereum Type-2 Transactions (with priority fee and max gas fee)
25
26
  - [x] EIP-2028 Call-data intrinsic gas cost estimates (plus access lists)
26
27
  - [x] EIP-2718 Ethereum Transaction Envelopes (and types)
@@ -29,11 +30,7 @@ What you get:
29
30
  - [x] RLP-Encoder and Decoder (including sedes)
30
31
  - [x] RPC-Client (IPC/HTTP) for Execution-Layer APIs
31
32
  - [x] Solidity bindings (compile contracts from Ruby)
32
-
33
- Soon (TM):
34
- - [ ] Smart Contract Support
35
- - [ ] EIP-1271 Smart-Contract Authentification
36
- - [ ] HD-Wallets (BIP-32) and Mnemonics (BIP-39)
33
+ - [x] Full smart-contract support (deploy, transact, and call)
37
34
 
38
35
  Contents:
39
36
  - [1. Installation](#1-installation)
@@ -45,7 +42,8 @@ Contents:
45
42
  - [2.5. Ethereum ABI Encoder and Decoder](#25-ethereum-abi-encoder-and-decoder)
46
43
  - [2.6. Ethereum RLP Encoder and Decoder](#26-ethereum-rlp-encoder-and-decoder)
47
44
  - [2.7. Ethereum RPC-Client](#27-ethereum-rpc-client)
48
- - [2.8 Solidity Compiler Bindings](#28-solidity-compiler-bindings)
45
+ - [2.8. Solidity Compiler Bindings](#28-solidity-compiler-bindings)
46
+ - [2.9. Interact with Smart Contract](#29-interact-with-smart-contract)
49
47
  - [3. Documentation](#3-documentation)
50
48
  - [4. Testing](#4-testing)
51
49
  - [5. Contributing](#5-contributing)
@@ -231,7 +229,7 @@ cli.get_nonce cli.eth_coinbase["result"]
231
229
 
232
230
  Check out `Eth::Api` for a list of supported RPC-APIs or consult the [Documentation](https://q9f.github.io/eth.rb/) for more details.
233
231
 
234
- ### 2.8 Solidity Compiler Bindings
232
+ ### 2.8. Solidity Compiler Bindings
235
233
  Link a system-level Solidity compiler (`solc`) to your Ruby library and compile contracts.
236
234
 
237
235
  ```ruby
@@ -253,6 +251,49 @@ contract = solc.compile "spec/fixtures/contracts/greeter.sol"
253
251
 
254
252
  The `contract["Greeter"]["bin"]` could be directly used to deploy the contract as `Eth::Tx` payload. Check out the [Documentation](https://q9f.github.io/eth.rb/) for more details.
255
253
 
254
+ ### 2.9. Interact with Smart Contract
255
+
256
+ Create, compile, and deploy smart contracts.
257
+
258
+ ```ruby
259
+ contract = Eth::Contract.from_file(file: 'spec/fixtures/contracts/dummy.sol')
260
+ # => #<Eth::Contract::Dummy:0x00007fbeee936598>
261
+ cli = Eth::Client.create "/tmp/geth.ipc"
262
+ # => #<Eth::Client::Ipc:0x00007fbeee946128 @gas_limit=21000, @id=0, @max_fee_per_gas=0.2e11, @max_priority_fee_per_gas=0, @path="/tmp/geth.ipc">
263
+ address = cli.deploy_and_wait(contract)
264
+ # => "0x2f2faa160420cee087ded96bad52475147136bd8"
265
+ ```
266
+
267
+ Transact with or call the deployed contract.
268
+
269
+ ```ruby
270
+ cli.transact_and_wait(contract, "set", 1234)
271
+ # => "0x49ca4c0a5729da19a1d2574de9a444a9cd3219bdad81745b54f9cf3bb83b6a06"
272
+ cli.call(contract, "get")
273
+ # => 1234
274
+ ```
275
+
276
+ Or call an existing contract, e.g., the ENS registry:
277
+
278
+ ```ruby
279
+ ens_registry_abi = '[{"inputs":[{"internalType":"contract ENS","name":"_old","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"label","type":"bytes32"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"address","name":"resolver","type":"address"}],"name":"NewResolver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"NewTTL","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"old","outputs":[{"internalType":"contract ENS","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"recordExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"setRecord","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"label","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"label","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"setSubnodeRecord","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"setTTL","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"ttl","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"}]'
280
+ ens_registry_address = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
281
+ ens_registry_name = "ENSRegistryWithFallback"
282
+ ens_registry = Eth::Contract.from_abi(name: ens_registry_name, address: ens_registry_address, abi: ens_registry_abi)
283
+ # => #<Eth::Contract::ENSRegistryWithFallback:0x000055bece570980>
284
+ ens_registry.address
285
+ # => "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
286
+ cli.call(ens_registry, "old")
287
+ # => "0x112234455c3a32fd11230c42e7bccd4a84e02010"
288
+ ```
289
+
290
+ The gem also comes with an EIP-1271 smart-contract authentification interface.
291
+
292
+ ```ruby
293
+ cli.is_valid_signature contract, hash, signature
294
+ # => true
295
+ ```
296
+
256
297
  ## 3. Documentation
257
298
  The documentation can be found at: https://q9f.github.io/eth.rb
258
299
 
data/eth.gemspec CHANGED
@@ -44,7 +44,7 @@ Gem::Specification.new do |spec|
44
44
  spec.add_dependency "rbsecp256k1", "~> 5.1"
45
45
 
46
46
  # openssl for encrypted key derivation
47
- spec.add_dependency "openssl", "~> 2.2"
47
+ spec.add_dependency "openssl", ">= 2.2", "< 4.0"
48
48
 
49
49
  # scrypt for encrypted key derivation
50
50
  spec.add_dependency "scrypt", "~> 3.0"
@@ -0,0 +1,137 @@
1
+ # Copyright (c) 2016-2022 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 log events.
24
+ module Event
25
+ extend self
26
+
27
+ # Compute topic for ABI event interface.
28
+ #
29
+ # @param interface [Hash] ABI event interface.
30
+ # @return [String] a hex-string topic.
31
+ def compute_topic(interface)
32
+ sig = Abi.signature(interface)
33
+ Util.prefix_hex(Util.bin_to_hex(Util.keccak256(sig)))
34
+ end
35
+
36
+ # A decoded event log.
37
+ class LogDescription
38
+ # The event ABI interface used to decode the log.
39
+ attr_accessor :event_interface
40
+
41
+ # The the input argument of the event.
42
+ attr_accessor :args
43
+
44
+ # The named input argument of the event.
45
+ attr_accessor :kwargs
46
+
47
+ # The topic hash.
48
+ attr_accessor :topic
49
+
50
+ # Decodes event log argument values.
51
+ #
52
+ # @param event_interface [Hash] event ABI type.
53
+ # @param log [Hash] transaction receipt log
54
+ def initialize(event_interface, log)
55
+ @event_interface = event_interface
56
+
57
+ inputs = event_interface.fetch("inputs")
58
+ data = log.fetch("data")
59
+ topics = log.fetch("topics", [])
60
+ anonymous = event_interface.fetch("anonymous", false)
61
+
62
+ @topic = topics[0] if !anonymous
63
+ @args, @kwargs = Event.decode_log(inputs, data, topics, anonymous)
64
+ end
65
+
66
+ # The event name. (e.g. Transfer)
67
+ def name
68
+ @name ||= event_interface.fetch("name")
69
+ end
70
+
71
+ # The event signature. (e.g. Transfer(address,address,uint256))
72
+ def signature
73
+ @signature ||= Abi.signature(event_interface)
74
+ end
75
+ end
76
+
77
+ # Decodes a stream of receipt logs with a set of ABI interfaces.
78
+ #
79
+ # @param interfaces [Array] event ABI types.
80
+ # @param logs [Array] transaction receipt logs
81
+ # @return [Hash] an enumerator of LogDescription objects.
82
+ def decode_logs(interfaces, logs)
83
+ Enumerator.new do |y|
84
+ topic_to_interfaces = Hash[interfaces.map { |i| [compute_topic(i), i] }]
85
+
86
+ logs.each do |log|
87
+ topic = log.fetch("topics", [])[0]
88
+ if topic && interface = topic_to_interfaces[topic]
89
+ y << [log, LogDescription.new(interface, log)]
90
+ else
91
+ y << [log, nil]
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ # Decodes event log argument values.
98
+ #
99
+ # @param inputs [Array] event ABI types.
100
+ # @param data [String] ABI event data to be decoded.
101
+ # @param topics [Array] ABI event topics to be decoded.
102
+ # @param anonymous [Boolean] If event signature is excluded from topics.
103
+ # @return [[Array, Hash]] decoded positional arguments and decoded keyword arguments.
104
+ # @raise [DecodingError] if decoding fails for type.
105
+ def decode_log(inputs, data, topics, anonymous = false)
106
+ topic_inputs, data_inputs = inputs.partition { |i| i["indexed"] }
107
+
108
+ topic_types = topic_inputs.map { |i| i["type"] }
109
+ data_types = data_inputs.map { |i| i["type"] }
110
+
111
+ # If event is anonymous, all topics are arguments. Otherwise, the first
112
+ # topic will be the event signature.
113
+ if anonymous == false
114
+ topics = topics[1..-1]
115
+ end
116
+
117
+ decoded_topics = topics.map.with_index { |t, i| Abi.decode([topic_types[i]], t)[0] }
118
+ decoded_data = Abi.decode(data_types, data)
119
+
120
+ args = []
121
+ kwargs = {}
122
+
123
+ inputs.each_with_index do |input, index|
124
+ if input["indexed"]
125
+ value = decoded_topics[topic_inputs.index(input)]
126
+ else
127
+ value = decoded_data[data_inputs.index(input)]
128
+ end
129
+ args[index] = value
130
+ kwargs[input["name"].to_sym] = value
131
+ end
132
+
133
+ return args, kwargs
134
+ end
135
+ end
136
+ end
137
+ end
data/lib/eth/abi/type.rb CHANGED
@@ -17,7 +17,7 @@
17
17
  # Provides the {Eth} module.
18
18
  module Eth
19
19
 
20
- # Provides a Ruby implementation of the Ethereum Applicatoin Binary Interface (ABI).
20
+ # Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
21
21
  module Abi
22
22
 
23
23
  # Provides a class to handle and parse common ABI types.
data/lib/eth/abi.rb CHANGED
@@ -16,12 +16,13 @@
16
16
 
17
17
  require "konstructor"
18
18
 
19
+ require "eth/abi/event"
19
20
  require "eth/abi/type"
20
21
 
21
22
  # Provides the {Eth} module.
22
23
  module Eth
23
24
 
24
- # Provides a Ruby implementation of the Ethereum Applicatoin Binary Interface (ABI).
25
+ # Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
25
26
  # ref: https://docs.soliditylang.org/en/develop/abi-spec.html
26
27
  module Abi
27
28
  extend self
@@ -43,7 +44,7 @@ module Eth
43
44
  # @return [String] the encoded ABI data.
44
45
  def encode(types, args)
45
46
 
46
- # prase all types
47
+ # parse all types
47
48
  parsed_types = types.map { |t| Type.parse(t) }
48
49
 
49
50
  # prepare the "head"
@@ -290,6 +291,17 @@ module Eth
290
291
  end
291
292
  end
292
293
 
294
+ # Build event signature string from ABI interface.
295
+ #
296
+ # @param interface [Hash] ABI event interface.
297
+ # @return [String] interface signature string.
298
+ def signature(interface)
299
+ name = interface.fetch("name")
300
+ inputs = interface.fetch("inputs", [])
301
+ types = inputs.map { |i| i.fetch("type") }
302
+ "#{name}(#{types.join(",")})"
303
+ end
304
+
293
305
  private
294
306
 
295
307
  # Properly encodes unsigned integers.
@@ -334,6 +346,8 @@ module Eth
334
346
  # Properly encodes byte-strings.
335
347
  def encode_bytes(arg, type)
336
348
  raise EncodingError, "Expecting String: #{arg}" unless arg.instance_of? String
349
+ arg = handle_hex_string arg, type
350
+
337
351
  if type.sub_type.empty?
338
352
  size = Util.zpad_int arg.size
339
353
  padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
@@ -392,5 +406,23 @@ module Eth
392
406
  raise EncodingError, "Could not parse address: #{arg}"
393
407
  end
394
408
  end
409
+
410
+ # The ABI encoder needs to be able to determine between a hex `"123"`
411
+ # and a binary `"123"` string.
412
+ def handle_hex_string(arg, type)
413
+ if Util.is_prefixed? arg or
414
+ (arg.size === type.sub_type.to_i * 2 and Util.is_hex? arg)
415
+
416
+ # There is no way telling whether a string is hex or binary with certainty
417
+ # in Ruby. Therefore, we assume a `0x` prefix to indicate a hex string.
418
+ # Additionally, if the string size is exactly the double of the expected
419
+ # binary size, we can assume a hex value.
420
+ return Util.hex_to_bin arg
421
+ else
422
+
423
+ # Everything else will be assumed binary or raw string.
424
+ return arg.b
425
+ end
426
+ end
395
427
  end
396
428
  end
data/lib/eth/chain.rb CHANGED
@@ -41,6 +41,9 @@ module Eth
41
41
  # Chain ID for Gnosis mainnet.
42
42
  XDAI = 100.freeze
43
43
 
44
+ # Chain ID for the Polygon Matic mainnet.
45
+ MATIC = 137.freeze
46
+
44
47
  # Chain ID for Arbitrum mainnet.
45
48
  ARBITRUM = 42161.freeze
46
49
 
@@ -77,6 +80,9 @@ module Eth
77
80
  # Chain ID for Optimistic Goerli testnet.
78
81
  GOERLI_OPTIMISM = 420.freeze
79
82
 
83
+ # Chain ID for the Polygon Mumbai testnet.
84
+ MUMBAI = 80001.freeze
85
+
80
86
  # Chain ID for Arbitrum Rinkeby testnet.
81
87
  RINKEBY_ARBITRUM = 421611.freeze
82
88
 
@@ -136,7 +142,7 @@ module Eth
136
142
  return v
137
143
  end
138
144
 
139
- # Converst a `v` value into a chain ID. This does not work for legacy signatures
145
+ # Converts a `v` value into a chain ID. This does not work for legacy signatures
140
146
  # with `v < 36` that do not conform with EIP-155.
141
147
  #
142
148
  # @param v [Integer] the signature's `v` value.
data/lib/eth/client.rb CHANGED
@@ -149,6 +149,204 @@ module Eth
149
149
  end
150
150
  end
151
151
 
152
+ # Deploys a contract and waits for it to be mined. Uses
153
+ # `eth_coinbase` or external signer if no sender key is provided.
154
+ #
155
+ # @overload deploy(contract)
156
+ # @param contract [Eth::Contract] contracts to deploy.
157
+ # @overload deploy(contract, *args, **kwargs)
158
+ # @param contract [Eth::Contract] contracts to deploy.
159
+ # *args Optional variable constructor parameter list
160
+ # **sender_key [Eth::Key] the sender private key.
161
+ # **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
162
+ # @return [String] the contract address.
163
+ def deploy_and_wait(contract, *args, **kwargs)
164
+ hash = wait_for_tx(deploy(contract, *args, **kwargs))
165
+ addr = eth_get_transaction_receipt(hash)["result"]["contractAddress"]
166
+ contract.address = Address.new(addr).to_s
167
+ end
168
+
169
+ # Deploys a contract. Uses `eth_coinbase` or external signer
170
+ # if no sender key is provided.
171
+ #
172
+ # @overload deploy(contract)
173
+ # @param contract [Eth::Contract] contracts to deploy.
174
+ # @overload deploy(contract, *args, **kwargs)
175
+ # @param contract [Eth::Contract] contracts to deploy.
176
+ # *args Optional variable constructor parameter list
177
+ # **sender_key [Eth::Key] the sender private key.
178
+ # **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
179
+ # @return [String] the transaction hash.
180
+ # @raise [ArgumentError] in case the contract does not have any source.
181
+ def deploy(contract, *args, **kwargs)
182
+ raise ArgumentError, "Cannot deploy contract without source or binary!" if contract.bin.nil?
183
+ raise ArgumentError, "Missing contract constructor params!" if contract.constructor_inputs.length != args.length
184
+ gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
185
+ data = contract.bin
186
+ unless args.empty?
187
+ data += encode_constructor_params(contract, args)
188
+ end
189
+ params = {
190
+ value: 0,
191
+ gas_limit: gas_limit,
192
+ chain_id: chain_id,
193
+ data: data,
194
+ }
195
+ if kwargs[:legacy]
196
+ params.merge!({
197
+ gas_price: max_fee_per_gas,
198
+ })
199
+ else
200
+ params.merge!({
201
+ priority_fee: max_priority_fee_per_gas,
202
+ max_gas_fee: max_fee_per_gas,
203
+ })
204
+ end
205
+ unless kwargs[:sender_key].nil?
206
+ # Uses the provided key as sender and signer
207
+ params.merge!({
208
+ from: kwargs[:sender_key].address,
209
+ nonce: get_nonce(kwargs[:sender_key].address),
210
+ })
211
+ tx = Eth::Tx.new(params)
212
+ tx.sign kwargs[:sender_key]
213
+ return eth_send_raw_transaction(tx.hex)["result"]
214
+ else
215
+ # Uses the default account as sender and external signer
216
+ params.merge!({
217
+ from: default_account,
218
+ nonce: get_nonce(default_account),
219
+ })
220
+ return eth_send_transaction(params)["result"]
221
+ end
222
+ end
223
+
224
+ # Calls a contract function without executing it
225
+ # (non-transactional contract read).
226
+ #
227
+ # @overload call(contract, function_name)
228
+ # @param contract [Eth::Contract] subject contract to call.
229
+ # @param function_name [String] method name to be called.
230
+ # @overload call(contract, function_name, value)
231
+ # @param contract [Eth::Contract] subject contract to call.
232
+ # @param function_name [String] method name to be called.
233
+ # @param value [Integer|String] function arguments.
234
+ # @overload call(contract, function_name, value, sender_key, legacy)
235
+ # @param contract [Eth::Contract] subject contract to call.
236
+ # @param function_name [String] method name to be called.
237
+ # @param value [Integer|String] function arguments.
238
+ # @param sender_key [Eth::Key] the sender private key.
239
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
240
+ # @return [Object] returns the result of the call.
241
+ def call(contract, function_name, *args, **kwargs)
242
+ func = contract.functions.select { |func| func.name == function_name }[0]
243
+ raise ArgumentError, "function_name does not exist!" if func.nil?
244
+ output = call_raw(contract, func, *args, **kwargs)
245
+ if output&.length == 1
246
+ return output[0]
247
+ else
248
+ return output
249
+ end
250
+ end
251
+
252
+ # Executes a contract function with a transaction (transactional
253
+ # contract read/write).
254
+ #
255
+ # @overload transact(contract, function_name)
256
+ # @param contract [Eth::Contract] subject contract to call.
257
+ # @param function_name [String] method name to be called.
258
+ # @overload transact(contract, function_name, value)
259
+ # @param contract [Eth::Contract] subject contract to call.
260
+ # @param function_name [String] method name to be called.
261
+ # @param value [Integer|String] function arguments.
262
+ # @overload transact(contract, function_name, value, sender_key, legacy, address)
263
+ # @param contract [Eth::Contract] subject contract to call.
264
+ # @param function_name [String] method name to be called.
265
+ # @param value [Integer|String] function arguments.
266
+ # @param sender_key [Eth::Key] the sender private key.
267
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
268
+ # @param address [String] contract address.
269
+ # @return [Object] returns the result of the call.
270
+ def transact(contract, function_name, *args, **kwargs)
271
+ gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
272
+ fun = contract.functions.select { |func| func.name == function_name }[0]
273
+ params = {
274
+ value: 0,
275
+ gas_limit: gas_limit,
276
+ chain_id: chain_id,
277
+ to: kwargs[:address] || contract.address,
278
+ data: call_payload(fun, args),
279
+ }
280
+ if kwargs[:legacy]
281
+ params.merge!({
282
+ gas_price: max_fee_per_gas,
283
+ })
284
+ else
285
+ params.merge!({
286
+ priority_fee: max_priority_fee_per_gas,
287
+ max_gas_fee: max_fee_per_gas,
288
+ })
289
+ end
290
+ unless kwargs[:sender_key].nil?
291
+ # use the provided key as sender and signer
292
+ params.merge!({
293
+ from: kwargs[:sender_key].address,
294
+ nonce: get_nonce(kwargs[:sender_key].address),
295
+ })
296
+ tx = Eth::Tx.new(params)
297
+ tx.sign kwargs[:sender_key]
298
+ return eth_send_raw_transaction(tx.hex)["result"]
299
+ else
300
+ # use the default account as sender and external signer
301
+ params.merge!({
302
+ from: default_account,
303
+ nonce: get_nonce(default_account),
304
+ })
305
+ return eth_send_transaction(params)["result"]
306
+ end
307
+ end
308
+
309
+ # Executes a contract function with a transaction and waits for it
310
+ # to be mined (transactional contract read/write).
311
+ #
312
+ # @overload transact_and_wait(contract, function_name)
313
+ # @param contract [Eth::Contract] subject contract to call.
314
+ # @param function_name [String] method name to be called.
315
+ # @overload transact_and_wait(contract, function_name, value)
316
+ # @param contract [Eth::Contract] subject contract to call.
317
+ # @param function_name [String] method name to be called.
318
+ # @param value [Integer|String] function arguments.
319
+ # @overload transact_and_wait(contract, function_name, value, sender_key, legacy, address)
320
+ # @param contract [Eth::Contract] subject contract to call.
321
+ # @param function_name [String] method name to be called.
322
+ # @param value [Integer|String] function arguments.
323
+ # @param sender_key [Eth::Key] the sender private key.
324
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
325
+ # @param address [String] contract address.
326
+ # @return [Object] returns the result of the call.
327
+ def transact_and_wait(contract, function_name, *args, **kwargs)
328
+ wait_for_tx(transact(contract, function_name, *args, **kwargs))
329
+ end
330
+
331
+ # Provides an interface to call `isValidSignature` as per EIP-1271 on a given
332
+ # smart contract to verify the given hash and signature matching the magic
333
+ # value.
334
+ #
335
+ # @param contract [Eth::Contract] a deployed contract implementing EIP-1271.
336
+ # @param hash [String] the message hash to be checked against the signature.
337
+ # @param signature [String] the signature to be recovered by the contract.
338
+ # @param magic [String] the expected magic value (defaults to `1626ba7e`).
339
+ # @return [Boolean] true if magic matches and signature is valid.
340
+ # @raise [ArgumentError] in case the contract cannot be called yet.
341
+ def is_valid_signature(contract, hash, signature, magic = "1626ba7e")
342
+ raise ArgumentError, "Contract not deployed yet." if contract.address.nil?
343
+ hash = Util.hex_to_bin hash if Util.is_hex? hash
344
+ signature = Util.hex_to_bin signature if Util.is_hex? signature
345
+ magic = Util.hex_to_bin magic if Util.is_hex? magic
346
+ result = call(contract, "isValidSignature", hash, signature)
347
+ return result === magic
348
+ end
349
+
152
350
  # Gives control over resetting the RPC request ID back to zero.
153
351
  # Usually not needed.
154
352
  #
@@ -169,7 +367,7 @@ module Eth
169
367
  # Waits for an transaction to be mined by the connected chain.
170
368
  #
171
369
  # @param hash [String] the transaction hash.
172
- # @return [String] the transactin hash once the transaction is mined.
370
+ # @return [String] the transaction hash once the transaction is mined.
173
371
  # @raise [Timeout::Error] if it's not mined within 5 minutes.
174
372
  def wait_for_tx(hash)
175
373
  start_time = Time.now
@@ -193,6 +391,55 @@ module Eth
193
391
 
194
392
  private
195
393
 
394
+ # Non-transactional function call called from call().
395
+ def call_raw(contract, func, *args, **kwargs)
396
+ gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
397
+ params = {
398
+ gas_limit: gas_limit,
399
+ chain_id: chain_id,
400
+ data: call_payload(func, args),
401
+ }
402
+ if kwargs[:address] || contract.address
403
+ params.merge!({ to: kwargs[:address] || contract.address })
404
+ end
405
+ if kwargs[:legacy]
406
+ params.merge!({
407
+ gas_price: max_fee_per_gas,
408
+ })
409
+ else
410
+ params.merge!({
411
+ priority_fee: max_priority_fee_per_gas,
412
+ max_gas_fee: max_fee_per_gas,
413
+ })
414
+ end
415
+ unless kwargs[:sender_key].nil?
416
+ # Uses the provided key as sender and signer
417
+ params.merge!({
418
+ from: kwargs[:sender_key].address,
419
+ nonce: get_nonce(kwargs[:sender_key].address),
420
+ })
421
+ tx = Eth::Tx.new(params)
422
+ tx.sign kwargs[:sender_key]
423
+ end
424
+ raw_result = eth_call(params)["result"]
425
+ types = func.outputs.map { |i| i.type }
426
+ return nil if raw_result == "0x"
427
+ Eth::Abi.decode(types, raw_result)
428
+ end
429
+
430
+ # Encodes function call payloads.
431
+ def call_payload(fun, args)
432
+ types = fun.inputs.map { |i| i.type }
433
+ encoded_str = Util.bin_to_hex(Eth::Abi.encode(types, args))
434
+ "0x" + fun.signature + (encoded_str.empty? ? "0" * 64 : encoded_str)
435
+ end
436
+
437
+ # Encodes constructor params
438
+ def encode_constructor_params(contract, args)
439
+ types = contract.constructor_inputs.map { |input| input.type }
440
+ Util.bin_to_hex(Eth::Abi.encode(types, args))
441
+ end
442
+
196
443
  # Prepares parameters and sends the command to the client.
197
444
  def send_command(command, args)
198
445
  args << "latest" if ["eth_getBalance", "eth_call"].include? command
@@ -230,3 +477,7 @@ module Eth
230
477
  end
231
478
  end
232
479
  end
480
+
481
+ # Load the client/* libraries
482
+ require "eth/client/http"
483
+ require "eth/client/ipc"
@@ -0,0 +1,42 @@
1
+ # Copyright (c) 2016-2022 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
+ # Provide classes for contract event.
21
+ class Contract::Event
22
+ attr_accessor :name, :signature, :input_types, :inputs, :event_string, :address
23
+
24
+ # Constructor of the {Eth::Contract::Event} class.
25
+ #
26
+ # @param data [Hash] contract event data.
27
+ def initialize(data)
28
+ @name = data["name"]
29
+ @input_types = data["inputs"].collect { |x| x["type"] }
30
+ @inputs = data["inputs"].collect { |x| x["name"] }
31
+ @event_string = "#{@name}(#{@input_types.join(",")})"
32
+ @signature = Digest::Keccak.hexdigest(@event_string, 256)
33
+ end
34
+
35
+ # Set the address of the smart contract
36
+ #
37
+ # @param address [String] contract address.
38
+ def set_address(address)
39
+ @address = address.nil? ? nil : Eth::Address.new(address).address
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,57 @@
1
+ # Copyright (c) 2016-2022 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 the methods for smart contract function.
21
+ class Contract::Function
22
+ attr_accessor :name, :inputs, :outputs, :signature, :constant, :function_string
23
+
24
+ # Constructor of the {Eth::Function} class.
25
+ #
26
+ # @param data [Hash] function input and output data.
27
+ def initialize(data)
28
+ @name = data["name"]
29
+ @constant = data["constant"]
30
+ @inputs = data["inputs"].map do |input|
31
+ Eth::Contract::FunctionInput.new(input)
32
+ end
33
+ @outputs = data["outputs"].collect do |output|
34
+ Eth::Contract::FunctionOutput.new(output)
35
+ end
36
+ @function_string = self.class.calc_signature(@name, @inputs)
37
+ @signature = self.class.encoded_function_signature(@function_string)
38
+ end
39
+
40
+ # Creates function strings.
41
+ #
42
+ # @param name [String] function name.
43
+ # @param inputs [Array<Eth::Contract::FunctionInput>] function input class list.
44
+ # @return [String] function string.
45
+ def self.calc_signature(name, inputs)
46
+ "#{name}(#{inputs.collect { |x| x.type }.join(",")})"
47
+ end
48
+
49
+ # Encodes a function signature.
50
+ #
51
+ # @param signature [String] function signature.
52
+ # @return [String] encoded function signature string.
53
+ def self.encoded_function_signature(signature)
54
+ Util.bin_to_hex Util.keccak256(signature)[0..3]
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2016-2022 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
+ # Provide classes for contract function input.
21
+ class Contract::FunctionInput
22
+ attr_accessor :type, :name
23
+
24
+ # Constructor of the {Eth::Contract::FunctionInput} class.
25
+ #
26
+ # @param data [Hash] contract abi data.
27
+ def initialize(data)
28
+ @type = Eth::Abi::Type.parse(data["type"])
29
+ @name = data["name"]
30
+ end
31
+
32
+ # Returns complete types with subtypes, e.g., `uint256`.
33
+ def type
34
+ @type.base_type + @type.sub_type
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2016-2022 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
+ # Provide classes for contract function output.
21
+ class Contract::FunctionOutput
22
+ attr_accessor :type, :name
23
+
24
+ # Constructor of the {Eth::Contract::FunctionOutput} class.
25
+ #
26
+ # @param data [Hash] contract abi data.
27
+ def initialize(data)
28
+ @type = Eth::Abi::Type.parse(data["type"])
29
+ @name = data["name"]
30
+ end
31
+
32
+ # Returns complete types with subtypes, e.g., `uint256`.
33
+ def type
34
+ @type.base_type + @type.sub_type
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,47 @@
1
+ # Copyright (c) 2016-2022 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
+ # Provide classes for contract initializer.
21
+ class Contract::Initializer
22
+ attr_accessor :contracts, :file
23
+
24
+ # Constructor of the {Eth::Contract::Initializer} class.
25
+ #
26
+ # @param file [String] file path to solidity code.
27
+ def initialize(file)
28
+ sol_output = Eth::Solidity.new.compile(file)
29
+ contracts = sol_output.keys
30
+
31
+ @contracts = []
32
+ contracts.each do |contract|
33
+ abi = sol_output[contract]["abi"]
34
+ name = contract
35
+ code = sol_output[contract]["bin"]
36
+ @contracts << Contract.new(name, code, abi)
37
+ end
38
+ end
39
+
40
+ # Builds and returns all contracts.
41
+ def build_all
42
+ @contracts.each do |contract|
43
+ contract.build
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,143 @@
1
+ # Copyright (c) 2016-2022 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 classes to access smart contracts
21
+ class Contract
22
+ attr_reader :address
23
+ attr_accessor :key
24
+ attr_accessor :gas_limit, :gas_price, :max_fee_per_gas, :max_priority_fee_per_gas, :nonce
25
+ attr_accessor :bin, :name, :abi, :class_object
26
+ attr_accessor :events, :functions, :constructor_inputs
27
+
28
+ # Constructor of the {Eth::Contract} class.
29
+ #
30
+ # @param name [String] contract name.
31
+ # @param bin [String] contract bin string.
32
+ # @param abi [String] contract abi string.
33
+ def initialize(name, bin, abi)
34
+ @name = name
35
+ @bin = bin
36
+ @abi = abi
37
+ @constructor_inputs, @functions, @events = parse_abi(abi)
38
+ end
39
+
40
+ # Creates a contract wrapper from a Solidity file.
41
+ #
42
+ # @param file [String] solidity file path.
43
+ # @param contract_index [Number] specify contract.
44
+ # @return [Eth::Contract::Object] Returns the class of the smart contract.
45
+ # @raise [ArgumentError] if the file path is empty or no contracts were compiled.
46
+ def self.from_file(file:, contract_index: 0)
47
+ raise ArgumentError, "Cannot find the contract at #{file.to_s}!" if !File.exist?(file.to_s)
48
+ contracts = Eth::Contract::Initializer.new(file).build_all
49
+ raise ArgumentError, "No contracts compiled." if contracts.empty?
50
+ contracts[contract_index].class_object.new
51
+ end
52
+
53
+ # Creates a contract wrapper from ABI and address.
54
+ #
55
+ # @param abi [String] contract abi string.
56
+ # @param address [String] contract address.
57
+ # @param name [String] name of contract.
58
+ # @return [Eth::Contract::Object] Returns the class of the smart contract.
59
+ # @raise [JSON::ParserError] if the json format is wrong.
60
+ # @raise [ArgumentError] if ABI, address, or name is missing.
61
+ def self.from_abi(abi:, address:, name:)
62
+ abi = abi.is_a?(Array) ? abi : JSON.parse(abi)
63
+ contract = Eth::Contract.new(name, nil, abi)
64
+ contract.build
65
+ contract = contract.class_object.new
66
+ contract.address = address
67
+ contract
68
+ end
69
+
70
+ # Creates a contract wrapper from binary and ABI.
71
+ #
72
+ # @param bin [String] contract bin string.
73
+ # @param abi [String] contract abi string.
74
+ # @param name [String] name of contract.
75
+ # @return [Eth::Contract::Object] Returns the class of the smart contract.
76
+ # @raise [JSON::ParserError] if the json format is wrong.
77
+ # @raise [ArgumentError] if ABI, binary, or name is missing.
78
+ def self.from_bin(bin:, abi:, name:)
79
+ abi = abi.is_a?(Array) ? abi : JSON.parse(abi)
80
+ contract = Eth::Contract.new(name, bin, abi)
81
+ contract.build
82
+ contract.class_object.new
83
+ end
84
+
85
+ # Sets the address of the smart contract.
86
+ #
87
+ # @param addr [String|Eth::Address] contract address string.
88
+ def address=(addr)
89
+ if addr.is_a? Eth::Address
90
+ @address = addr.to_s
91
+ else
92
+ @address = Eth::Address.new(addr).to_s
93
+ end
94
+ @events.each do |event|
95
+ event.set_address(@address)
96
+ end
97
+ end
98
+
99
+ # Create meta classes for smart contracts.
100
+ def build
101
+ class_name = @name
102
+ parent = self
103
+ class_methods = Class.new do
104
+ extend Forwardable
105
+ def_delegators :parent, :key, :key=
106
+ def_delegators :parent, :name, :abi, :bin
107
+ def_delegators :parent, :gas_limit, :gas_price, :gas_limit=, :gas_price=, :nonce, :nonce=
108
+ def_delegators :parent, :max_fee_per_gas, :max_fee_per_gas=, :max_priority_fee_per_gas, :max_priority_fee_per_gas=
109
+ def_delegators :parent, :events
110
+ def_delegators :parent, :address, :address=
111
+ def_delegator :parent, :functions
112
+ def_delegator :parent, :constructor_inputs
113
+ define_method :parent do
114
+ parent
115
+ end
116
+ end
117
+ Eth::Contract.send(:remove_const, class_name) if Eth::Contract.const_defined?(class_name, false)
118
+ Eth::Contract.const_set(class_name, class_methods)
119
+ @class_object = class_methods
120
+ end
121
+
122
+ private
123
+
124
+ def parse_abi(abi)
125
+ constructor = abi.detect { |x| x["type"] == "constructor" }
126
+ if !constructor.nil?
127
+ constructor_inputs = constructor["inputs"].map { |input| Eth::Contract::FunctionInput.new(input) }
128
+ else
129
+ constructor_inputs = []
130
+ end
131
+ functions = abi.select { |x| x["type"] == "function" }.map { |fun| Eth::Contract::Function.new(fun) }
132
+ events = abi.select { |x| x["type"] == "event" }.map { |evt| Eth::Contract::Event.new(evt) }
133
+ [constructor_inputs, functions, events]
134
+ end
135
+ end
136
+ end
137
+
138
+ # Load the contract/* libraries
139
+ require "eth/contract/event"
140
+ require "eth/contract/function"
141
+ require "eth/contract/function_input"
142
+ require "eth/contract/function_output"
143
+ require "eth/contract/initializer"
@@ -29,7 +29,7 @@ module Eth
29
29
  # A singleton class for binary values of fixed length.
30
30
  class << self
31
31
 
32
- # Create a serializable bianry of fixed size.
32
+ # Create a serializable binary of fixed size.
33
33
  #
34
34
  # @param l [Integer] the fixed size of the binary.
35
35
  # @param allow_empty [Boolean] indicator wether empty binaries should be allowed.
@@ -47,7 +47,7 @@ module Eth
47
47
  end
48
48
  end
49
49
 
50
- # Create a serializable bianry of variable size.
50
+ # Create a serializable binary of variable size.
51
51
  #
52
52
  # @param min_length [Integer] the minimum size of the binary.
53
53
  # @param max_length [Integer] the maximum size of the binary.
data/lib/eth/tx.rb CHANGED
@@ -69,13 +69,16 @@ module Eth
69
69
  # The zero byte is 0x00.
70
70
  ZERO_BYTE = "\x00".freeze
71
71
 
72
+ # Smart contract transaction gas cost
73
+ CREATE_GAS = 32_000.freeze
74
+
72
75
  # Creates a new transaction of any type for given parameters and chain ID.
73
76
  # Required parameters are (optional in brackets):
74
77
  # - EIP-1559: chain_id, nonce, priority_fee, max_gas_fee, gas_limit(, from, to,
75
78
  # value, data, access_list)
76
79
  # - EIP-2930: chain_id, nonce, gas_price, gas_limit, access_list(, from, to,
77
80
  # value, data)
78
- # - Legacy: nonce, gas_price, gas_lmit(, from, to, value, data)
81
+ # - Legacy: nonce, gas_price, gas_limit(, from, to, value, data)
79
82
  #
80
83
  # @param params [Hash] all necessary transaction fields.
81
84
  # @param chain_id [Integer] the EIP-155 Chain ID (legacy transactions only).
data/lib/eth/version.rb CHANGED
@@ -16,5 +16,5 @@
16
16
  module Eth
17
17
 
18
18
  # Defines the version of the {Eth} module.
19
- VERSION = "0.5.2".freeze
19
+ VERSION = "0.5.5".freeze
20
20
  end
data/lib/eth.rb CHANGED
@@ -22,9 +22,8 @@ require "eth/api"
22
22
  require "eth/address"
23
23
  require "eth/chain"
24
24
  require "eth/constant"
25
+ require "eth/contract"
25
26
  require "eth/client"
26
- require "eth/client/http"
27
- require "eth/client/ipc"
28
27
  require "eth/eip712"
29
28
  require "eth/key"
30
29
  require "eth/rlp"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Ellis
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2022-03-28 00:00:00.000000000 Z
12
+ date: 2022-05-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: keccak
@@ -57,16 +57,22 @@ dependencies:
57
57
  name: openssl
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - "~>"
60
+ - - ">="
61
61
  - !ruby/object:Gem::Version
62
62
  version: '2.2'
63
+ - - "<"
64
+ - !ruby/object:Gem::Version
65
+ version: '4.0'
63
66
  type: :runtime
64
67
  prerelease: false
65
68
  version_requirements: !ruby/object:Gem::Requirement
66
69
  requirements:
67
- - - "~>"
70
+ - - ">="
68
71
  - !ruby/object:Gem::Version
69
72
  version: '2.2'
73
+ - - "<"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.0'
70
76
  - !ruby/object:Gem::Dependency
71
77
  name: scrypt
72
78
  requirement: !ruby/object:Gem::Requirement
@@ -89,6 +95,7 @@ executables: []
89
95
  extensions: []
90
96
  extra_rdoc_files: []
91
97
  files:
98
+ - ".github/dependabot.yml"
92
99
  - ".github/workflows/codeql.yml"
93
100
  - ".github/workflows/docs.yml"
94
101
  - ".github/workflows/spec.yml"
@@ -108,6 +115,7 @@ files:
108
115
  - eth.gemspec
109
116
  - lib/eth.rb
110
117
  - lib/eth/abi.rb
118
+ - lib/eth/abi/event.rb
111
119
  - lib/eth/abi/type.rb
112
120
  - lib/eth/address.rb
113
121
  - lib/eth/api.rb
@@ -116,6 +124,12 @@ files:
116
124
  - lib/eth/client/http.rb
117
125
  - lib/eth/client/ipc.rb
118
126
  - lib/eth/constant.rb
127
+ - lib/eth/contract.rb
128
+ - lib/eth/contract/event.rb
129
+ - lib/eth/contract/function.rb
130
+ - lib/eth/contract/function_input.rb
131
+ - lib/eth/contract/function_output.rb
132
+ - lib/eth/contract/initializer.rb
119
133
  - lib/eth/eip712.rb
120
134
  - lib/eth/key.rb
121
135
  - lib/eth/key/decrypter.rb