eth-rb 0.1.6 → 0.1.8

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/eth.rb +74 -6
  3. metadata +6 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c74984527b36ab6b3d780197b3294c027412b15bc6cf26642bc48ea21f4fa29a
4
- data.tar.gz: ca0bb0771f0a5056e4afad1991ec607d3b62d6a840e3891f60d3907ec13ec667
3
+ metadata.gz: 7cad4ab46c86f240e38bf48d77832f0c067582133d1bbdb4e1690cd5b52afa77
4
+ data.tar.gz: 54bafb01cd2fa6c8cf5dbbd50ec9e1831de9b1aee9dbc2a1b8c6a96eb59a113c
5
5
  SHA512:
6
- metadata.gz: 61c2952036e1288e66e920e9cadbd63dc2bed13ab774c89e0d8acd845663f1543feb79c3046d7cc74dad5c0d208d4d748ebcc0e54f09e516e11d2db899a0cc07
7
- data.tar.gz: 979726311cdbcb82b68f9587ff726c49e30cf73597902b8404fca61a0c14572fe2b0eadcfc4aadcdfa3993dc3475d0b50ddf042bd2cf3abeddfc0bc7ead34578
6
+ metadata.gz: 8c851f58edc21271766a2e9da90a14a47b90cde4ef48bcd9d99f0a178e7c4c5b9b74f3776e8c6660d5b477d38690c288ccff584c3839e27549ab6fc51c72dcde
7
+ data.tar.gz: ccc8efb8c5b74378088eca855d613320049a5abdaa7efe52f20847fb784928a653399a5b0f0ffce2f000afb884e0acb74709255b2e35730dfcd8892a4cf5a4ff
data/lib/eth.rb CHANGED
@@ -6,10 +6,13 @@
6
6
  # Usage:
7
7
  # ./eth.rb — show latest block number
8
8
  # ./eth.rb <address> — show ETH balance of <address>
9
+ # ./eth.rb <private_key> — derive public key + address, show its balance
9
10
  # ./eth.rb --price — show current ETH/USD price
11
+ # ./eth.rb --genkey or -g — generate a new private key + address
12
+ # ./eth.rb --pubkey <key> — derive public key + address from <key>
10
13
  # ./eth.rb --version or -v — show version
11
- # ./eth.rb send <key> <to> <amount> — send ETH (amount in ETH)
12
- # ./eth.rb send <key> <to> \$<amount> — send ETH (amount in USD, converted at current price)
14
+ # ./eth.rb --send <key> <to> <amount> — send ETH (amount in ETH)
15
+ # ./eth.rb --send <key> <to> \$<amount> — send ETH (amount in USD, converted at current price)
13
16
  # ./eth.rb --help — show this usage info
14
17
 
15
18
  require "net/http"
@@ -22,7 +25,7 @@ require "digest/keccak"
22
25
  ALCHEMY_URL = "https://eth-mainnet.g.alchemy.com/v2/alch_PhpVkmsabZhYV69otj1rF"
23
26
  COINGECKO_URL = "https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd"
24
27
  CHAIN_ID = 1 # Ethereum mainnet
25
- VERSION = "0.1.6"
28
+ VERSION = "0.1.8"
26
29
 
27
30
  # ── JSON-RPC ────────────────────────────────────────────────────────────────
28
31
 
@@ -144,12 +147,52 @@ def private_key_to_address(priv_hex)
144
147
  hash.byteslice(12, 20)
145
148
  end
146
149
 
150
+ # Derive the uncompressed public key (04 || x || y, 64 bytes) from a
151
+ # private key hex string (with or without 0x)
152
+ def private_key_to_pubkey(priv_hex)
153
+ priv_int = normalize_hex(priv_hex).to_i(16)
154
+ pub_point = GROUP.generator.multiply_by_scalar(priv_int)
155
+
156
+ pad32 = ->(b) { b.bytesize >= 32 ? b : "\x00" * (32 - b.bytesize) + b }
157
+ "\x04" + pad32.call(to_big_endian(pub_point.x)) + pad32.call(to_big_endian(pub_point.y))
158
+ end
159
+
147
160
  def normalize_hex(str)
148
161
  hex = str.start_with?("0x") || str.start_with?("0X") ? str[2..] : str
149
162
  hex = "0#{hex}" if hex.bytesize.odd?
150
163
  hex
151
164
  end
152
165
 
