eth 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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