eth 0.5.5 → 0.5.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/docs.yml +1 -1
- data/CHANGELOG.md +30 -0
- data/README.md +23 -259
- data/lib/eth/abi.rb +22 -4
- data/lib/eth/chain.rb +9 -0
- data/lib/eth/client/http_auth.rb +73 -0
- data/lib/eth/client.rb +98 -79
- data/lib/eth/contract/function.rb +1 -1
- data/lib/eth/contract/function_input.rb +3 -2
- data/lib/eth/contract/function_output.rb +1 -1
- data/lib/eth/signature.rb +5 -2
- data/lib/eth/tx/eip1559.rb +1 -1
- data/lib/eth/tx/eip2930.rb +1 -1
- data/lib/eth/tx/legacy.rb +1 -1
- data/lib/eth/tx.rb +6 -3
- data/lib/eth/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86b3022f82e96aed2d1aad682ad106f3f5efe9488e03ed048e0c5862d30c3284
|
4
|
+
data.tar.gz: 40cb5224db9e074360586aca07f6421edc47668fa0f5d81728fa7574f0c2a978
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf5872ec79f0c708a5c05060514b9179279db310b1c0c0682679cd90bcd0998f9ef1a7f8787c298380a252462c5ea33fdc3d44c0bdb5e3c57991515514077b85
|
7
|
+
data.tar.gz: 4a7adde0050a724270f5779a2fe4fca596202a930ee7e1af1e4493878a21e7102ee75a2d5ce91ac455b016c9c396d6771396e5c890f5dcef7ea220b0eadd2d58
|
data/.github/workflows/docs.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,36 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
+
## [0.5.6]
|
5
|
+
### Added
|
6
|
+
- Eth/client: Add gas limit override option for contract deployments ([#128](https://github.com/q9f/eth.rb/pull/128))
|
7
|
+
- Eth/abi: support dynamic array encoding ([#122](https://github.com/q9f/eth.rb/pull/122))
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- Eth/client: Include contract constructor args when estimating intrinsic gas ([#111](https://github.com/q9f/eth.rb/pull/111))
|
11
|
+
- Eth/abi: allow parsing numerics from string inputs ([#112](https://github.com/q9f/eth.rb/pull/112))
|
12
|
+
- Eth/signature: fix prefix_message for multibyte characters ([#120](https://github.com/q9f/eth.rb/pull/120))
|
13
|
+
- Eth/abi: raise error if numeric comes as string ([#114](https://github.com/q9f/eth.rb/pull/114))
|
14
|
+
- Gem: bump version to 0.5.6 ([#130](https://github.com/q9f/eth.rb/pull/130))
|
15
|
+
|
16
|
+
## [0.5.5]
|
17
|
+
### Added
|
18
|
+
- Eth/contract: Add missing def_delegator for constructor_inputs ([#96](https://github.com/q9f/eth.rb/pull/96))
|
19
|
+
- Eth/client: Enable passing in constructor params to deploy ([#106](https://github.com/q9f/eth.rb/pull/106))
|
20
|
+
- Eth/chain: add matic/mumbai ([#107](https://github.com/q9f/eth.rb/pull/107))
|
21
|
+
|
22
|
+
### Changed
|
23
|
+
- Gem: bump version to 0.5.5 ([#89](https://github.com/q9f/eth.rb/pull/89))
|
24
|
+
- Docs: update changelog for 0.5.4 ([#90](https://github.com/q9f/eth.rb/pull/90))
|
25
|
+
- Ci: add weekly dependency checks ([#91](https://github.com/q9f/eth.rb/pull/91))
|
26
|
+
- Build(deps): bump github/codeql-action from 1 to 2 ([#92](https://github.com/q9f/eth.rb/pull/92))
|
27
|
+
- Build(deps): bump actions/checkout from 2 to 3 ([#93](https://github.com/q9f/eth.rb/pull/93))
|
28
|
+
- Build(deps): bump JamesIves/github-pages-deploy-action from 4.1.7 to 4.3.3 ([#94](https://github.com/q9f/eth.rb/pull/94))
|
29
|
+
- Eth/abi: fix handling of hex values for byte strings ([#100](https://github.com/q9f/eth.rb/pull/100))
|
30
|
+
- Eth/abi: add a testcase for handling hex and bin strings ([#101](https://github.com/q9f/eth.rb/pull/101))
|
31
|
+
- Eth/abi: Fix Eth::Abi::DecodingError in call method ([#105](https://github.com/q9f/eth.rb/pull/105))
|
32
|
+
- Eth: some docs and cleanups ([#108](https://github.com/q9f/eth.rb/pull/108))
|
33
|
+
|
4
34
|
## [0.5.4]
|
5
35
|
### Added
|
6
36
|
- Eth/client: method for eip-1271 ([#80](https://github.com/q9f/eth.rb/pull/80))
|
data/README.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
<!--
|
2
|
+
# @markup markdown
|
3
|
+
# @title Ethereum for Ruby
|
4
|
+
# @author Afri Schoedon
|
5
|
+
-->
|
6
|
+
|
1
7
|
# Ethereum for Ruby
|
2
8
|
|
3
9
|
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/q9f/eth.rb/Spec)](https://github.com/q9f/eth.rb/actions)
|
@@ -30,26 +36,9 @@ What you get:
|
|
30
36
|
- [x] RLP-Encoder and Decoder (including sedes)
|
31
37
|
- [x] RPC-Client (IPC/HTTP) for Execution-Layer APIs
|
32
38
|
- [x] Solidity bindings (compile contracts from Ruby)
|
33
|
-
- [x] Full smart-contract support (deploy, transact, and call)
|
34
|
-
|
35
|
-
Contents:
|
36
|
-
- [1. Installation](#1-installation)
|
37
|
-
- [2. Usage](#2-usage)
|
38
|
-
- [2.1. Ethereum Keys and Addresses (EIP-55)](#21-ethereum-keys-and-addresses-eip-55)
|
39
|
-
- [2.2. Ethereum Signatures (EIP-191, EIP-712)](#22-ethereum-signatures-eip-191-eip-712)
|
40
|
-
- [2.3. Ethereum Chains (EIP-155)](#23-ethereum-chains-eip-155)
|
41
|
-
- [2.4. Ethereum Transactions (EIP-1559, EIP-2718, EIP-2930)](#24-ethereum-transactions-eip-1559-eip-2718-eip-2930)
|
42
|
-
- [2.5. Ethereum ABI Encoder and Decoder](#25-ethereum-abi-encoder-and-decoder)
|
43
|
-
- [2.6. Ethereum RLP Encoder and Decoder](#26-ethereum-rlp-encoder-and-decoder)
|
44
|
-
- [2.7. Ethereum RPC-Client](#27-ethereum-rpc-client)
|
45
|
-
- [2.8. Solidity Compiler Bindings](#28-solidity-compiler-bindings)
|
46
|
-
- [2.9. Interact with Smart Contract](#29-interact-with-smart-contract)
|
47
|
-
- [3. Documentation](#3-documentation)
|
48
|
-
- [4. Testing](#4-testing)
|
49
|
-
- [5. Contributing](#5-contributing)
|
50
|
-
- [6. License and Credits](#6-license-and-credits)
|
39
|
+
- [x] ~~Full~~ Some smart-contract support (deploy, transact, and call)
|
51
40
|
|
52
|
-
##
|
41
|
+
## Installation
|
53
42
|
Add this line to your application's Gemfile:
|
54
43
|
|
55
44
|
```ruby
|
@@ -62,252 +51,27 @@ Or install it yourself as:
|
|
62
51
|
gem install eth
|
63
52
|
```
|
64
53
|
|
65
|
-
##
|
66
|
-
Check out
|
67
|
-
[![Yard
|
68
|
-
and
|
54
|
+
## Usage
|
55
|
+
Check out the
|
56
|
+
[![Yard API Docs](https://img.shields.io/badge/documentation-API-blue)](https://q9f.github.io/eth.rb)
|
57
|
+
and the
|
69
58
|
[![Usage Wiki](https://img.shields.io/badge/usage-WIKI-blue)](https://github.com/q9f/eth.rb/wiki)
|
70
|
-
for
|
71
|
-
|
72
|
-
### 2.1. Ethereum Keys and Addresses (EIP-55)
|
73
|
-
Generate a random Secp256k1 key-pair.
|
74
|
-
|
75
|
-
```ruby
|
76
|
-
key = Eth::Key.new
|
77
|
-
# => #<Eth::Key:0x00005574a6ba80b8 @private_key=#<Secp256k1::PrivateKey:0x00005574a6b9a0a8 @data=")&\x86P\xB5\x16\xD9]\xFA;\x1F\xF6\xD9\xCF\xE3Vj/\xE2\x81\xC0\x9D\xE9\x05o!q\x82G\x9A\x10Q">, @public_key=#<Secp256k1::PublicKey:0x00005574a6b9bf98>>
|
78
|
-
```
|
79
|
-
|
80
|
-
Create an password-encrypted Ethereum key-store.
|
81
|
-
|
82
|
-
```ruby
|
83
|
-
my_key = Eth::Key.new priv: "30137644b564785d01420f8043f043d74dcca64008e57c59f8ce713a0005a54b"
|
84
|
-
key_store = Eth::Key::Encrypter.perform my_key, "secret-password-1337"
|
85
|
-
# => "{\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"7e5c0fe1e27f4ea61b0f4427dd63555f\"},\"ciphertext\":\"6353653bba494cdae6bcd510febc980cdc6f7b23cfbdf950d7a909a69625c8fd\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":262144,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"cce96286f3c32267fc91f756365307fe6a4c83b6b2a73c69535f721fa407736c\"},\"mac\":\"3361ffd2b158a1d7bca5a5fd86a251ba3e9d80b602c867a2e0f47023a0e17a57\"},\"id\":\"642ee9fc-72e4-4d0a-902f-247c0b59bfda\",\"version\":3}"
|
86
|
-
restored_key = Eth::Key::Decrypter.perform key_store, "secret-password-1337"
|
87
|
-
# => "30137644b564785d01420f8043f043d74dcca64008e57c59f8ce713a0005a54b"
|
88
|
-
```
|
89
|
-
|
90
|
-
Manage Ethereum address objects adhering to EIP-55 checksum format.
|
91
|
-
|
92
|
-
```ruby
|
93
|
-
address = Eth::Address.new "0xd496b23d61f88a8c7758fca7560dcfac7b3b01f9"
|
94
|
-
# => #<Eth::Address:0x00005574a6bd4fc8 @address="0xd496b23d61f88a8c7758fca7560dcfac7b3b01f9">
|
95
|
-
address.valid?
|
96
|
-
# => true
|
97
|
-
address.checksummed # EIP 55
|
98
|
-
# => "0xD496b23D61F88A8C7758fca7560dCFac7b3b01F9"
|
99
|
-
```
|
100
|
-
|
101
|
-
See `/spec` or [Documentation](https://q9f.github.io/eth.rb/) for more details about key-pairs, encrypting/decrypting key-stores with a secret, and checksummed addresses.
|
102
|
-
|
103
|
-
### 2.2. Ethereum Signatures (EIP-191, EIP-712)
|
104
|
-
Manage keypairs to sign messages in EIP-191 (`personal_sign`) format or typed data in EIP-712 (`sign_typed_data`) format.
|
105
|
-
|
106
|
-
```ruby
|
107
|
-
key = Eth::Key.new priv: "268be6f4a68c40f6862b7ac9aed8f701dc25a95ddb9a44d8b1f520b75f440a9a"
|
108
|
-
# => #<Eth::Key:0x00005574a699adc0 @private_key=#<Secp256k1::PrivateKey:0x00005574a6998200 @data="&\x8B\xE6\xF4\xA6\x8C@\xF6\x86+z\xC9\xAE\xD8\xF7\x01\xDC%\xA9]\xDB\x9AD\xD8\xB1\xF5 \xB7_D\n\x9A">, @public_key=#<Secp256k1::PublicKey:0x00005574a6998160>>
|
109
|
-
key.public_hex
|
110
|
-
# => "04b45200621c013a5fbab999ac33b0c836328a04afa0255ffbe6ea0f6fd97e187b02199886d942a9f50f7e279a2bc74c93b2afcbd7255489939f9b36a5eae5e281"
|
111
|
-
key.address.to_s
|
112
|
-
# => "0xD496b23D61F88A8C7758fca7560dCFac7b3b01F9"
|
113
|
-
key.personal_sign "Hello World!"
|
114
|
-
# => "ac6a59417d8688c8144f01a662384fa691636b48a071d4b7c13902bb87ca472b0bce1d7a758f39a5759ed5e937ce61f50dd1b83158371f8d0faeb9b7d81c194225"
|
115
|
-
```
|
116
|
-
|
117
|
-
Recover and verify personal signatures respecting EIPs 155, 191, and 712.
|
118
|
-
|
119
|
-
```ruby
|
120
|
-
address = Eth::Address.new "0xd496b23d61f88a8c7758fca7560dcfac7b3b01f9"
|
121
|
-
# => #<Eth::Address:0x00005574a6bd4fc8 @address="0xd496b23d61f88a8c7758fca7560dcfac7b3b01f9">
|
122
|
-
signature = "ac6a59417d8688c8144f01a662384fa691636b48a071d4b7c13902bb87ca472b0bce1d7a758f39a5759ed5e937ce61f50dd1b83158371f8d0faeb9b7d81c19422d"
|
123
|
-
# => "ac6a59417d8688c8144f01a662384fa691636b48a071d4b7c13902bb87ca472b0bce1d7a758f39a5759ed5e937ce61f50dd1b83158371f8d0faeb9b7d81c19422d"
|
124
|
-
recovered_key = Eth::Signature.personal_recover "Hello World!", signature, Eth::Chain::GOERLI
|
125
|
-
# => "04b45200621c013a5fbab999ac33b0c836328a04afa0255ffbe6ea0f6fd97e187b02199886d942a9f50f7e279a2bc74c93b2afcbd7255489939f9b36a5eae5e281"
|
126
|
-
Eth::Util.public_key_to_address(recovered_key).to_s
|
127
|
-
# => "0xD496b23D61F88A8C7758fca7560dCFac7b3b01F9"
|
128
|
-
Eth::Signature.verify "Hello World!", signature, address, Eth::Chain::GOERLI
|
129
|
-
# => true
|
130
|
-
```
|
131
|
-
|
132
|
-
See `/spec` or [Documentation](https://q9f.github.io/eth.rb/) for signing typed data as per EIP-712.
|
133
|
-
|
134
|
-
### 2.3. Ethereum Chains (EIP-155)
|
135
|
-
Manage Ethereum chain IDs for EIP-155 replay protection.
|
136
|
-
|
137
|
-
```ruby
|
138
|
-
chain_id = Eth::Chain::OPTIMISM
|
139
|
-
# => 10
|
140
|
-
v = Eth::Chain.to_v 0, Eth::Chain::OPTIMISM
|
141
|
-
# => 55
|
142
|
-
recovery_id = Eth::Chain.to_recovery_id v, Eth::Chain::OPTIMISM
|
143
|
-
# => 0
|
144
|
-
chain_id = Eth::Chain.to_chain_id v
|
145
|
-
# => 10
|
146
|
-
```
|
147
|
-
|
148
|
-
### 2.4. Ethereum Transactions (EIP-1559, EIP-2718, EIP-2930)
|
149
|
-
Create an EIP-1559-conform transaction:
|
150
|
-
|
151
|
-
```ruby
|
152
|
-
payload = {
|
153
|
-
chain_id: Eth::Chain::GOERLI,
|
154
|
-
nonce: 5,
|
155
|
-
priority_fee: 3 * Eth::Unit::GWEI,
|
156
|
-
max_gas_fee: 69 * Eth::Unit::GWEI,
|
157
|
-
gas_limit: 230_420,
|
158
|
-
to: "0xCaA29806044A08E533963b2e573C1230A2cd9a2d",
|
159
|
-
value: 0.069423 * Eth::Unit::ETHER,
|
160
|
-
}
|
161
|
-
# => {:chain_id=>5, :nonce=>5, :priority_fee=>0.3e10, :max_gas_fee=>0.69e11, :gas_limit=>230420, :to=>"0xCaA29806044A08E533963b2e573C1230A2cd9a2d", :value=>0.69423e17}
|
162
|
-
tx = Eth::Tx.new payload
|
163
|
-
# => #<Eth::Tx::Eip1559:0x0000557e35fc5a68 @access_list=[], @amount=69423000000000000, @chain_id=5, @destination="CaA29806044A08E533963b2e573C1230A2cd9a2d", @gas_limit=230420, @max_fee_per_gas=69000000000, @max_priority_fee_per_gas=3000000000, @payload="", @sender="", @signature_r=0, @signature_s=0, @signature_y_parity=nil, @signer_nonce=5, @type=2>
|
164
|
-
my_key = Eth::Key.new priv: "30137644b564785d01420f8043f043d74dcca64008e57c59f8ce713a0005a54b"
|
165
|
-
# => #<Eth::Key:0x0000557e36243178 @private_key=#<Secp256k1::PrivateKey:0x0000557e36242d40 @data="0\x13vD\xB5dx]\x01B\x0F\x80C\xF0C\xD7M\xCC\xA6@\b\xE5|Y\xF8\xCEq:\x00\x05\xA5K">, @public_key=#<Secp256k1::PublicKey:0x0000557e36242cf0>>
|
166
|
-
tx.sign my_key
|
167
|
-
# => "cba302c0ebf8d0205a78ae97f560419b407e32e2426f416abc95a9bfc9dac09c"
|
168
|
-
tx.hex
|
169
|
-
# => "02f873050584b2d05e00851010b872008303841494caa29806044a08e533963b2e573c1230a2cd9a2d87f6a3d9c63df00080c080a03aa187d10b138d3e0155729adb961cd89e10f988ba2d19d6869770b9e5a23d10a04d40864600136ae214916043c7d63b849c98db757e95c86983a036982816e1af"
|
170
|
-
```
|
171
|
-
|
172
|
-
This gem also supports access lists and ABI-encoded data payloads. See `/spec` or [Documentation](https://q9f.github.io/eth.rb/) for more details about the various supported transaction types (legacy, type-1, type-2), payload parameters, and how to estimate intrinsic gas costs.
|
173
|
-
|
174
|
-
### 2.5. Ethereum ABI Encoder and Decoder
|
175
|
-
Encode and decode Ethereum application binary interface data (ABI).
|
176
|
-
|
177
|
-
```ruby
|
178
|
-
Eth::Util.bin_to_hex Eth::Abi.encode(["string", "address"], ["Hello, Bob!", "0xd496b23d61f88a8c7758fca7560dcfac7b3b01f9"])
|
179
|
-
# => "0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000d496b23d61f88a8c7758fca7560dcfac7b3b01f9000000000000000000000000000000000000000000000000000000000000000b48656c6c6f2c20426f6221000000000000000000000000000000000000000000"
|
180
|
-
Eth::Abi.decode(["string", "address"], "0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000d496b23d61f88a8c7758fca7560dcfac7b3b01f9000000000000000000000000000000000000000000000000000000000000000b48656c6c6f2c20426f6221000000000000000000000000000000000000000000")
|
181
|
-
# => ["Hello, Bob!", "0xd496b23d61f88a8c7758fca7560dcfac7b3b01f9"]
|
182
|
-
```
|
183
|
-
|
184
|
-
### 2.6. Ethereum RLP Encoder and Decoder
|
185
|
-
Serialize and deserialize Ethereum recursive-length prefix data (RLP).
|
186
|
-
|
187
|
-
```ruby
|
188
|
-
Eth::Util.bin_to_hex Eth::Rlp.encode ["Hello, Bob!", "0xd496b23d61f88a8c7758fca7560dcfac7b3b01f9"]
|
189
|
-
# => "f78b48656c6c6f2c20426f6221aa307864343936623233643631663838613863373735386663613735363064636661633762336230316639"
|
190
|
-
Eth::Rlp.decode "f78b48656c6c6f2c20426f6221aa307864343936623233643631663838613863373735386663613735363064636661633762336230316639"
|
191
|
-
# => ["Hello, Bob!", "0xd496b23d61f88a8c7758fca7560dcfac7b3b01f9"]
|
192
|
-
```
|
193
|
-
|
194
|
-
Or ;-)
|
195
|
-
|
196
|
-
```ruby
|
197
|
-
Eth::Rlp.decode "c7c0c1c0c3c0c1c0"
|
198
|
-
# => [[], [[]], [[], [[]]]]
|
199
|
-
```
|
200
|
-
|
201
|
-
### 2.7. Ethereum RPC-Client
|
202
|
-
Create an IPC- or HTTP-RPC-API client to seamlessly query the chain state, e.g., Infura over HTTPS with access token:
|
203
|
-
|
204
|
-
```ruby
|
205
|
-
infura = Eth::Client.create "https://mainnet.infura.io/v3/#{access_token}"
|
206
|
-
# => #<Eth::Client::Http:0x000055d43f3ca460 @gas_limit=21000, @host="mainnet.infura.io", @id=0, @max_fee_per_gas=0.2e11, @max_priority_fee_per_gas=0, @port=443, @ssl=true, @uri=#<URI::HTTPS https://mainnet.infura.io/v3/31b...d93>>
|
207
|
-
deposit_contract = Eth::Address.new "0x00000000219ab540356cBB839Cbe05303d7705Fa"
|
208
|
-
# => #<Eth::Address:0x000055d43f381738 @address="0x00000000219ab540356cBB839Cbe05303d7705Fa">
|
209
|
-
infura.get_balance deposit_contract
|
210
|
-
# => 9087314000069000000000069
|
211
|
-
```
|
212
|
-
|
213
|
-
Or set up a local development environment with `geth --dev`:
|
214
|
-
|
215
|
-
```ruby
|
216
|
-
cli = Eth::Client.create "/tmp/geth.ipc"
|
217
|
-
# => #<Eth::Client::Ipc:0x000055d43f51c390 @gas_limit=21000, @id=0, @max_fee_per_gas=0.2e11, @max_priority_fee_per_gas=0, @path="/tmp/geth.ipc">
|
218
|
-
cli.eth_coinbase
|
219
|
-
# => {"jsonrpc"=>"2.0", "id"=>1, "result"=>"0x6868074fb21c48dfad0c448fbabd99383a6598e4"}
|
220
|
-
tx = cli.transfer_and_wait(Eth::Key.new.address, 1337 * Eth::Unit::ETHER)
|
221
|
-
# => "0x141c6dff40df34fe4fce5a65588d2161dab3e0e977fb8049ff7d79bc901034f7"
|
222
|
-
cli.eth_get_transaction_by_hash tx
|
223
|
-
# => {"jsonrpc"=>"2.0", "id"=>8, "result"=> {"blockHash"=>"0x47e742038c75851348dbda87b15fde044d54c442c371f43bea881a44d5589de3", "blockNumber"=>"0x1", "from"=>"0x6868074fb21c48dfad0c448fbabd99383a6598e4", "gas"=>"0x5208", "gasPrice"=>"0x342770c1", "maxFeePerGas"=>"0x77359401", "maxPriorityFeePerGas"=>"0x1", "hash"=>"0x141c6dff40df34fe4fce5a65588d2161dab3e0e977fb8049ff7d79bc901034f7", "input"=>"0x", "nonce"=>"0x0", "to"=>"0x311c61e5dc6123ad016bb7fd687d283c327bcd5f", "transactionIndex"=>"0x0", "value"=>"0x487a9a304539440000", "type"=>"0x2", "accessList"=>[], "chainId"=>"0x539", "v"=>"0x0", "r"=>"0xb42477d69eae65a3a3d91d9cb173e4a45a403fb0a15fa729dbfdc9d13211d7b5", "s"=>"0x4a2f98fc2b61c2d7c907520bc8c6ebe42ea6fe1cb6824f95e4b30e9464395100"}}
|
224
|
-
cli.get_balance "0x311c61e5dc6123ad016bb7fd687d283c327bcd5f"
|
225
|
-
# => 1337000000000000000000
|
226
|
-
cli.get_nonce cli.eth_coinbase["result"]
|
227
|
-
# => 1
|
228
|
-
```
|
229
|
-
|
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.
|
231
|
-
|
232
|
-
### 2.8. Solidity Compiler Bindings
|
233
|
-
Link a system-level Solidity compiler (`solc`) to your Ruby library and compile contracts.
|
234
|
-
|
235
|
-
```ruby
|
236
|
-
solc = Eth::Solidity.new
|
237
|
-
# => #<Eth::Solidity:0x000055f05040c6d0 @compiler="/usr/bin/solc">
|
238
|
-
contract = solc.compile "spec/fixtures/contracts/greeter.sol"
|
239
|
-
# => {"Greeter"=>
|
240
|
-
# {"abi"=>
|
241
|
-
# [{"inputs"=>[{"internalType"=>"string", "name"=>"message", "type"=>"string"}], "stateMutability"=>"nonpayable", "type"=>"constructor"},
|
242
|
-
# {"inputs"=>[], "name"=>"greet", "outputs"=>[{"internalType"=>"string", "name"=>"", "type"=>"string"}], "stateMutability"=>"view", "type"=>"function"},
|
243
|
-
# {"inputs"=>[], "name"=>"kill", "outputs"=>[], "stateMutability"=>"nonpayable", "type"=>"function"}],
|
244
|
-
# "bin"=>
|
245
|
-
# "6080604052348015...6c634300080c0033"},
|
246
|
-
# "Mortal"=>
|
247
|
-
# {"abi"=>[{"inputs"=>[], "stateMutability"=>"nonpayable", "type"=>"constructor"}, {"inputs"=>[], "name"=>"kill", "outputs"=>[], "stateMutability"=>"nonpayable", "type"=>"function"}],
|
248
|
-
# "bin"=>
|
249
|
-
# "6080604052348015...6c634300080c0033"}}
|
250
|
-
```
|
251
|
-
|
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.
|
253
|
-
|
254
|
-
### 2.9. Interact with Smart Contract
|
255
|
-
|
256
|
-
Create, compile, and deploy smart contracts.
|
257
|
-
|
258
|
-
```ruby
|
259
|
-
contract = Eth::Contract.from_file(file: 'spec/fixtures/contracts/dummy.sol')
|
260
|
-
# => #<Eth::Contract::Dummy:0x00007fbeee936598>
|
261
|
-
cli = Eth::Client.create "/tmp/geth.ipc"
|
262
|
-
# => #<Eth::Client::Ipc:0x00007fbeee946128 @gas_limit=21000, @id=0, @max_fee_per_gas=0.2e11, @max_priority_fee_per_gas=0, @path="/tmp/geth.ipc">
|
263
|
-
address = cli.deploy_and_wait(contract)
|
264
|
-
# => "0x2f2faa160420cee087ded96bad52475147136bd8"
|
265
|
-
```
|
266
|
-
|
267
|
-
Transact with or call the deployed contract.
|
268
|
-
|
269
|
-
```ruby
|
270
|
-
cli.transact_and_wait(contract, "set", 1234)
|
271
|
-
# => "0x49ca4c0a5729da19a1d2574de9a444a9cd3219bdad81745b54f9cf3bb83b6a06"
|
272
|
-
cli.call(contract, "get")
|
273
|
-
# => 1234
|
274
|
-
```
|
275
|
-
|
276
|
-
Or call an existing contract, e.g., the ENS registry:
|
277
|
-
|
278
|
-
```ruby
|
279
|
-
ens_registry_abi = '[{"inputs":[{"internalType":"contract ENS","name":"_old","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"label","type":"bytes32"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"address","name":"resolver","type":"address"}],"name":"NewResolver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"NewTTL","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"old","outputs":[{"internalType":"contract ENS","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"recordExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"setRecord","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"label","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"label","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"setSubnodeRecord","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"setTTL","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"ttl","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"}]'
|
280
|
-
ens_registry_address = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
|
281
|
-
ens_registry_name = "ENSRegistryWithFallback"
|
282
|
-
ens_registry = Eth::Contract.from_abi(name: ens_registry_name, address: ens_registry_address, abi: ens_registry_abi)
|
283
|
-
# => #<Eth::Contract::ENSRegistryWithFallback:0x000055bece570980>
|
284
|
-
ens_registry.address
|
285
|
-
# => "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
|
286
|
-
cli.call(ens_registry, "old")
|
287
|
-
# => "0x112234455c3a32fd11230c42e7bccd4a84e02010"
|
288
|
-
```
|
289
|
-
|
290
|
-
The gem also comes with an EIP-1271 smart-contract authentification interface.
|
291
|
-
|
292
|
-
```ruby
|
293
|
-
cli.is_valid_signature contract, hash, signature
|
294
|
-
# => true
|
295
|
-
```
|
59
|
+
for all the details and example snippets.
|
296
60
|
|
297
|
-
##
|
61
|
+
## Documentation
|
298
62
|
The documentation can be found at: https://q9f.github.io/eth.rb
|
299
63
|
|
300
64
|
For any specific version, docs can be generated by `yard`:
|
301
65
|
|
302
66
|
```shell
|
303
67
|
gem install bundler rdoc yard
|
304
|
-
git checkout
|
68
|
+
git checkout $VERSION
|
305
69
|
yard doc
|
306
70
|
```
|
307
71
|
|
308
72
|
The goal is to have 100% API documentation available.
|
309
73
|
|
310
|
-
##
|
74
|
+
## Testing
|
311
75
|
The test suite expects working local HTTP and IPC endpoints with a prefunded developer account, e.g.:
|
312
76
|
|
313
77
|
```shell
|
@@ -324,24 +88,24 @@ rspec
|
|
324
88
|
|
325
89
|
The goal is to have 100% specification coverage for all code inside this gem.
|
326
90
|
|
327
|
-
##
|
91
|
+
## Contributing
|
328
92
|
Pull requests are welcome! To contribute, please consider the following:
|
329
93
|
* Code should be fully documented. Run `yard doc` and make sure it does not yield any warnings or undocumented sets.
|
330
94
|
* Code should be fully covered by tests. Run `rspec` to make sure all tests pass. The CI has an integration that will assis you to identify uncovered lines of code and get coverage up to 100%.
|
331
95
|
* Code should be formatted properly. Try to eliminate the most common issues such as trailing white-spaces or duplicate new-lines. Usage of the `rufo` gem is recommended.
|
332
|
-
* Submit pull requests, questions, or issues to Github: https://github.com/q9f/eth.rb
|
96
|
+
* Submit pull requests, questions, or issues to Github: <https://github.com/q9f/eth.rb>
|
333
97
|
|
334
|
-
##
|
98
|
+
## License and Credits
|
335
99
|
The `eth` gem is licensed under the conditions of [Apache 2.0](./LICENSE.txt). Please see [AUTHORS](./AUTHORS.txt) for contributors and copyright notices.
|
336
100
|
|
337
101
|
This gem is a complete rewrite of the old `eth` gem by Steve Ellis.
|
338
|
-
* https://github.com/se3000/ruby-eth
|
102
|
+
* <https://github.com/se3000/ruby-eth> (MIT)
|
339
103
|
|
340
104
|
It is not only a rewrite of the `eth` gem but also a partial merge of the `ethereum` gem by Marek Kirejczyk and Yuta Kurotaki.
|
341
|
-
* https://github.com/EthWorks/ethereum.rb (MIT)
|
105
|
+
* <https://github.com/EthWorks/ethereum.rb> (MIT)
|
342
106
|
|
343
107
|
This gem also includes a revised version of the ABI gem by Jan Xie and Zhang Yaning.
|
344
|
-
* https://github.com/cryptape/ruby-ethereum-abi (MIT)
|
108
|
+
* <https://github.com/cryptape/ruby-ethereum-abi> (MIT)
|
345
109
|
|
346
110
|
It also contains a condensed version of the RLP gem by Jan Xie and Zhang Yaning.
|
347
|
-
* https://github.com/cryptape/ruby-rlp (MIT)
|
111
|
+
* <https://github.com/cryptape/ruby-rlp> (MIT)
|
data/lib/eth/abi.rb
CHANGED
@@ -74,7 +74,7 @@ module Eth
|
|
74
74
|
# @return [String] the encoded type.
|
75
75
|
# @raise [EncodingError] if value does not match type.
|
76
76
|
def encode_type(type, arg)
|
77
|
-
if %w(string bytes).include? type.base_type and type.sub_type.empty?
|
77
|
+
if %w(string bytes).include? type.base_type and type.sub_type.empty? and type.dimensions.empty?
|
78
78
|
raise EncodingError, "Argument must be a String" unless arg.instance_of? String
|
79
79
|
|
80
80
|
# encodes strings and bytes
|
@@ -89,10 +89,24 @@ module Eth
|
|
89
89
|
head += encode_type Type.size_type, arg.size
|
90
90
|
nested_sub = type.nested_sub
|
91
91
|
nested_sub_size = type.nested_sub.size
|
92
|
-
arg.size.times do |i|
|
93
92
|
|
94
|
-
|
95
|
-
|
93
|
+
# calculate offsets
|
94
|
+
if %w(string bytes).include?(type.base_type) && type.sub_type.empty?
|
95
|
+
offset = 0
|
96
|
+
arg.size.times do |i|
|
97
|
+
if i == 0
|
98
|
+
offset = arg.size * 32
|
99
|
+
else
|
100
|
+
number_of_words = ((arg[i - 1].size + 32 - 1) / 32).floor
|
101
|
+
total_bytes_length = number_of_words * 32
|
102
|
+
offset += total_bytes_length + 32
|
103
|
+
end
|
104
|
+
|
105
|
+
head += encode_type Type.size_type, offset
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
arg.size.times do |i|
|
96
110
|
head += encode_type nested_sub, arg[i]
|
97
111
|
end
|
98
112
|
return "#{head}#{tail}"
|
@@ -306,6 +320,7 @@ module Eth
|
|
306
320
|
|
307
321
|
# Properly encodes unsigned integers.
|
308
322
|
def encode_uint(arg, type)
|
323
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
309
324
|
raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::UINT_MAX or arg < Constant::UINT_MIN
|
310
325
|
real_size = type.sub_type.to_i
|
311
326
|
i = arg.to_i
|
@@ -315,6 +330,7 @@ module Eth
|
|
315
330
|
|
316
331
|
# Properly encodes signed integers.
|
317
332
|
def encode_int(arg, type)
|
333
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
318
334
|
raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::INT_MAX or arg < Constant::INT_MIN
|
319
335
|
real_size = type.sub_type.to_i
|
320
336
|
i = arg.to_i
|
@@ -330,6 +346,7 @@ module Eth
|
|
330
346
|
|
331
347
|
# Properly encodes unsigned fixed-point numbers.
|
332
348
|
def encode_ufixed(arg, type)
|
349
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
333
350
|
high, low = type.sub_type.split("x").map(&:to_i)
|
334
351
|
raise ValueOutOfBounds, arg unless arg >= 0 and arg < 2 ** high
|
335
352
|
return Util.zpad_int((arg * 2 ** low).to_i)
|
@@ -337,6 +354,7 @@ module Eth
|
|
337
354
|
|
338
355
|
# Properly encodes signed fixed-point numbers.
|
339
356
|
def encode_fixed(arg, type)
|
357
|
+
raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric
|
340
358
|
high, low = type.sub_type.split("x").map(&:to_i)
|
341
359
|
raise ValueOutOfBounds, arg unless arg >= -2 ** (high - 1) and arg < 2 ** (high - 1)
|
342
360
|
i = (arg * 2 ** low).to_i
|
data/lib/eth/chain.rb
CHANGED
@@ -92,6 +92,15 @@ module Eth
|
|
92
92
|
# Chain ID for the geth private network preset.
|
93
93
|
PRIVATE_GETH = 1337.freeze
|
94
94
|
|
95
|
+
# Indicates wether the given `v` indicates a legacy chain value
|
96
|
+
# used by ledger wallets without EIP-155 replay protection.
|
97
|
+
#
|
98
|
+
# @param v [Integer] the signature's `v` value.
|
99
|
+
# @return [Boolean] true if ledger's legacy value.
|
100
|
+
def is_ledger?(v)
|
101
|
+
[0, 1].include? v
|
102
|
+
end
|
103
|
+
|
95
104
|
# Indicates wether the given `v` indicates a legacy chain value
|
96
105
|
# without EIP-155 replay protection.
|
97
106
|
#
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Copyright (c) 2016-2022 The Ruby-Eth Contributors
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require "net/http"
|
16
|
+
|
17
|
+
# Provides the {Eth} module.
|
18
|
+
module Eth
|
19
|
+
|
20
|
+
# Provides an HTTP/S-RPC client with basic authentication.
|
21
|
+
class Client::HttpAuth < Client
|
22
|
+
|
23
|
+
# The host of the HTTP endpoint.
|
24
|
+
attr_reader :host
|
25
|
+
|
26
|
+
# The port of the HTTP endpoint.
|
27
|
+
attr_reader :port
|
28
|
+
|
29
|
+
# The full URI of the HTTP endpoint, including path.
|
30
|
+
attr_reader :uri
|
31
|
+
|
32
|
+
# Attribute indicator for SSL.
|
33
|
+
attr_reader :ssl
|
34
|
+
|
35
|
+
# Attribute for user.
|
36
|
+
attr_reader :user
|
37
|
+
|
38
|
+
# Constructor for the HTTP Client. Should not be used; use
|
39
|
+
# {Client.create} intead.
|
40
|
+
#
|
41
|
+
# @param host [String] an URI pointing to an HTTP RPC-API.
|
42
|
+
def initialize(host)
|
43
|
+
super
|
44
|
+
uri = URI.parse(host)
|
45
|
+
raise ArgumentError, "Unable to parse the HTTP-URI!" unless ["http", "https"].include? uri.scheme
|
46
|
+
@host = uri.host
|
47
|
+
@port = uri.port
|
48
|
+
@ssl = uri.scheme == "https"
|
49
|
+
@user = uri.user
|
50
|
+
@password = uri.password
|
51
|
+
@uri = URI("#{uri.scheme}://#{uri.user}:#{uri.password}@#{@host}:#{@port}#{uri.path}")
|
52
|
+
end
|
53
|
+
|
54
|
+
# Sends an RPC request to the connected HTTP client.
|
55
|
+
#
|
56
|
+
# @param payload [Hash] the RPC request parameters.
|
57
|
+
# @return [String] a JSON-encoded response.
|
58
|
+
def send(payload)
|
59
|
+
http = Net::HTTP.new(@host, @port)
|
60
|
+
http.use_ssl = @ssl
|
61
|
+
header = { "Content-Type" => "application/json" }
|
62
|
+
request = Net::HTTP::Post.new(@uri, header)
|
63
|
+
request.body = payload
|
64
|
+
response = http.request(request)
|
65
|
+
response.body
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Attribute for password.
|
72
|
+
attr_reader :password
|
73
|
+
end
|
data/lib/eth/client.rb
CHANGED
@@ -28,24 +28,31 @@ module Eth
|
|
28
28
|
# The connected network's client coinbase.
|
29
29
|
attr_accessor :default_account
|
30
30
|
|
31
|
-
# The default transaction max priority fee per gas in Wei.
|
31
|
+
# The default transaction max priority fee per gas in Wei, defaults to {Tx::DEFAULT_PRIORITY_FEE}.
|
32
32
|
attr_accessor :max_priority_fee_per_gas
|
33
33
|
|
34
|
-
# The default transaction max fee per gas in Wei.
|
34
|
+
# The default transaction max fee per gas in Wei, defaults to {Tx::DEFAULT_GAS_PRICE}.
|
35
35
|
attr_accessor :max_fee_per_gas
|
36
36
|
|
37
|
-
# The default gas limit for the transaction.
|
37
|
+
# The default gas limit for the transaction, defaults to {Tx::DEFAULT_GAS_LIMIT}.
|
38
38
|
attr_accessor :gas_limit
|
39
39
|
|
40
40
|
# Creates a new RPC-Client, either by providing an HTTP/S host or
|
41
|
-
# an IPC path.
|
41
|
+
# an IPC path. Supports basic authentication with username and password.
|
42
|
+
#
|
43
|
+
# **Note**, this sets the folling gas defaults: {Tx::DEFAULT_PRIORITY_FEE},
|
44
|
+
# {Tx::DEFAULT_GAS_PRICE}, and {Tx::DEFAULT_GAS_LIMIT}. Use
|
45
|
+
# {#max_priority_fee_per_gas}, {#max_fee_per_gas}, and {#gas_limit} to set
|
46
|
+
# custom values prior to submitting transactions.
|
42
47
|
#
|
43
48
|
# @param host [String] either an HTTP/S host or an IPC path.
|
44
49
|
# @return [Eth::Client::Ipc] an IPC client.
|
50
|
+
# @return [Eth::Client::HttpAuth] an HTTP client with basic authentication.
|
45
51
|
# @return [Eth::Client::Http] an HTTP client.
|
46
52
|
# @raise [ArgumentError] in case it cannot determine the client type.
|
47
53
|
def self.create(host)
|
48
54
|
return Client::Ipc.new host if host.end_with? ".ipc"
|
55
|
+
return Client::HttpAuth.new host if Regexp.new(":.*@.*:", Regexp::IGNORECASE).match host
|
49
56
|
return Client::Http.new host if host.start_with? "http"
|
50
57
|
raise ArgumentError, "Unable to detect client type!"
|
51
58
|
end
|
@@ -54,13 +61,16 @@ module Eth
|
|
54
61
|
# use {Client.create} intead.
|
55
62
|
def initialize(_)
|
56
63
|
@id = 0
|
57
|
-
@max_priority_fee_per_gas =
|
64
|
+
@max_priority_fee_per_gas = Tx::DEFAULT_PRIORITY_FEE
|
58
65
|
@max_fee_per_gas = Tx::DEFAULT_GAS_PRICE
|
59
66
|
@gas_limit = Tx::DEFAULT_GAS_LIMIT
|
60
67
|
end
|
61
68
|
|
62
69
|
# Gets the default account (coinbase) of the connected client.
|
63
70
|
#
|
71
|
+
# **Note**, that many remote providers (e.g., Infura) do not provide
|
72
|
+
# any accounts.
|
73
|
+
#
|
64
74
|
# @return [Eth::Address] the coinbase account address.
|
65
75
|
def default_account
|
66
76
|
@default_account ||= Address.new eth_coinbase["result"]
|
@@ -90,14 +100,12 @@ module Eth
|
|
90
100
|
end
|
91
101
|
|
92
102
|
# Simply transfer Ether to an account and waits for it to be mined.
|
93
|
-
# Uses `eth_coinbase` and external signer if no
|
103
|
+
# Uses `eth_coinbase` and external signer if no sender key is
|
94
104
|
# provided.
|
95
105
|
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
# @
|
99
|
-
# @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
100
|
-
# @return [String] the transaction hash.
|
106
|
+
# See {#transfer} for params and overloads.
|
107
|
+
#
|
108
|
+
# @return [String] the transaction hash once it is mined.
|
101
109
|
def transfer_and_wait(destination, amount, sender_key = nil, legacy = false)
|
102
110
|
wait_for_tx(transfer(destination, amount, sender_key, legacy))
|
103
111
|
end
|
@@ -106,11 +114,14 @@ module Eth
|
|
106
114
|
# access lists attached. Uses `eth_coinbase` and external signer
|
107
115
|
# if no sender key is provided.
|
108
116
|
#
|
117
|
+
# **Note**, that many remote providers (e.g., Infura) do not provide
|
118
|
+
# any accounts. Provide a `sender_key` if you experience issues.
|
119
|
+
#
|
109
120
|
# @param destination [Eth::Address] the destination address.
|
110
121
|
# @param amount [Integer] the transfer amount in Wei.
|
111
122
|
# @param sender_key [Eth::Key] the sender private key.
|
112
123
|
# @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
113
|
-
# @return [String] the transaction hash.
|
124
|
+
# @return [String] the local transaction hash.
|
114
125
|
def transfer(destination, amount, sender_key = nil, legacy = false)
|
115
126
|
params = {
|
116
127
|
value: amount,
|
@@ -152,14 +163,9 @@ module Eth
|
|
152
163
|
# Deploys a contract and waits for it to be mined. Uses
|
153
164
|
# `eth_coinbase` or external signer if no sender key is provided.
|
154
165
|
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
# @
|
158
|
-
# @param contract [Eth::Contract] contracts to deploy.
|
159
|
-
# *args Optional variable constructor parameter list
|
160
|
-
# **sender_key [Eth::Key] the sender private key.
|
161
|
-
# **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
162
|
-
# @return [String] the contract address.
|
166
|
+
# See {#deploy} for params and overloads.
|
167
|
+
#
|
168
|
+
# @return [String] the contract address once it's mined.
|
163
169
|
def deploy_and_wait(contract, *args, **kwargs)
|
164
170
|
hash = wait_for_tx(deploy(contract, *args, **kwargs))
|
165
171
|
addr = eth_get_transaction_receipt(hash)["result"]["contractAddress"]
|
@@ -169,23 +175,34 @@ module Eth
|
|
169
175
|
# Deploys a contract. Uses `eth_coinbase` or external signer
|
170
176
|
# if no sender key is provided.
|
171
177
|
#
|
178
|
+
# **Note**, that many remote providers (e.g., Infura) do not provide
|
179
|
+
# any accounts. Provide a `sender_key` if you experience issues.
|
180
|
+
#
|
172
181
|
# @overload deploy(contract)
|
173
182
|
# @param contract [Eth::Contract] contracts to deploy.
|
183
|
+
# @overload deploy(contract, *args)
|
184
|
+
# @param contract [Eth::Contract] the contracts to deploy.
|
185
|
+
# @param *args (optional) variable constructor parameter list.
|
174
186
|
# @overload deploy(contract, *args, **kwargs)
|
175
|
-
# @param contract [Eth::Contract] contracts to deploy.
|
176
|
-
# *args
|
177
|
-
# **sender_key [Eth::Key] the sender private key.
|
178
|
-
# **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
187
|
+
# @param contract [Eth::Contract] the contracts to deploy.
|
188
|
+
# @param *args (optional) variable constructor parameter list.
|
189
|
+
# @param **sender_key [Eth::Key] the sender private key.
|
190
|
+
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
191
|
+
# @param **gas_limit [Integer] optional gas limit override for deploying the contract.
|
179
192
|
# @return [String] the transaction hash.
|
180
193
|
# @raise [ArgumentError] in case the contract does not have any source.
|
181
194
|
def deploy(contract, *args, **kwargs)
|
182
195
|
raise ArgumentError, "Cannot deploy contract without source or binary!" if contract.bin.nil?
|
183
196
|
raise ArgumentError, "Missing contract constructor params!" if contract.constructor_inputs.length != args.length
|
184
|
-
gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
|
185
197
|
data = contract.bin
|
186
198
|
unless args.empty?
|
187
199
|
data += encode_constructor_params(contract, args)
|
188
200
|
end
|
201
|
+
gas_limit = if kwargs[:gas_limit]
|
202
|
+
kwargs[:gas_limit]
|
203
|
+
else
|
204
|
+
Tx.estimate_intrinsic_gas(data) + Tx::CREATE_GAS
|
205
|
+
end
|
189
206
|
params = {
|
190
207
|
value: 0,
|
191
208
|
gas_limit: gas_limit,
|
@@ -224,23 +241,24 @@ module Eth
|
|
224
241
|
# Calls a contract function without executing it
|
225
242
|
# (non-transactional contract read).
|
226
243
|
#
|
227
|
-
# @overload call(contract,
|
228
|
-
# @param contract [Eth::Contract] subject contract to call.
|
229
|
-
# @param
|
230
|
-
# @overload call(contract,
|
231
|
-
# @param contract [Eth::Contract] subject contract to call.
|
232
|
-
# @param
|
233
|
-
# @param
|
234
|
-
# @overload call(contract,
|
235
|
-
# @param contract [Eth::Contract] subject contract to call.
|
236
|
-
# @param
|
237
|
-
# @param
|
238
|
-
# @param sender_key [Eth::Key] the sender private key.
|
239
|
-
# @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
244
|
+
# @overload call(contract, function)
|
245
|
+
# @param contract [Eth::Contract] the subject contract to call.
|
246
|
+
# @param function [String] method name to be called.
|
247
|
+
# @overload call(contract, function, *args)
|
248
|
+
# @param contract [Eth::Contract] the subject contract to call.
|
249
|
+
# @param function [String] method name to be called.
|
250
|
+
# @param *args optional function arguments.
|
251
|
+
# @overload call(contract, function, *args, **kwargs)
|
252
|
+
# @param contract [Eth::Contract] the subject contract to call.
|
253
|
+
# @param function [String] method name to be called.
|
254
|
+
# @param *args optional function arguments.
|
255
|
+
# @param **sender_key [Eth::Key] the sender private key.
|
256
|
+
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
257
|
+
# @param **gas_limit [Integer] optional gas limit override for deploying the contract.
|
240
258
|
# @return [Object] returns the result of the call.
|
241
|
-
def call(contract,
|
242
|
-
func = contract.functions.select { |func| func.name ==
|
243
|
-
raise ArgumentError, "
|
259
|
+
def call(contract, function, *args, **kwargs)
|
260
|
+
func = contract.functions.select { |func| func.name == function }[0]
|
261
|
+
raise ArgumentError, "this function does not exist!" if func.nil?
|
244
262
|
output = call_raw(contract, func, *args, **kwargs)
|
245
263
|
if output&.length == 1
|
246
264
|
return output[0]
|
@@ -252,24 +270,32 @@ module Eth
|
|
252
270
|
# Executes a contract function with a transaction (transactional
|
253
271
|
# contract read/write).
|
254
272
|
#
|
255
|
-
#
|
256
|
-
#
|
257
|
-
#
|
258
|
-
# @overload transact(contract,
|
259
|
-
# @param contract [Eth::Contract] subject contract to
|
260
|
-
# @param
|
261
|
-
#
|
262
|
-
#
|
263
|
-
# @param
|
264
|
-
# @param
|
265
|
-
#
|
266
|
-
# @param
|
267
|
-
# @param
|
268
|
-
# @param
|
269
|
-
#
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
+
# **Note**, that many remote providers (e.g., Infura) do not provide
|
274
|
+
# any accounts. Provide a `sender_key` if you experience issues.
|
275
|
+
#
|
276
|
+
# @overload transact(contract, function)
|
277
|
+
# @param contract [Eth::Contract] the subject contract to write to.
|
278
|
+
# @param function [String] method name to be executed.
|
279
|
+
# @overload transact(contract, function, *args)
|
280
|
+
# @param contract [Eth::Contract] the subject contract to write to.
|
281
|
+
# @param function [String] method name to be executed.
|
282
|
+
# @param *args optional function arguments.
|
283
|
+
# @overload transact(contract, function, *args, **kwargs)
|
284
|
+
# @param contract [Eth::Contract] the subject contract to write to.
|
285
|
+
# @param function_name [String] method name to be executed.
|
286
|
+
# @param *args optional function arguments.
|
287
|
+
# @param **sender_key [Eth::Key] the sender private key.
|
288
|
+
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
289
|
+
# @param **address [Eth::Address] contract address.
|
290
|
+
# @param **gas_limit [Integer] optional gas limit override for deploying the contract.
|
291
|
+
# @return [Object] returns the result of the transaction.
|
292
|
+
def transact(contract, function, *args, **kwargs)
|
293
|
+
gas_limit = if kwargs[:gas_limit]
|
294
|
+
kwargs[:gas_limit]
|
295
|
+
else
|
296
|
+
Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
|
297
|
+
end
|
298
|
+
fun = contract.functions.select { |func| func.name == function }[0]
|
273
299
|
params = {
|
274
300
|
value: 0,
|
275
301
|
gas_limit: gas_limit,
|
@@ -309,23 +335,11 @@ module Eth
|
|
309
335
|
# Executes a contract function with a transaction and waits for it
|
310
336
|
# to be mined (transactional contract read/write).
|
311
337
|
#
|
312
|
-
#
|
313
|
-
#
|
314
|
-
#
|
315
|
-
|
316
|
-
|
317
|
-
# @param function_name [String] method name to be called.
|
318
|
-
# @param value [Integer|String] function arguments.
|
319
|
-
# @overload transact_and_wait(contract, function_name, value, sender_key, legacy, address)
|
320
|
-
# @param contract [Eth::Contract] subject contract to call.
|
321
|
-
# @param function_name [String] method name to be called.
|
322
|
-
# @param value [Integer|String] function arguments.
|
323
|
-
# @param sender_key [Eth::Key] the sender private key.
|
324
|
-
# @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
325
|
-
# @param address [String] contract address.
|
326
|
-
# @return [Object] returns the result of the call.
|
327
|
-
def transact_and_wait(contract, function_name, *args, **kwargs)
|
328
|
-
wait_for_tx(transact(contract, function_name, *args, **kwargs))
|
338
|
+
# See {#transact} for params and overloads.
|
339
|
+
#
|
340
|
+
# @return [Object] returns the result of the transaction.
|
341
|
+
def transact_and_wait(contract, function, *args, **kwargs)
|
342
|
+
wait_for_tx(transact(contract, function, *args, **kwargs))
|
329
343
|
end
|
330
344
|
|
331
345
|
# Provides an interface to call `isValidSignature` as per EIP-1271 on a given
|
@@ -381,7 +395,7 @@ module Eth
|
|
381
395
|
end
|
382
396
|
|
383
397
|
# Metafunction to provide all known RPC commands defined in
|
384
|
-
# Eth::Api as snake_case methods to the Eth::Client classes.
|
398
|
+
# {Eth::Api} as snake_case methods to the {Eth::Client} classes.
|
385
399
|
Api::COMMANDS.each do |cmd|
|
386
400
|
method_name = cmd.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
|
387
401
|
define_method method_name do |*args|
|
@@ -393,7 +407,11 @@ module Eth
|
|
393
407
|
|
394
408
|
# Non-transactional function call called from call().
|
395
409
|
def call_raw(contract, func, *args, **kwargs)
|
396
|
-
gas_limit =
|
410
|
+
gas_limit = if kwargs[:gas_limit]
|
411
|
+
kwargs[:gas_limit]
|
412
|
+
else
|
413
|
+
Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
|
414
|
+
end
|
397
415
|
params = {
|
398
416
|
gas_limit: gas_limit,
|
399
417
|
chain_id: chain_id,
|
@@ -431,7 +449,7 @@ module Eth
|
|
431
449
|
def call_payload(fun, args)
|
432
450
|
types = fun.inputs.map { |i| i.type }
|
433
451
|
encoded_str = Util.bin_to_hex(Eth::Abi.encode(types, args))
|
434
|
-
|
452
|
+
Util.prefix_hex(fun.signature + (encoded_str.empty? ? "0" * 64 : encoded_str))
|
435
453
|
end
|
436
454
|
|
437
455
|
# Encodes constructor params
|
@@ -480,4 +498,5 @@ end
|
|
480
498
|
|
481
499
|
# Load the client/* libraries
|
482
500
|
require "eth/client/http"
|
501
|
+
require "eth/client/http_auth"
|
483
502
|
require "eth/client/ipc"
|
@@ -43,7 +43,7 @@ module Eth
|
|
43
43
|
# @param inputs [Array<Eth::Contract::FunctionInput>] function input class list.
|
44
44
|
# @return [String] function string.
|
45
45
|
def self.calc_signature(name, inputs)
|
46
|
-
"#{name}(#{inputs.collect { |x| x.
|
46
|
+
"#{name}(#{inputs.collect { |x| x.raw_type }.join(",")})"
|
47
47
|
end
|
48
48
|
|
49
49
|
# Encodes a function signature.
|
@@ -19,19 +19,20 @@ module Eth
|
|
19
19
|
|
20
20
|
# Provide classes for contract function input.
|
21
21
|
class Contract::FunctionInput
|
22
|
-
attr_accessor :type, :name
|
22
|
+
attr_accessor :type, :raw_type, :name
|
23
23
|
|
24
24
|
# Constructor of the {Eth::Contract::FunctionInput} class.
|
25
25
|
#
|
26
26
|
# @param data [Hash] contract abi data.
|
27
27
|
def initialize(data)
|
28
|
+
@raw_type = data["type"]
|
28
29
|
@type = Eth::Abi::Type.parse(data["type"])
|
29
30
|
@name = data["name"]
|
30
31
|
end
|
31
32
|
|
32
33
|
# Returns complete types with subtypes, e.g., `uint256`.
|
33
34
|
def type
|
34
|
-
@type.base_type + @type.sub_type
|
35
|
+
@type.base_type + @type.sub_type + @type.dimensions.map { |dimension| "[#{dimension > 0 ? dimension : ""}]" }.join("")
|
35
36
|
end
|
36
37
|
end
|
37
38
|
end
|
@@ -31,7 +31,7 @@ module Eth
|
|
31
31
|
|
32
32
|
# Returns complete types with subtypes, e.g., `uint256`.
|
33
33
|
def type
|
34
|
-
@type.base_type + @type.sub_type
|
34
|
+
@type.base_type + @type.sub_type + @type.dimensions.map { |dimension| "[#{dimension > 0 ? dimension : ""}]" }.join("")
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
data/lib/eth/signature.rb
CHANGED
@@ -38,7 +38,7 @@ module Eth
|
|
38
38
|
# @param message [String] the message string to be prefixed.
|
39
39
|
# @return [String] an EIP-191 prefixed string.
|
40
40
|
def prefix_message(message)
|
41
|
-
"#{EIP191_PREFIX_BYTE}Ethereum Signed Message:\n#{message.
|
41
|
+
"#{EIP191_PREFIX_BYTE}Ethereum Signed Message:\n#{message.bytesize}#{message}"
|
42
42
|
end
|
43
43
|
|
44
44
|
# Dissects a signature blob of 65+ bytes into its `r`, `s`, and `v`
|
@@ -70,7 +70,10 @@ module Eth
|
|
70
70
|
context = Secp256k1::Context.new
|
71
71
|
r, s, v = dissect signature
|
72
72
|
v = v.to_i(16)
|
73
|
-
|
73
|
+
if !Chain.is_ledger? v and !Chain.is_legacy? v
|
74
|
+
min_v = 2 * chain_id + 35
|
75
|
+
raise SignatureError, "Invalid signature v byte #{v} for chain ID #{chain_id}!" if v < min_v
|
76
|
+
end
|
74
77
|
recovery_id = Chain.to_recovery_id v, chain_id
|
75
78
|
signature_rs = Util.hex_to_bin "#{r}#{s}"
|
76
79
|
recoverable_signature = context.recoverable_signature_from_compact signature_rs, recovery_id
|
data/lib/eth/tx/eip1559.rb
CHANGED
@@ -188,7 +188,7 @@ module Eth
|
|
188
188
|
|
189
189
|
# recover sender address
|
190
190
|
v = Chain.to_v recovery_id, chain_id
|
191
|
-
public_key = Signature.recover(unsigned_hash, "#{r}#{s}#{v.to_s(16)}", chain_id)
|
191
|
+
public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v.to_s(16)}", chain_id)
|
192
192
|
address = Util.public_key_to_address(public_key).to_s
|
193
193
|
@sender = Tx.sanitize_address address
|
194
194
|
end
|
data/lib/eth/tx/eip2930.rb
CHANGED
@@ -183,7 +183,7 @@ module Eth
|
|
183
183
|
|
184
184
|
# recover sender address
|
185
185
|
v = Chain.to_v recovery_id, chain_id
|
186
|
-
public_key = Signature.recover(unsigned_hash, "#{r}#{s}#{v.to_s(16)}", chain_id)
|
186
|
+
public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v.to_s(16)}", chain_id)
|
187
187
|
address = Util.public_key_to_address(public_key).to_s
|
188
188
|
@sender = Tx.sanitize_address address
|
189
189
|
end
|
data/lib/eth/tx/legacy.rb
CHANGED
@@ -156,7 +156,7 @@ module Eth
|
|
156
156
|
unless chain_id.nil?
|
157
157
|
|
158
158
|
# recover sender address
|
159
|
-
public_key = Signature.recover(unsigned_hash, "#{r}#{s}#{v}", chain_id)
|
159
|
+
public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v}", chain_id)
|
160
160
|
address = Util.public_key_to_address(public_key).to_s
|
161
161
|
@sender = Tx.sanitize_address address
|
162
162
|
else
|
data/lib/eth/tx.rb
CHANGED
@@ -39,8 +39,11 @@ module Eth
|
|
39
39
|
# The minimum transaction gas limit required for a value transfer.
|
40
40
|
DEFAULT_GAS_LIMIT = 21_000.freeze
|
41
41
|
|
42
|
-
# The "default" transaction
|
43
|
-
|
42
|
+
# The "default" transaction priority fee of 1.01 GWei. Do not use.
|
43
|
+
DEFAULT_PRIORITY_FEE = (1.01 * Unit::GWEI).freeze
|
44
|
+
|
45
|
+
# The "default" transaction gas price of 42.69 GWei. Do not use.
|
46
|
+
DEFAULT_GAS_PRICE = (42.69 * Unit::GWEI).freeze
|
44
47
|
|
45
48
|
# The calldata gas cost of a non-zero byte as per EIP-2028.
|
46
49
|
COST_NON_ZERO_BYTE = 16.freeze
|
@@ -55,7 +58,7 @@ module Eth
|
|
55
58
|
COST_ADDRESS = 2_400.freeze
|
56
59
|
|
57
60
|
# The maximum transaction gas limit is bound by the block gas limit.
|
58
|
-
BLOCK_GAS_LIMIT =
|
61
|
+
BLOCK_GAS_LIMIT = 30_000_000.freeze
|
59
62
|
|
60
63
|
# The legacy transaction type is 0.
|
61
64
|
TYPE_LEGACY = 0x00.freeze
|
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.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Ellis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-
|
12
|
+
date: 2022-10-31 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: keccak
|
@@ -122,6 +122,7 @@ files:
|
|
122
122
|
- lib/eth/chain.rb
|
123
123
|
- lib/eth/client.rb
|
124
124
|
- lib/eth/client/http.rb
|
125
|
+
- lib/eth/client/http_auth.rb
|
125
126
|
- lib/eth/client/ipc.rb
|
126
127
|
- lib/eth/constant.rb
|
127
128
|
- lib/eth/contract.rb
|
@@ -177,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
177
178
|
- !ruby/object:Gem::Version
|
178
179
|
version: '0'
|
179
180
|
requirements: []
|
180
|
-
rubygems_version: 3.3.
|
181
|
+
rubygems_version: 3.3.23
|
181
182
|
signing_key:
|
182
183
|
specification_version: 4
|
183
184
|
summary: Ruby Ethereum library.
|