166
+ # Generate a random private key: a scalar in [1, n-1] where n is the
167
+ # order of the secp256k1 curve group
168
+ def generate_private_key
169
+ SecureRandom.random_number(GROUP.order - 1) + 1
170
+ end
171
+
172
+ def show_new_key
173
+ priv = generate_private_key
174
+ priv_hex = "0x%064x" % priv
175
+ addr = "0x#{private_key_to_address(priv_hex).unpack1('H*')}"
176
+ puts "Private Key: #{priv_hex}"
177
+ puts "Address: #{addr}"
178
+ end
179
+
180
+ def cmd_pubkey(args)
181
+ if args.empty?
182
+ puts "Usage: ./eth.rb --pubkey <private_key_hex>"
183
+ exit 1
184
+ end
185
+
186
+ priv_hex = normalize_hex(args[0])
187
+ priv = priv_hex.to_i(16)
188
+ pub_hex = "0x#{private_key_to_pubkey(args[0]).unpack1('H*')}"
189
+ addr = "0x#{private_key_to_address(args[0]).unpack1('H*')}"
190
+
191
+ puts "Private Key: 0x%064x" % priv
192
+ puts "Public Key: #{pub_hex}"
193
+ puts "Address: #{addr}"
194
+ end
195
+
153
196
  # ── Transaction signing ─────────────────────────────────────────────────────
154
197
 
155
198
  def build_and_sign_tx(private_key_hex, to_address, amount_wei)
@@ -231,7 +274,7 @@ end
231
274
  def cmd_send(args)
232
275
  # p args
233
276
  if args.length < 3
234
- puts "Usage: ./eth.rb send <private_key_hex> <to_address> <amount>"
277
+ puts "Usage: ./eth.rb --send <private_key_hex> <to_address> <amount>"
235
278
  puts " amount: number (ETH) or $number (USD, converted to ETH at current price)"
236
279
  exit 1
237
280
  end
@@ -272,6 +315,22 @@ def show_block_number
272
315
  puts "Hex: #{hex_block}"
273
316
  end
274
317
 
318
+ # True if str is a 64-hex-digit private key (0x prefix optional)
319
+ def private_key?(str)
320
+ normalize_hex(str).match?(/\A[0-9a-fA-F]{64}\z/)
321
+ end
322
+
323
+ # Derive the public key and address from a private key, then show the balance
324
+ def show_balance_from_key(priv_hex)
325
+ pub_hex = "0x#{private_key_to_pubkey(priv_hex).unpack1('H*')}"
326
+ addr = "0x#{private_key_to_address(priv_hex).unpack1('H*')}"
327
+
328
+ puts "Public Key: #{pub_hex}"
329
+ puts "Address: #{addr}"
330
+ puts ""
331
+ show_balance(addr)
332
+ end
333
+
275
334
  def show_balance(address)
276
335
  hex_wei = rpc_call("eth_getBalance", [address, "latest"])
277
336
  wei = hex_wei.to_i(16)
@@ -323,6 +382,7 @@ def show_version
323
382
  end
324
383
 
325
384
  def print_usage
385
+ puts "eth.rb v#{VERSION}"
326
386
  puts File.read(__FILE__)[/^#\sUsage:.*?(?=\n\n)/m]
327
387
  end
328
388
 
@@ -333,14 +393,22 @@ def run_cli(args)
333
393
  show_block_number
334
394
  elsif args[0] == "--price"
335
395
  show_price
396
+ elsif args[0] == "--genkey" || args[0] == "-g"
397
+ show_new_key
398
+ elsif args[0] == "--pubkey" || args[0] == "pubkey"
399
+ cmd_pubkey(args[1..])
336
400
  elsif args[0] == "--version" || args[0] == "-v"
337
401
  show_version
338
402
  elsif args[0] == "--help" || args[0] == "-h"
339
403
  print_usage
340
- elsif args[0] == "send"
404
+ elsif args[0] == "--send" || args[0] == "send"
341
405
  cmd_send(args[1..])
342
406
  else
343
- show_balance(args[0])
407
+ if private_key?(args[0])
408
+ show_balance_from_key(args[0])
409
+ else
410
+ show_balance(args[0])
411
+ end
344
412
  end
345
413
  end
346
414
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eth-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Khalid Shaikh
@@ -37,8 +37,9 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.3'
40
- description: A lightweight CLI tool for querying Ethereum balances, block numbers,
41
- ETH/USD prices, and sending transactions via Alchemy JSON-RPC.
40
+ description: A lightweight CLI tool for generating Ethereum private keys and addresses,
41
+ querying balances, block numbers, and ETH/USD prices, and sending transactions via
42
+ Alchemy JSON-RPC.
42
43
  email:
43
44
  - k@iai.lol
44
45
  executables:
@@ -68,5 +69,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
69
  requirements: []
69
70
  rubygems_version: 4.0.17
70
71
  specification_version: 4
71
- summary: Ethereum blockchain queries via Alchemy JSON-RPC
72
+ summary: 'Ethereum CLI: balances, prices, key generation, and transactions via Alchemy
73
+ JSON-RPC'
72
74
  test_files: []