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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0375271f13b257337cbc0ab138c2cdf972de8f8e03cf60bb9b672b2221ac988d
4
- data.tar.gz: 19e85a6be34904b5544d9c6845ae19231616d65f27a8146c747f66068f2c23db
3
+ metadata.gz: af4a4068c445fcbb0ec94375ffdf650894464bf62d69dd7a14af232452c3af3c
4
+ data.tar.gz: a962fe008b6ecb75bdc5a70c9363aa31f95d84f82359da53e7195544bbcb9db2
5
5
  SHA512:
6
- metadata.gz: d5cc31684e8dfbff08b7250436a0bd4a94fa90942fd27b03292806cf2ac190673f1e507d8bcabd80b968a7206401a5210298c75c1ecc7c1d94765a7e94654d0d
7
- data.tar.gz: 11b5a667d5cb333848aee384f4276fb3982a69104ae43c9650aec572ec3d093601c44b1b33167029b3ea37bcba6d8fd46da795ada67f6ad50fc39550fd092434
6
+ metadata.gz: d909d4249f0f7a72cae28c0f4208e1aa8c8ba69779149a20ececf4a53cd6186b43062d7a9aa5921e3c3fee9f3e014df8e6dd01586c0651f714d9bca47a4953da
7
+ data.tar.gz: ad74d13143142ab3cf71cadfa79cbd1a7a9cf2628c0b5b6e279edd4385b20764b837e14b0f8f7f24d55884893a7b79b4c1323f89aca66adfa40eb6af8bd4db41
@@ -19,7 +19,7 @@ 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
24
  - uses: actions/checkout@v2
25
25
  - uses: ruby/setup-ruby@v1
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
- Functions to interact with smart contract.
256
+ Create, compile, and deploy smart contracts.
260
257
 
261
258
  ```ruby
262
- contract = Eth::Contract.create(file: 'spec/fixtures/contracts/dummy.sol')
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", "~> 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"
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 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 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 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
@@ -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 Applicatoin Binary Interface (ABI).
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
- # prase all types
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
- # Converst a `v` value into a chain ID. This does not work for legacy signatures
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
- # Deploy contract and waits for it to be mined.
153
- # Uses `eth_coinbase` or external signer
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
- contract.address = eth_get_transaction_receipt(hash)["result"]["contractAddress"]
167
+ addr = eth_get_transaction_receipt(hash)["result"]["contractAddress"]
168
+ contract.address = Address.new(addr).to_s
169
169
  end
170
170
 
171
- # Deploy contract. Uses `eth_coinbase` or external signer
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
- # Encoding for function calls.
222
- def call_payload(fun, args)
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
- # Function call with transaction.
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
- # Function call with transaction and waits for it to be mined.
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 transactin hash once the transaction is mined.
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 bin [String] contract bin string.
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 argument is incorrect.
51
- def self.create(file: nil, bin: nil, abi: nil, address: nil, name: nil, contract_index: nil)
52
- if File.exist?(file.to_s)
53
- contracts = Eth::Contract::Initializer.new(file).build_all
54
- raise "No contracts compiled" if contracts.empty?
55
- if contract_index
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
- # Set the address of the smart contract
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
- @address = addr.nil? ? nil : Eth::Address.new(addr).address
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
@@ -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
@@ -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, gas_lmit(, from, to, value, data)
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
@@ -16,5 +16,5 @@
16
16
  module Eth
17
17
 
18
18
  # Defines the version of the {Eth} module.
19
- VERSION = "0.5.3".freeze
19
+ VERSION = "0.5.4".freeze
20
20
  end
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.3
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-06 00:00:00.000000000 Z
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