eth 0.5.3 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +18 -0
- data/.github/workflows/codeql.yml +4 -4
- data/.github/workflows/docs.yml +2 -2
- data/.github/workflows/spec.yml +2 -2
- data/CHANGELOG.md +42 -6
- data/README.md +34 -11
- data/eth.gemspec +1 -1
- data/lib/eth/abi/event.rb +1 -1
- data/lib/eth/abi/type.rb +1 -1
- data/lib/eth/abi.rb +44 -6
- data/lib/eth/chain.rb +7 -1
- data/lib/eth/client.rb +128 -97
- data/lib/eth/contract/event.rb +1 -0
- data/lib/eth/contract/function.rb +5 -4
- data/lib/eth/contract/function_input.rb +4 -2
- data/lib/eth/contract/function_output.rb +5 -0
- data/lib/eth/contract/initializer.rb +2 -1
- data/lib/eth/contract.rb +51 -28
- data/lib/eth/rlp/sedes/binary.rb +2 -2
- data/lib/eth/signature.rb +1 -1
- data/lib/eth/tx.rb +1 -1
- data/lib/eth/version.rb +1 -1
- data/lib/eth.rb +0 -7
- metadata +12 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aabc7ea4a3cd8e94e91e5e9d3bfb0600960079c72e9f99de61d721cae0a73feb
|
|
4
|
+
data.tar.gz: 04e28c96e36958ca58cd49e1e6182a17bf5bc56cab28bd0052f8881899250294
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e7413915597355913492ea5dc22167507ab4d75b3de8783b8c87fb0849e036316f33264053fd166ada749a08215d599197eb4b94d8c954c898aec80bb91f3d7
|
|
7
|
+
data.tar.gz: c8b0a8c288ffe282edbda04d73dff58baa66e9f50dba1998c2450b775ed27201b988048dcfd3b902ac81d19b6dfe9e73d60d86b0c300df7e0cddc301b14ec13c
|
|
@@ -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@
|
|
27
|
+
uses: actions/checkout@v3
|
|
28
28
|
- name: "Initialize CodeQL"
|
|
29
|
-
uses: github/codeql-action/init@
|
|
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@
|
|
33
|
+
uses: github/codeql-action/autobuild@v2
|
|
34
34
|
- name: "Perform CodeQL Analysis"
|
|
35
|
-
uses: github/codeql-action/analyze@
|
|
35
|
+
uses: github/codeql-action/analyze@v2
|
|
36
36
|
- uses: ruby/setup-ruby@v1
|
|
37
37
|
with:
|
|
38
38
|
ruby-version: '2.7'
|
data/.github/workflows/docs.yml
CHANGED
|
@@ -10,7 +10,7 @@ jobs:
|
|
|
10
10
|
docs:
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
steps:
|
|
13
|
-
- uses: actions/checkout@
|
|
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@
|
|
23
|
+
uses: JamesIves/github-pages-deploy-action@v4.3.3
|
|
24
24
|
with:
|
|
25
25
|
branch: gh-pages
|
|
26
26
|
folder: doc/
|
data/.github/workflows/spec.yml
CHANGED
|
@@ -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.
|
|
22
|
+
ruby: ['2.7', '3.1']
|
|
23
23
|
steps:
|
|
24
|
-
- uses: actions/checkout@
|
|
24
|
+
- uses: actions/checkout@v3
|
|
25
25
|
- uses: ruby/setup-ruby@v1
|
|
26
26
|
with:
|
|
27
27
|
ruby-version: ${{ matrix.ruby }}
|
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,8 +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)
|
|
49
|
-
- [2.9 Interact with Smart Contract](#29-interact-with-smart-contract)
|
|
45
|
+
- [2.8. Solidity Compiler Bindings](#28-solidity-compiler-bindings)
|
|
46
|
+
- [2.9. Interact with Smart Contract](#29-interact-with-smart-contract)
|
|
50
47
|
- [3. Documentation](#3-documentation)
|
|
51
48
|
- [4. Testing](#4-testing)
|
|
52
49
|
- [5. Contributing](#5-contributing)
|
|
@@ -232,7 +229,7 @@ cli.get_nonce cli.eth_coinbase["result"]
|
|
|
232
229
|
|
|
233
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.
|
|
234
231
|
|
|
235
|
-
### 2.8 Solidity Compiler Bindings
|
|
232
|
+
### 2.8. Solidity Compiler Bindings
|
|
236
233
|
Link a system-level Solidity compiler (`solc`) to your Ruby library and compile contracts.
|
|
237
234
|
|
|
238
235
|
```ruby
|
|
@@ -254,23 +251,49 @@ contract = solc.compile "spec/fixtures/contracts/greeter.sol"
|
|
|
254
251
|
|
|
255
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.
|
|
256
253
|
|
|
257
|
-
### 2.9 Interact with Smart Contract
|
|
254
|
+
### 2.9. Interact with Smart Contract
|
|
258
255
|
|
|
259
|
-
|
|
256
|
+
Create, compile, and deploy smart contracts.
|
|
260
257
|
|
|
261
258
|
```ruby
|
|
262
|
-
contract = Eth::Contract.
|
|
259
|
+
contract = Eth::Contract.from_file(file: 'spec/fixtures/contracts/dummy.sol')
|
|
263
260
|
# => #<Eth::Contract::Dummy:0x00007fbeee936598>
|
|
264
261
|
cli = Eth::Client.create "/tmp/geth.ipc"
|
|
265
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">
|
|
266
263
|
address = cli.deploy_and_wait(contract)
|
|
267
264
|
# => "0x2f2faa160420cee087ded96bad52475147136bd8"
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Transact with or call the deployed contract.
|
|
268
|
+
|
|
269
|
+
```ruby
|
|
268
270
|
cli.transact_and_wait(contract, "set", 1234)
|
|
269
271
|
# => "0x49ca4c0a5729da19a1d2574de9a444a9cd3219bdad81745b54f9cf3bb83b6a06"
|
|
270
272
|
cli.call(contract, "get")
|
|
271
273
|
# => 1234
|
|
272
274
|
```
|
|
273
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
|
+
|
|
274
297
|
## 3. Documentation
|
|
275
298
|
The documentation can be found at: https://q9f.github.io/eth.rb
|
|
276
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", "
|
|
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"
|
data/lib/eth/abi/event.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
|
|
20
|
+
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
|
|
21
21
|
module Abi
|
|
22
22
|
|
|
23
23
|
# Provides a module to decode transaction log events.
|
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
|
|
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
|
@@ -22,7 +22,7 @@ require "eth/abi/type"
|
|
|
22
22
|
# Provides the {Eth} module.
|
|
23
23
|
module Eth
|
|
24
24
|
|
|
25
|
-
# Provides a Ruby implementation of the Ethereum
|
|
25
|
+
# Provides a Ruby implementation of the Ethereum Application Binary Interface (ABI).
|
|
26
26
|
# ref: https://docs.soliditylang.org/en/develop/abi-spec.html
|
|
27
27
|
module Abi
|
|
28
28
|
extend self
|
|
@@ -44,7 +44,7 @@ module Eth
|
|
|
44
44
|
# @return [String] the encoded ABI data.
|
|
45
45
|
def encode(types, args)
|
|
46
46
|
|
|
47
|
-
#
|
|
47
|
+
# parse all types
|
|
48
48
|
parsed_types = types.map { |t| Type.parse(t) }
|
|
49
49
|
|
|
50
50
|
# prepare the "head"
|
|
@@ -74,7 +74,7 @@ module Eth
|
|
|
74
74
|
# @return [String] the encoded type.
|
|
75
75
|
# @raise [EncodingError] if value does not match type.
|
|
76
76
|
def encode_type(type, arg)
|
|
77
|
-
if %w(string bytes).include? type.base_type and type.sub_type.empty?
|
|
77
|
+
if %w(string bytes).include? type.base_type and type.sub_type.empty? and type.dimensions.empty?
|
|
78
78
|
raise EncodingError, "Argument must be a String" unless arg.instance_of? String
|
|
79
79
|
|
|
80
80
|
# encodes strings and bytes
|
|
@@ -89,10 +89,24 @@ module Eth
|
|
|
89
89
|
head += encode_type Type.size_type, arg.size
|
|
90
90
|
nested_sub = type.nested_sub
|
|
91
91
|
nested_sub_size = type.nested_sub.size
|
|
92
|
-
arg.size.times do |i|
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
# calculate offsets
|
|
94
|
+
if %w(string bytes).include?(type.base_type) && type.sub_type.empty?
|
|
95
|
+
offset = 0
|
|
96
|
+
arg.size.times do |i|
|
|
97
|
+
if i == 0
|
|
98
|
+
offset = arg.size * 32
|
|
99
|
+
else
|
|
100
|
+
number_of_words = ((arg[i - 1].size + 32 - 1) / 32).floor
|
|
101
|
+
total_bytes_length = number_of_words * 32
|
|
102
|
+
offset += total_bytes_length + 32
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
head += encode_type Type.size_type, offset
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
arg.size.times do |i|
|
|
96
110
|
head += encode_type nested_sub, arg[i]
|
|
97
111
|
end
|
|
98
112
|
return "#{head}#{tail}"
|
|
@@ -306,6 +320,7 @@ module Eth
|
|
|
306
320
|
|
|
307
321
|
# Properly encodes unsigned integers.
|
|
308
322
|
def encode_uint(arg, type)
|
|
323
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
|
309
324
|
raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::UINT_MAX or arg < Constant::UINT_MIN
|
|
310
325
|
real_size = type.sub_type.to_i
|
|
311
326
|
i = arg.to_i
|
|
@@ -315,6 +330,7 @@ module Eth
|
|
|
315
330
|
|
|
316
331
|
# Properly encodes signed integers.
|
|
317
332
|
def encode_int(arg, type)
|
|
333
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
|
318
334
|
raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::INT_MAX or arg < Constant::INT_MIN
|
|
319
335
|
real_size = type.sub_type.to_i
|
|
320
336
|
i = arg.to_i
|
|
@@ -330,6 +346,7 @@ module Eth
|
|
|
330
346
|
|
|
331
347
|
# Properly encodes unsigned fixed-point numbers.
|
|
332
348
|
def encode_ufixed(arg, type)
|
|
349
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
|
333
350
|
high, low = type.sub_type.split("x").map(&:to_i)
|
|
334
351
|
raise ValueOutOfBounds, arg unless arg >= 0 and arg < 2 ** high
|
|
335
352
|
return Util.zpad_int((arg * 2 ** low).to_i)
|
|
@@ -337,6 +354,7 @@ module Eth
|
|
|
337
354
|
|
|
338
355
|
# Properly encodes signed fixed-point numbers.
|
|
339
356
|
def encode_fixed(arg, type)
|
|
357
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
|
340
358
|
high, low = type.sub_type.split("x").map(&:to_i)
|
|
341
359
|
raise ValueOutOfBounds, arg unless arg >= -2 ** (high - 1) and arg < 2 ** (high - 1)
|
|
342
360
|
i = (arg * 2 ** low).to_i
|
|
@@ -346,6 +364,8 @@ module Eth
|
|
|
346
364
|
# Properly encodes byte-strings.
|
|
347
365
|
def encode_bytes(arg, type)
|
|
348
366
|
raise EncodingError, "Expecting String: #{arg}" unless arg.instance_of? String
|
|
367
|
+
arg = handle_hex_string arg, type
|
|
368
|
+
|
|
349
369
|
if type.sub_type.empty?
|
|
350
370
|
size = Util.zpad_int arg.size
|
|
351
371
|
padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size)
|
|
@@ -404,5 +424,23 @@ module Eth
|
|
|
404
424
|
raise EncodingError, "Could not parse address: #{arg}"
|
|
405
425
|
end
|
|
406
426
|
end
|
|
427
|
+
|
|
428
|
+
# The ABI encoder needs to be able to determine between a hex `"123"`
|
|
429
|
+
# and a binary `"123"` string.
|
|
430
|
+
def handle_hex_string(arg, type)
|
|
431
|
+
if Util.is_prefixed? arg or
|
|
432
|
+
(arg.size === type.sub_type.to_i * 2 and Util.is_hex? arg)
|
|
433
|
+
|
|
434
|
+
# There is no way telling whether a string is hex or binary with certainty
|
|
435
|
+
# in Ruby. Therefore, we assume a `0x` prefix to indicate a hex string.
|
|
436
|
+
# Additionally, if the string size is exactly the double of the expected
|
|
437
|
+
# binary size, we can assume a hex value.
|
|
438
|
+
return Util.hex_to_bin arg
|
|
439
|
+
else
|
|
440
|
+
|
|
441
|
+
# Everything else will be assumed binary or raw string.
|
|
442
|
+
return arg.b
|
|
443
|
+
end
|
|
444
|
+
end
|
|
407
445
|
end
|
|
408
446
|
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
|
-
#
|
|
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,108 +149,55 @@ module Eth
|
|
|
149
149
|
end
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
#
|
|
153
|
-
#
|
|
154
|
-
# if no sender key is provided.
|
|
152
|
+
# Deploys a contract and waits for it to be mined. Uses
|
|
153
|
+
# `eth_coinbase` or external signer if no sender key is provided.
|
|
155
154
|
#
|
|
156
155
|
# @overload deploy(contract)
|
|
157
156
|
# @param contract [Eth::Contract] contracts to deploy.
|
|
158
|
-
# @overload deploy(contract,
|
|
159
|
-
# @param contract [Eth::Contract] contracts to deploy.
|
|
160
|
-
# @param sender_key [Eth::Key] the sender private key.
|
|
161
|
-
# @overload deploy(contract, sender_key, legacy)
|
|
157
|
+
# @overload deploy(contract, *args, **kwargs)
|
|
162
158
|
# @param contract [Eth::Contract] contracts to deploy.
|
|
163
|
-
#
|
|
164
|
-
#
|
|
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
|
+
# **gas_limit [Integer] optional gas limit override for deploying the contract.
|
|
165
163
|
# @return [String] the contract address.
|
|
166
|
-
def deploy_and_wait(contract,
|
|
167
|
-
hash = wait_for_tx(deploy(contract,
|
|
168
|
-
|
|
164
|
+
def deploy_and_wait(contract, *args, **kwargs)
|
|
165
|
+
hash = wait_for_tx(deploy(contract, *args, **kwargs))
|
|
166
|
+
addr = eth_get_transaction_receipt(hash)["result"]["contractAddress"]
|
|
167
|
+
contract.address = Address.new(addr).to_s
|
|
169
168
|
end
|
|
170
169
|
|
|
171
|
-
#
|
|
170
|
+
# Deploys a contract. Uses `eth_coinbase` or external signer
|
|
172
171
|
# if no sender key is provided.
|
|
173
172
|
#
|
|
174
173
|
# @overload deploy(contract)
|
|
175
174
|
# @param contract [Eth::Contract] contracts to deploy.
|
|
176
|
-
# @overload deploy(contract,
|
|
177
|
-
# @param contract [Eth::Contract] contracts to deploy.
|
|
178
|
-
# @param sender_key [Eth::Key] the sender private key.
|
|
179
|
-
# @overload deploy(contract, sender_key, legacy)
|
|
175
|
+
# @overload deploy(contract, *args, **kwargs)
|
|
180
176
|
# @param contract [Eth::Contract] contracts to deploy.
|
|
181
|
-
#
|
|
182
|
-
#
|
|
177
|
+
# *args Optional variable constructor parameter list
|
|
178
|
+
# **sender_key [Eth::Key] the sender private key.
|
|
179
|
+
# **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
|
180
|
+
# **gas_limit [Integer] optional gas limit override for deploying the contract.
|
|
183
181
|
# @return [String] the transaction hash.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
data
|
|
191
|
-
}
|
|
192
|
-
if legacy
|
|
193
|
-
params.merge!({
|
|
194
|
-
gas_price: max_fee_per_gas,
|
|
195
|
-
})
|
|
196
|
-
else
|
|
197
|
-
params.merge!({
|
|
198
|
-
priority_fee: max_priority_fee_per_gas,
|
|
199
|
-
max_gas_fee: max_fee_per_gas,
|
|
200
|
-
})
|
|
201
|
-
end
|
|
202
|
-
unless sender_key.nil?
|
|
203
|
-
# use the provided key as sender and signer
|
|
204
|
-
params.merge!({
|
|
205
|
-
from: sender_key.address,
|
|
206
|
-
nonce: get_nonce(sender_key.address),
|
|
207
|
-
})
|
|
208
|
-
tx = Eth::Tx.new(params)
|
|
209
|
-
tx.sign sender_key
|
|
210
|
-
return eth_send_raw_transaction(tx.hex)["result"]
|
|
211
|
-
else
|
|
212
|
-
# use the default account as sender and external signer
|
|
213
|
-
params.merge!({
|
|
214
|
-
from: default_account,
|
|
215
|
-
nonce: get_nonce(default_account),
|
|
216
|
-
})
|
|
217
|
-
return eth_send_transaction(params)["result"]
|
|
182
|
+
# @raise [ArgumentError] in case the contract does not have any source.
|
|
183
|
+
def deploy(contract, *args, **kwargs)
|
|
184
|
+
raise ArgumentError, "Cannot deploy contract without source or binary!" if contract.bin.nil?
|
|
185
|
+
raise ArgumentError, "Missing contract constructor params!" if contract.constructor_inputs.length != args.length
|
|
186
|
+
data = contract.bin
|
|
187
|
+
unless args.empty?
|
|
188
|
+
data += encode_constructor_params(contract, args)
|
|
218
189
|
end
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
encoded_str = Util.bin_to_hex(Eth::Abi.encode(types, args))
|
|
225
|
-
"0x" + fun.signature + (encoded_str.empty? ? "0" * 64 : encoded_str)
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
# Non-transactional function call called from call().
|
|
229
|
-
#
|
|
230
|
-
# @overload call_raw(contract, func)
|
|
231
|
-
# @param contract [Eth::Contract] subject contract to call.
|
|
232
|
-
# @param func [Eth::Contract::Function] method name to be called.
|
|
233
|
-
# @overload call_raw(contract, func, value)
|
|
234
|
-
# @param contract [Eth::Contract] subject contract to call.
|
|
235
|
-
# @param func [Eth::Contract::Function] method name to be called.
|
|
236
|
-
# @param value [Integer|String] function arguments.
|
|
237
|
-
# @overload call_raw(contract, func, value, sender_key, legacy)
|
|
238
|
-
# @param contract [Eth::Contract] subject contract to call.
|
|
239
|
-
# @param func [Eth::Contract::Function] method name to be called.
|
|
240
|
-
# @param value [Integer|String] function arguments.
|
|
241
|
-
# @param sender_key [Eth::Key] the sender private key.
|
|
242
|
-
# @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
|
243
|
-
# @return [Object] returns the result of the call.
|
|
244
|
-
def call_raw(contract, func, *args, **kwargs)
|
|
245
|
-
gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
|
|
190
|
+
gas_limit = if kwargs[:gas_limit]
|
|
191
|
+
kwargs[:gas_limit]
|
|
192
|
+
else
|
|
193
|
+
Tx.estimate_intrinsic_gas(data) + Tx::CREATE_GAS
|
|
194
|
+
end
|
|
246
195
|
params = {
|
|
196
|
+
value: 0,
|
|
247
197
|
gas_limit: gas_limit,
|
|
248
198
|
chain_id: chain_id,
|
|
249
|
-
data:
|
|
199
|
+
data: data,
|
|
250
200
|
}
|
|
251
|
-
if kwargs[:address] || contract.address
|
|
252
|
-
params.merge!({ to: kwargs[:address] || contract.address })
|
|
253
|
-
end
|
|
254
201
|
if kwargs[:legacy]
|
|
255
202
|
params.merge!({
|
|
256
203
|
gas_price: max_fee_per_gas,
|
|
@@ -262,26 +209,26 @@ module Eth
|
|
|
262
209
|
})
|
|
263
210
|
end
|
|
264
211
|
unless kwargs[:sender_key].nil?
|
|
265
|
-
#
|
|
212
|
+
# Uses the provided key as sender and signer
|
|
266
213
|
params.merge!({
|
|
267
214
|
from: kwargs[:sender_key].address,
|
|
268
215
|
nonce: get_nonce(kwargs[:sender_key].address),
|
|
269
216
|
})
|
|
270
217
|
tx = Eth::Tx.new(params)
|
|
271
218
|
tx.sign kwargs[:sender_key]
|
|
219
|
+
return eth_send_raw_transaction(tx.hex)["result"]
|
|
272
220
|
else
|
|
273
|
-
#
|
|
221
|
+
# Uses the default account as sender and external signer
|
|
274
222
|
params.merge!({
|
|
275
223
|
from: default_account,
|
|
276
224
|
nonce: get_nonce(default_account),
|
|
277
225
|
})
|
|
226
|
+
return eth_send_transaction(params)["result"]
|
|
278
227
|
end
|
|
279
|
-
raw_result = eth_call(params)["result"]
|
|
280
|
-
types = func.outputs.map { |i| i.type }
|
|
281
|
-
Eth::Abi.decode(types, raw_result)
|
|
282
228
|
end
|
|
283
229
|
|
|
284
|
-
#
|
|
230
|
+
# Calls a contract function without executing it
|
|
231
|
+
# (non-transactional contract read).
|
|
285
232
|
#
|
|
286
233
|
# @overload call(contract, function_name)
|
|
287
234
|
# @param contract [Eth::Contract] subject contract to call.
|
|
@@ -290,25 +237,27 @@ module Eth
|
|
|
290
237
|
# @param contract [Eth::Contract] subject contract to call.
|
|
291
238
|
# @param function_name [String] method name to be called.
|
|
292
239
|
# @param value [Integer|String] function arguments.
|
|
293
|
-
# @overload call(contract, function_name, value, sender_key, legacy)
|
|
240
|
+
# @overload call(contract, function_name, value, sender_key, legacy, gas_limit)
|
|
294
241
|
# @param contract [Eth::Contract] subject contract to call.
|
|
295
242
|
# @param function_name [String] method name to be called.
|
|
296
243
|
# @param value [Integer|String] function arguments.
|
|
297
244
|
# @param sender_key [Eth::Key] the sender private key.
|
|
298
245
|
# @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
|
246
|
+
# @param gas_limit [Integer] optional gas limit override for deploying the contract.
|
|
299
247
|
# @return [Object] returns the result of the call.
|
|
300
248
|
def call(contract, function_name, *args, **kwargs)
|
|
301
249
|
func = contract.functions.select { |func| func.name == function_name }[0]
|
|
302
250
|
raise ArgumentError, "function_name does not exist!" if func.nil?
|
|
303
251
|
output = call_raw(contract, func, *args, **kwargs)
|
|
304
|
-
if output
|
|
252
|
+
if output&.length == 1
|
|
305
253
|
return output[0]
|
|
306
254
|
else
|
|
307
255
|
return output
|
|
308
256
|
end
|
|
309
257
|
end
|
|
310
258
|
|
|
311
|
-
#
|
|
259
|
+
# Executes a contract function with a transaction (transactional
|
|
260
|
+
# contract read/write).
|
|
312
261
|
#
|
|
313
262
|
# @overload transact(contract, function_name)
|
|
314
263
|
# @param contract [Eth::Contract] subject contract to call.
|
|
@@ -317,16 +266,21 @@ module Eth
|
|
|
317
266
|
# @param contract [Eth::Contract] subject contract to call.
|
|
318
267
|
# @param function_name [String] method name to be called.
|
|
319
268
|
# @param value [Integer|String] function arguments.
|
|
320
|
-
# @overload transact(contract, function_name, value, sender_key, legacy, address)
|
|
269
|
+
# @overload transact(contract, function_name, value, sender_key, legacy, address, gas_limit)
|
|
321
270
|
# @param contract [Eth::Contract] subject contract to call.
|
|
322
271
|
# @param function_name [String] method name to be called.
|
|
323
272
|
# @param value [Integer|String] function arguments.
|
|
324
273
|
# @param sender_key [Eth::Key] the sender private key.
|
|
325
274
|
# @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
|
326
275
|
# @param address [String] contract address.
|
|
276
|
+
# @param gas_limit [Integer] optional gas limit override for deploying the contract.
|
|
327
277
|
# @return [Object] returns the result of the call.
|
|
328
278
|
def transact(contract, function_name, *args, **kwargs)
|
|
329
|
-
gas_limit =
|
|
279
|
+
gas_limit = if kwargs[:gas_limit]
|
|
280
|
+
kwargs[:gas_limit]
|
|
281
|
+
else
|
|
282
|
+
Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
|
|
283
|
+
end
|
|
330
284
|
fun = contract.functions.select { |func| func.name == function_name }[0]
|
|
331
285
|
params = {
|
|
332
286
|
value: 0,
|
|
@@ -364,7 +318,8 @@ module Eth
|
|
|
364
318
|
end
|
|
365
319
|
end
|
|
366
320
|
|
|
367
|
-
#
|
|
321
|
+
# Executes a contract function with a transaction and waits for it
|
|
322
|
+
# to be mined (transactional contract read/write).
|
|
368
323
|
#
|
|
369
324
|
# @overload transact_and_wait(contract, function_name)
|
|
370
325
|
# @param contract [Eth::Contract] subject contract to call.
|
|
@@ -385,6 +340,25 @@ module Eth
|
|
|
385
340
|
wait_for_tx(transact(contract, function_name, *args, **kwargs))
|
|
386
341
|
end
|
|
387
342
|
|
|
343
|
+
# Provides an interface to call `isValidSignature` as per EIP-1271 on a given
|
|
344
|
+
# smart contract to verify the given hash and signature matching the magic
|
|
345
|
+
# value.
|
|
346
|
+
#
|
|
347
|
+
# @param contract [Eth::Contract] a deployed contract implementing EIP-1271.
|
|
348
|
+
# @param hash [String] the message hash to be checked against the signature.
|
|
349
|
+
# @param signature [String] the signature to be recovered by the contract.
|
|
350
|
+
# @param magic [String] the expected magic value (defaults to `1626ba7e`).
|
|
351
|
+
# @return [Boolean] true if magic matches and signature is valid.
|
|
352
|
+
# @raise [ArgumentError] in case the contract cannot be called yet.
|
|
353
|
+
def is_valid_signature(contract, hash, signature, magic = "1626ba7e")
|
|
354
|
+
raise ArgumentError, "Contract not deployed yet." if contract.address.nil?
|
|
355
|
+
hash = Util.hex_to_bin hash if Util.is_hex? hash
|
|
356
|
+
signature = Util.hex_to_bin signature if Util.is_hex? signature
|
|
357
|
+
magic = Util.hex_to_bin magic if Util.is_hex? magic
|
|
358
|
+
result = call(contract, "isValidSignature", hash, signature)
|
|
359
|
+
return result === magic
|
|
360
|
+
end
|
|
361
|
+
|
|
388
362
|
# Gives control over resetting the RPC request ID back to zero.
|
|
389
363
|
# Usually not needed.
|
|
390
364
|
#
|
|
@@ -405,7 +379,7 @@ module Eth
|
|
|
405
379
|
# Waits for an transaction to be mined by the connected chain.
|
|
406
380
|
#
|
|
407
381
|
# @param hash [String] the transaction hash.
|
|
408
|
-
# @return [String] the
|
|
382
|
+
# @return [String] the transaction hash once the transaction is mined.
|
|
409
383
|
# @raise [Timeout::Error] if it's not mined within 5 minutes.
|
|
410
384
|
def wait_for_tx(hash)
|
|
411
385
|
start_time = Time.now
|
|
@@ -429,6 +403,59 @@ module Eth
|
|
|
429
403
|
|
|
430
404
|
private
|
|
431
405
|
|
|
406
|
+
# Non-transactional function call called from call().
|
|
407
|
+
def call_raw(contract, func, *args, **kwargs)
|
|
408
|
+
gas_limit = if kwargs[:gas_limit]
|
|
409
|
+
kwargs[:gas_limit]
|
|
410
|
+
else
|
|
411
|
+
Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
|
|
412
|
+
end
|
|
413
|
+
params = {
|
|
414
|
+
gas_limit: gas_limit,
|
|
415
|
+
chain_id: chain_id,
|
|
416
|
+
data: call_payload(func, args),
|
|
417
|
+
}
|
|
418
|
+
if kwargs[:address] || contract.address
|
|
419
|
+
params.merge!({ to: kwargs[:address] || contract.address })
|
|
420
|
+
end
|
|
421
|
+
if kwargs[:legacy]
|
|
422
|
+
params.merge!({
|
|
423
|
+
gas_price: max_fee_per_gas,
|
|
424
|
+
})
|
|
425
|
+
else
|
|
426
|
+
params.merge!({
|
|
427
|
+
priority_fee: max_priority_fee_per_gas,
|
|
428
|
+
max_gas_fee: max_fee_per_gas,
|
|
429
|
+
})
|
|
430
|
+
end
|
|
431
|
+
unless kwargs[:sender_key].nil?
|
|
432
|
+
# Uses the provided key as sender and signer
|
|
433
|
+
params.merge!({
|
|
434
|
+
from: kwargs[:sender_key].address,
|
|
435
|
+
nonce: get_nonce(kwargs[:sender_key].address),
|
|
436
|
+
})
|
|
437
|
+
tx = Eth::Tx.new(params)
|
|
438
|
+
tx.sign kwargs[:sender_key]
|
|
439
|
+
end
|
|
440
|
+
raw_result = eth_call(params)["result"]
|
|
441
|
+
types = func.outputs.map { |i| i.type }
|
|
442
|
+
return nil if raw_result == "0x"
|
|
443
|
+
Eth::Abi.decode(types, raw_result)
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# Encodes function call payloads.
|
|
447
|
+
def call_payload(fun, args)
|
|
448
|
+
types = fun.inputs.map { |i| i.type }
|
|
449
|
+
encoded_str = Util.bin_to_hex(Eth::Abi.encode(types, args))
|
|
450
|
+
"0x" + fun.signature + (encoded_str.empty? ? "0" * 64 : encoded_str)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
# Encodes constructor params
|
|
454
|
+
def encode_constructor_params(contract, args)
|
|
455
|
+
types = contract.constructor_inputs.map { |input| input.type }
|
|
456
|
+
Util.bin_to_hex(Eth::Abi.encode(types, args))
|
|
457
|
+
end
|
|
458
|
+
|
|
432
459
|
# Prepares parameters and sends the command to the client.
|
|
433
460
|
def send_command(command, args)
|
|
434
461
|
args << "latest" if ["eth_getBalance", "eth_call"].include? command
|
|
@@ -466,3 +493,7 @@ module Eth
|
|
|
466
493
|
end
|
|
467
494
|
end
|
|
468
495
|
end
|
|
496
|
+
|
|
497
|
+
# Load the client/* libraries
|
|
498
|
+
require "eth/client/http"
|
|
499
|
+
require "eth/client/ipc"
|
data/lib/eth/contract/event.rb
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
# Provides the {Eth} module.
|
|
18
18
|
module Eth
|
|
19
|
+
|
|
19
20
|
# Provides the methods for smart contract function.
|
|
20
21
|
class Contract::Function
|
|
21
22
|
attr_accessor :name, :inputs, :outputs, :signature, :constant, :function_string
|
|
@@ -36,21 +37,21 @@ module Eth
|
|
|
36
37
|
@signature = self.class.encoded_function_signature(@function_string)
|
|
37
38
|
end
|
|
38
39
|
|
|
39
|
-
#
|
|
40
|
+
# Creates function strings.
|
|
40
41
|
#
|
|
41
42
|
# @param name [String] function name.
|
|
42
43
|
# @param inputs [Array<Eth::Contract::FunctionInput>] function input class list.
|
|
43
44
|
# @return [String] function string.
|
|
44
45
|
def self.calc_signature(name, inputs)
|
|
45
|
-
"#{name}(#{inputs.collect { |x| x.
|
|
46
|
+
"#{name}(#{inputs.collect { |x| x.raw_type }.join(",")})"
|
|
46
47
|
end
|
|
47
48
|
|
|
48
|
-
#
|
|
49
|
+
# Encodes a function signature.
|
|
49
50
|
#
|
|
50
51
|
# @param signature [String] function signature.
|
|
51
52
|
# @return [String] encoded function signature string.
|
|
52
53
|
def self.encoded_function_signature(signature)
|
|
53
|
-
|
|
54
|
+
Util.bin_to_hex Util.keccak256(signature)[0..3]
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
57
|
end
|
|
@@ -16,19 +16,21 @@
|
|
|
16
16
|
|
|
17
17
|
# Provides the {Eth} module.
|
|
18
18
|
module Eth
|
|
19
|
+
|
|
19
20
|
# Provide classes for contract function input.
|
|
20
21
|
class Contract::FunctionInput
|
|
21
|
-
attr_accessor :type, :name
|
|
22
|
+
attr_accessor :type, :raw_type, :name
|
|
22
23
|
|
|
23
24
|
# Constructor of the {Eth::Contract::FunctionInput} class.
|
|
24
25
|
#
|
|
25
26
|
# @param data [Hash] contract abi data.
|
|
26
27
|
def initialize(data)
|
|
28
|
+
@raw_type = data["type"]
|
|
27
29
|
@type = Eth::Abi::Type.parse(data["type"])
|
|
28
30
|
@name = data["name"]
|
|
29
31
|
end
|
|
30
32
|
|
|
31
|
-
# Returns types
|
|
33
|
+
# Returns complete types with subtypes, e.g., `uint256`.
|
|
32
34
|
def type
|
|
33
35
|
@type.base_type + @type.sub_type
|
|
34
36
|
end
|
|
@@ -16,15 +16,20 @@
|
|
|
16
16
|
|
|
17
17
|
# Provides the {Eth} module.
|
|
18
18
|
module Eth
|
|
19
|
+
|
|
19
20
|
# Provide classes for contract function output.
|
|
20
21
|
class Contract::FunctionOutput
|
|
21
22
|
attr_accessor :type, :name
|
|
22
23
|
|
|
24
|
+
# Constructor of the {Eth::Contract::FunctionOutput} class.
|
|
25
|
+
#
|
|
26
|
+
# @param data [Hash] contract abi data.
|
|
23
27
|
def initialize(data)
|
|
24
28
|
@type = Eth::Abi::Type.parse(data["type"])
|
|
25
29
|
@name = data["name"]
|
|
26
30
|
end
|
|
27
31
|
|
|
32
|
+
# Returns complete types with subtypes, e.g., `uint256`.
|
|
28
33
|
def type
|
|
29
34
|
@type.base_type + @type.sub_type
|
|
30
35
|
end
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
# Provides the {Eth} module.
|
|
18
18
|
module Eth
|
|
19
|
+
|
|
19
20
|
# Provide classes for contract initializer.
|
|
20
21
|
class Contract::Initializer
|
|
21
22
|
attr_accessor :contracts, :file
|
|
@@ -36,7 +37,7 @@ module Eth
|
|
|
36
37
|
end
|
|
37
38
|
end
|
|
38
39
|
|
|
39
|
-
#
|
|
40
|
+
# Builds and returns all contracts.
|
|
40
41
|
def build_all
|
|
41
42
|
@contracts.each do |contract|
|
|
42
43
|
contract.build
|
data/lib/eth/contract.rb
CHANGED
|
@@ -37,51 +37,66 @@ module Eth
|
|
|
37
37
|
@constructor_inputs, @functions, @events = parse_abi(abi)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
# Creates a contract wrapper.
|
|
40
|
+
# Creates a contract wrapper from a Solidity file.
|
|
41
41
|
#
|
|
42
42
|
# @param file [String] solidity file path.
|
|
43
|
-
# @param
|
|
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
|
+
#
|
|
44
55
|
# @param abi [String] contract abi string.
|
|
45
56
|
# @param address [String] contract address.
|
|
46
57
|
# @param name [String] name of contract.
|
|
47
|
-
# @param contract_index [Number] specify contract.
|
|
48
58
|
# @return [Eth::Contract::Object] Returns the class of the smart contract.
|
|
49
59
|
# @raise [JSON::ParserError] if the json format is wrong.
|
|
50
|
-
# @raise [ArgumentError] if
|
|
51
|
-
def self.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
contract = contracts[contract_index].class_object.new
|
|
57
|
-
else
|
|
58
|
-
contract = contracts.first.class_object.new
|
|
59
|
-
end
|
|
60
|
-
elsif ![name, bin, abi].include? nil
|
|
61
|
-
begin
|
|
62
|
-
abi = abi.is_a?(Array) ? abi : JSON.parse(abi)
|
|
63
|
-
rescue JSON::ParserError => e
|
|
64
|
-
raise e
|
|
65
|
-
end
|
|
66
|
-
contract = Eth::Contract.new(name, bin, abi)
|
|
67
|
-
contract.build
|
|
68
|
-
contract = contract.class_object.new
|
|
69
|
-
else
|
|
70
|
-
raise ArgumentError, "The argument is incorrect."
|
|
71
|
-
end
|
|
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
|
|
72
66
|
contract.address = address
|
|
73
67
|
contract
|
|
74
68
|
end
|
|
75
69
|
|
|
76
|
-
#
|
|
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.
|
|
77
88
|
def address=(addr)
|
|
78
|
-
|
|
89
|
+
if addr.is_a? Eth::Address
|
|
90
|
+
@address = addr.to_s
|
|
91
|
+
else
|
|
92
|
+
@address = Eth::Address.new(addr).to_s
|
|
93
|
+
end
|
|
79
94
|
@events.each do |event|
|
|
80
95
|
event.set_address(@address)
|
|
81
96
|
end
|
|
82
97
|
end
|
|
83
98
|
|
|
84
|
-
# Create classes for smart contracts
|
|
99
|
+
# Create meta classes for smart contracts.
|
|
85
100
|
def build
|
|
86
101
|
class_name = @name
|
|
87
102
|
parent = self
|
|
@@ -94,6 +109,7 @@ module Eth
|
|
|
94
109
|
def_delegators :parent, :events
|
|
95
110
|
def_delegators :parent, :address, :address=
|
|
96
111
|
def_delegator :parent, :functions
|
|
112
|
+
def_delegator :parent, :constructor_inputs
|
|
97
113
|
define_method :parent do
|
|
98
114
|
parent
|
|
99
115
|
end
|
|
@@ -118,3 +134,10 @@ module Eth
|
|
|
118
134
|
end
|
|
119
135
|
end
|
|
120
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"
|
data/lib/eth/rlp/sedes/binary.rb
CHANGED
|
@@ -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
|
|
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
|
|
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/signature.rb
CHANGED
|
@@ -38,7 +38,7 @@ module Eth
|
|
|
38
38
|
# @param message [String] the message string to be prefixed.
|
|
39
39
|
# @return [String] an EIP-191 prefixed string.
|
|
40
40
|
def prefix_message(message)
|
|
41
|
-
"#{EIP191_PREFIX_BYTE}Ethereum Signed Message:\n#{message.
|
|
41
|
+
"#{EIP191_PREFIX_BYTE}Ethereum Signed Message:\n#{message.bytesize}#{message}"
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
# Dissects a signature blob of 65+ bytes into its `r`, `s`, and `v`
|
data/lib/eth/tx.rb
CHANGED
|
@@ -78,7 +78,7 @@ module Eth
|
|
|
78
78
|
# value, data, access_list)
|
|
79
79
|
# - EIP-2930: chain_id, nonce, gas_price, gas_limit, access_list(, from, to,
|
|
80
80
|
# value, data)
|
|
81
|
-
# - Legacy: nonce, gas_price,
|
|
81
|
+
# - Legacy: nonce, gas_price, gas_limit(, from, to, value, data)
|
|
82
82
|
#
|
|
83
83
|
# @param params [Hash] all necessary transaction fields.
|
|
84
84
|
# @param chain_id [Integer] the EIP-155 Chain ID (legacy transactions only).
|
data/lib/eth/version.rb
CHANGED
data/lib/eth.rb
CHANGED
|
@@ -23,14 +23,7 @@ require "eth/address"
|
|
|
23
23
|
require "eth/chain"
|
|
24
24
|
require "eth/constant"
|
|
25
25
|
require "eth/contract"
|
|
26
|
-
require "eth/contract/event"
|
|
27
|
-
require "eth/contract/function"
|
|
28
|
-
require "eth/contract/function_input"
|
|
29
|
-
require "eth/contract/function_output"
|
|
30
|
-
require "eth/contract/initializer"
|
|
31
26
|
require "eth/client"
|
|
32
|
-
require "eth/client/http"
|
|
33
|
-
require "eth/client/ipc"
|
|
34
27
|
require "eth/eip712"
|
|
35
28
|
require "eth/key"
|
|
36
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.
|
|
4
|
+
version: 0.5.6
|
|
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-
|
|
12
|
+
date: 2022-07-01 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"
|
|
@@ -170,7 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
170
177
|
- !ruby/object:Gem::Version
|
|
171
178
|
version: '0'
|
|
172
179
|
requirements: []
|
|
173
|
-
rubygems_version: 3.3.
|
|
180
|
+
rubygems_version: 3.3.15
|
|
174
181
|
signing_key:
|
|
175
182
|
specification_version: 4
|
|
176
183
|
summary: Ruby Ethereum library.
|