eth 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/spec.yml +1 -1
- data/CHANGELOG.md +29 -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 +2 -2
- data/lib/eth/chain.rb +1 -1
- data/lib/eth/client.rb +75 -72
- data/lib/eth/contract.rb +43 -28
- data/lib/eth/rlp/sedes/binary.rb +2 -2
- data/lib/eth/tx.rb +1 -1
- data/lib/eth/version.rb +1 -1
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af4a4068c445fcbb0ec94375ffdf650894464bf62d69dd7a14af232452c3af3c
|
4
|
+
data.tar.gz: a962fe008b6ecb75bdc5a70c9363aa31f95d84f82359da53e7195544bbcb9db2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d909d4249f0f7a72cae28c0f4208e1aa8c8ba69779149a20ececf4a53cd6186b43062d7a9aa5921e3c3fee9f3e014df8e6dd01586c0651f714d9bca47a4953da
|
7
|
+
data.tar.gz: ad74d13143142ab3cf71cadfa79cbd1a7a9cf2628c0b5b6e279edd4385b20764b837e14b0f8f7f24d55884893a7b79b4c1323f89aca66adfa40eb6af8bd4db41
|
data/.github/workflows/spec.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,20 +1,47 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
+
## [0.5.3]
|
5
|
+
### Added
|
6
|
+
- Smart contract support ([#68](https://github.com/q9f/eth.rb/pull/68))
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
- Eth/abi: decode event log ([#69](https://github.com/q9f/eth.rb/pull/69))
|
10
|
+
- Gem: bump version ([#70](https://github.com/q9f/eth.rb/pull/70))
|
11
|
+
- Eth/abi/event: batch log decoder ([#71](https://github.com/q9f/eth.rb/pull/71))
|
12
|
+
|
13
|
+
## [0.5.2]
|
14
|
+
### Added
|
15
|
+
- Eth/solidity: add solidity compiler bindings ([#66](https://github.com/q9f/eth.rb/pull/66))
|
16
|
+
|
17
|
+
### Changed
|
18
|
+
- Eth: remove duplicated code ([#62](https://github.com/q9f/eth.rb/pull/62))
|
19
|
+
- Ci: allow coverage to drop to 99% without failing ([#63](https://github.com/q9f/eth.rb/pull/63))
|
20
|
+
- Docs: update readme ([#64](https://github.com/q9f/eth.rb/pull/64))
|
21
|
+
- Docs: add wiki to readme ([#65](https://github.com/q9f/eth.rb/pull/65))
|
22
|
+
|
4
23
|
## [0.5.1]
|
24
|
+
### Added
|
25
|
+
- Add eth::rlp module ([#52](https://github.com/q9f/eth.rb/pull/52))
|
26
|
+
- Eth/client: implement http/ipc ([#37](https://github.com/q9f/eth.rb/pull/37))
|
27
|
+
|
5
28
|
### Changed
|
6
29
|
- Docs: update changelog ([#61](https://github.com/q9f/eth.rb/pull/61))
|
7
30
|
- Eth/chain: add sepolia chain id; docs ([#60](https://github.com/q9f/eth.rb/pull/60))
|
8
31
|
- 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
32
|
- Eth/tx: properly serialize signatures ([#58](https://github.com/q9f/eth.rb/pull/58))
|
11
33
|
- Eth/client: fix legacy transfer ([#57](https://github.com/q9f/eth.rb/pull/57))
|
12
34
|
- 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
35
|
- Docs: update changelog ([#53](https://github.com/q9f/eth.rb/pull/53))
|
15
36
|
- Spec: add upstream test fixtures for keystore ([#50](https://github.com/q9f/eth.rb/pull/50))
|
16
37
|
|
17
38
|
## [0.5.0]
|
39
|
+
### Added
|
40
|
+
- Eth/tx: create legacy, type-1, and type-2 transactions [#33](https://github.com/q9f/eth.rb/pull/33)
|
41
|
+
- Signature: implement eip 712 typed structured data signing [#27](https://github.com/q9f/eth.rb/pull/27)
|
42
|
+
- Lib: import ABI to eth/abi [#29](https://github.com/q9f/eth.rb/pull/29)
|
43
|
+
- Eth/chains: implement eip 155 for replay protection [#20](https://github.com/q9f/eth.rb/pull/20)
|
44
|
+
|
18
45
|
### Changed
|
19
46
|
- Docs: update readme with features [#49](https://github.com/q9f/eth.rb/pull/49)
|
20
47
|
- Eth/tx: add method to estimate intrinsic gas costs [#48](https://github.com/q9f/eth.rb/pull/48)
|
@@ -26,16 +53,12 @@ All notable changes to this project will be documented in this file.
|
|
26
53
|
- Lib: improve error handling [#39](https://github.com/q9f/eth.rb/pull/39)
|
27
54
|
- Docs: update readme for tx and keys [#40](https://github.com/q9f/eth.rb/pull/40)
|
28
55
|
- 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
56
|
- 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
57
|
- Rename util and chain to singular [#26](https://github.com/q9f/eth.rb/pull/26)
|
34
58
|
- Docs: add some examples to readme [#25](https://github.com/q9f/eth.rb/pull/25)
|
35
59
|
- Key/signature: personal sign and verify [#24](https://github.com/q9f/eth.rb/pull/24)
|
36
60
|
- Ci: only run coverage on CI [#23](https://github.com/q9f/eth.rb/pull/23)
|
37
61
|
- 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
62
|
- Eth/util: public_key_to_address should return an eth::address [#19](https://github.com/q9f/eth.rb/pull/19)
|
40
63
|
- Ci: add docs workflow [#18](https://github.com/q9f/eth.rb/pull/18)
|
41
64
|
- 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"
|
data/lib/eth/chain.rb
CHANGED
@@ -136,7 +136,7 @@ module Eth
|
|
136
136
|
return v
|
137
137
|
end
|
138
138
|
|
139
|
-
#
|
139
|
+
# Converts a `v` value into a chain ID. This does not work for legacy signatures
|
140
140
|
# with `v < 36` that do not conform with EIP-155.
|
141
141
|
#
|
142
142
|
# @param v [Integer] the signature's `v` value.
|
data/lib/eth/client.rb
CHANGED
@@ -149,9 +149,8 @@ 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.
|
@@ -165,10 +164,11 @@ module Eth
|
|
165
164
|
# @return [String] the contract address.
|
166
165
|
def deploy_and_wait(contract, sender_key: nil, legacy: false)
|
167
166
|
hash = wait_for_tx(deploy(contract, sender_key: sender_key, legacy: legacy))
|
168
|
-
|
167
|
+
addr = eth_get_transaction_receipt(hash)["result"]["contractAddress"]
|
168
|
+
contract.address = Address.new(addr).to_s
|
169
169
|
end
|
170
170
|
|
171
|
-
#
|
171
|
+
# Deploys a contract. Uses `eth_coinbase` or external signer
|
172
172
|
# if no sender key is provided.
|
173
173
|
#
|
174
174
|
# @overload deploy(contract)
|
@@ -181,7 +181,9 @@ module Eth
|
|
181
181
|
# @param sender_key [Eth::Key] the sender private key.
|
182
182
|
# @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
183
183
|
# @return [String] the transaction hash.
|
184
|
+
# @raise [ArgumentError] in case the contract does not have any source.
|
184
185
|
def deploy(contract, sender_key: nil, legacy: false)
|
186
|
+
raise ArgumentError, "Cannot deploy contract without source or binary!" if contract.bin.nil?
|
185
187
|
gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
|
186
188
|
params = {
|
187
189
|
value: 0,
|
@@ -218,70 +220,8 @@ module Eth
|
|
218
220
|
end
|
219
221
|
end
|
220
222
|
|
221
|
-
#
|
222
|
-
|
223
|
-
types = fun.inputs.map { |i| i.type }
|
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
|
246
|
-
params = {
|
247
|
-
gas_limit: gas_limit,
|
248
|
-
chain_id: chain_id,
|
249
|
-
data: call_payload(func, args),
|
250
|
-
}
|
251
|
-
if kwargs[:address] || contract.address
|
252
|
-
params.merge!({ to: kwargs[:address] || contract.address })
|
253
|
-
end
|
254
|
-
if kwargs[:legacy]
|
255
|
-
params.merge!({
|
256
|
-
gas_price: max_fee_per_gas,
|
257
|
-
})
|
258
|
-
else
|
259
|
-
params.merge!({
|
260
|
-
priority_fee: max_priority_fee_per_gas,
|
261
|
-
max_gas_fee: max_fee_per_gas,
|
262
|
-
})
|
263
|
-
end
|
264
|
-
unless kwargs[:sender_key].nil?
|
265
|
-
# use the provided key as sender and signer
|
266
|
-
params.merge!({
|
267
|
-
from: kwargs[:sender_key].address,
|
268
|
-
nonce: get_nonce(kwargs[:sender_key].address),
|
269
|
-
})
|
270
|
-
tx = Eth::Tx.new(params)
|
271
|
-
tx.sign kwargs[:sender_key]
|
272
|
-
else
|
273
|
-
# use the default account as sender and external signer
|
274
|
-
params.merge!({
|
275
|
-
from: default_account,
|
276
|
-
nonce: get_nonce(default_account),
|
277
|
-
})
|
278
|
-
end
|
279
|
-
raw_result = eth_call(params)["result"]
|
280
|
-
types = func.outputs.map { |i| i.type }
|
281
|
-
Eth::Abi.decode(types, raw_result)
|
282
|
-
end
|
283
|
-
|
284
|
-
# Non-transactional function calls.
|
223
|
+
# Calls a contract function without executing it
|
224
|
+
# (non-transactional contract read).
|
285
225
|
#
|
286
226
|
# @overload call(contract, function_name)
|
287
227
|
# @param contract [Eth::Contract] subject contract to call.
|
@@ -308,7 +248,8 @@ module Eth
|
|
308
248
|
end
|
309
249
|
end
|
310
250
|
|
311
|
-
#
|
251
|
+
# Executes a contract function with a transaction (transactional
|
252
|
+
# contract read/write).
|
312
253
|
#
|
313
254
|
# @overload transact(contract, function_name)
|
314
255
|
# @param contract [Eth::Contract] subject contract to call.
|
@@ -364,7 +305,8 @@ module Eth
|
|
364
305
|
end
|
365
306
|
end
|
366
307
|
|
367
|
-
#
|
308
|
+
# Executes a contract function with a transaction and waits for it
|
309
|
+
# to be mined (transactional contract read/write).
|
368
310
|
#
|
369
311
|
# @overload transact_and_wait(contract, function_name)
|
370
312
|
# @param contract [Eth::Contract] subject contract to call.
|
@@ -385,6 +327,25 @@ module Eth
|
|
385
327
|
wait_for_tx(transact(contract, function_name, *args, **kwargs))
|
386
328
|
end
|
387
329
|
|
330
|
+
# Provides an interface to call `isValidSignature` as per EIP-1271 on a given
|
331
|
+
# smart contract to verify the given hash and signature matching the magic
|
332
|
+
# value.
|
333
|
+
#
|
334
|
+
# @param contract [Eth::Contract] a deployed contract implementing EIP-1271.
|
335
|
+
# @param hash [String] the message hash to be checked against the signature.
|
336
|
+
# @param signature [String] the signature to be recovered by the contract.
|
337
|
+
# @param magic [String] the expected magic value (defaults to `1626ba7e`).
|
338
|
+
# @return [Boolean] true if magic matches and signature is valid.
|
339
|
+
# @raise [ArgumentError] in case the contract cannot be called yet.
|
340
|
+
def is_valid_signature(contract, hash, signature, magic = "1626ba7e")
|
341
|
+
raise ArgumentError, "Contract not deployed yet." if contract.address.nil?
|
342
|
+
hash = Util.hex_to_bin hash if Util.is_hex? hash
|
343
|
+
signature = Util.hex_to_bin signature if Util.is_hex? signature
|
344
|
+
magic = Util.hex_to_bin magic if Util.is_hex? magic
|
345
|
+
result = call(contract, "isValidSignature", hash, signature)
|
346
|
+
return result === magic
|
347
|
+
end
|
348
|
+
|
388
349
|
# Gives control over resetting the RPC request ID back to zero.
|
389
350
|
# Usually not needed.
|
390
351
|
#
|
@@ -405,7 +366,7 @@ module Eth
|
|
405
366
|
# Waits for an transaction to be mined by the connected chain.
|
406
367
|
#
|
407
368
|
# @param hash [String] the transaction hash.
|
408
|
-
# @return [String] the
|
369
|
+
# @return [String] the transaction hash once the transaction is mined.
|
409
370
|
# @raise [Timeout::Error] if it's not mined within 5 minutes.
|
410
371
|
def wait_for_tx(hash)
|
411
372
|
start_time = Time.now
|
@@ -429,6 +390,48 @@ module Eth
|
|
429
390
|
|
430
391
|
private
|
431
392
|
|
393
|
+
# Non-transactional function call called from call().
|
394
|
+
def call_raw(contract, func, *args, **kwargs)
|
395
|
+
gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
|
396
|
+
params = {
|
397
|
+
gas_limit: gas_limit,
|
398
|
+
chain_id: chain_id,
|
399
|
+
data: call_payload(func, args),
|
400
|
+
}
|
401
|
+
if kwargs[:address] || contract.address
|
402
|
+
params.merge!({ to: kwargs[:address] || contract.address })
|
403
|
+
end
|
404
|
+
if kwargs[:legacy]
|
405
|
+
params.merge!({
|
406
|
+
gas_price: max_fee_per_gas,
|
407
|
+
})
|
408
|
+
else
|
409
|
+
params.merge!({
|
410
|
+
priority_fee: max_priority_fee_per_gas,
|
411
|
+
max_gas_fee: max_fee_per_gas,
|
412
|
+
})
|
413
|
+
end
|
414
|
+
unless kwargs[:sender_key].nil?
|
415
|
+
# use the provided key as sender and signer
|
416
|
+
params.merge!({
|
417
|
+
from: kwargs[:sender_key].address,
|
418
|
+
nonce: get_nonce(kwargs[:sender_key].address),
|
419
|
+
})
|
420
|
+
tx = Eth::Tx.new(params)
|
421
|
+
tx.sign kwargs[:sender_key]
|
422
|
+
end
|
423
|
+
raw_result = eth_call(params)["result"]
|
424
|
+
types = func.outputs.map { |i| i.type }
|
425
|
+
Eth::Abi.decode(types, raw_result)
|
426
|
+
end
|
427
|
+
|
428
|
+
# Encodes function call payloads.
|
429
|
+
def call_payload(fun, args)
|
430
|
+
types = fun.inputs.map { |i| i.type }
|
431
|
+
encoded_str = Util.bin_to_hex(Eth::Abi.encode(types, args))
|
432
|
+
"0x" + fun.signature + (encoded_str.empty? ? "0" * 64 : encoded_str)
|
433
|
+
end
|
434
|
+
|
432
435
|
# Prepares parameters and sends the command to the client.
|
433
436
|
def send_command(command, args)
|
434
437
|
args << "latest" if ["eth_getBalance", "eth_call"].include? command
|
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
|
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/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
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.4
|
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-05-
|
12
|
+
date: 2022-05-16 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
|