eth 0.5.2 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
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