Class: BlockGiven::Wallet

Inherits:
Object
  • Object
show all
Defined in:
lib/block_given/wallet.rb

Overview

A private key + (optional) client, able to sign and send transactions (viem's WalletClient + Account).

wallet = BlockGiven::Wallet.new(private_key: ENV["PRIVATE_KEY"])
wallet.address
wallet.send_transaction(to: "0x...", value: BlockGiven::Utils.parse_ether("0.01")).wait

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(private_key:, client: nil) ⇒ Wallet

Returns a new instance of Wallet.



17
18
19
20
21
22
23
24
# File 'lib/block_given/wallet.rb', line 17

def initialize(private_key:, client: nil)
  hex = Utils.strip_hex(private_key.to_s.strip)
  raise InvalidArgumentError, "private key must be 32 bytes hex" unless hex.match?(/\A[0-9a-fA-F]{64}\z/)

  @key = Eth::Key.new(priv: hex)
  @address = @key.address.checksummed
  @client = client
end

Instance Attribute Details

#addressObject (readonly)

Returns the value of attribute address.



11
12
13
# File 'lib/block_given/wallet.rb', line 11

def address
  @address
end

Class Method Details

.generate(client: nil) ⇒ Object



13
14
15
# File 'lib/block_given/wallet.rb', line 13

def self.generate(client: nil)
  new(private_key: Eth::Key.new.private_hex, client: client)
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



93
# File 'lib/block_given/wallet.rb', line 93

def ==(other) = other.is_a?(Wallet) && other.address == address

#balance(block: :latest) ⇒ Object



33
# File 'lib/block_given/wallet.rb', line 33

def balance(block: :latest) = client.get_balance(address, block: block)

#chainObject



31
# File 'lib/block_given/wallet.rb', line 31

def chain = client.chain

#clientObject



29
# File 'lib/block_given/wallet.rb', line 29

def client = @client || BlockGiven.client

#hashObject



95
# File 'lib/block_given/wallet.rb', line 95

def hash = address.hash

#inspectObject



97
# File 'lib/block_given/wallet.rb', line 97

def inspect = "#<BlockGiven::Wallet #{address}>"

#nonce(block: :pending) ⇒ Object



34
# File 'lib/block_given/wallet.rb', line 34

def nonce(block: :pending) = client.get_transaction_count(address, block: block)

#prepare_transaction(to: nil, value: 0, data: nil, gas: nil, nonce: nil, max_fee_per_gas: nil, max_priority_fee_per_gas: nil, gas_price: nil, chain_id: nil, access_list: nil) ⇒ Object

Resolves every missing field (nonce, gas, fees, chain id) without signing.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/block_given/wallet.rb', line 64

def prepare_transaction(to: nil, value: 0, data: nil, gas: nil, nonce: nil, max_fee_per_gas: nil,
                        max_priority_fee_per_gas: nil, gas_price: nil, chain_id: nil, access_list: nil)
  to = Utils.checksum_address(to) if to
  value = coerce_wei(value)
  data = data.nil? || data.empty? ? "" : Utils.prefix_hex(data)

  params = {
    from: address, to: to, value: value, data: data,
    chain_id: chain_id || client.chain.id,
    nonce: nonce || self.nonce,
    gas: gas || estimate_gas(to: to, data: data, value: value),
    access_list: access_list
  }

  if gas_price
    params[:gas_price] = gas_price
  else
    fees = if max_fee_per_gas && max_priority_fee_per_gas
             { max_fee_per_gas: max_fee_per_gas, max_priority_fee_per_gas: max_priority_fee_per_gas }
           else
             estimated = client.estimate_fees_per_gas
             { max_fee_per_gas: max_fee_per_gas || estimated[:max_fee_per_gas],
               max_priority_fee_per_gas: max_priority_fee_per_gas || estimated[:max_priority_fee_per_gas] }
           end
    params.merge!(fees)
  end
  params
end

#private_keyObject



26
# File 'lib/block_given/wallet.rb', line 26

def private_key = Utils.prefix_hex(@key.private_hex)

#public_keyObject



27
# File 'lib/block_given/wallet.rb', line 27

def public_key = Utils.prefix_hex(@key.public_hex)

#send_transaction(**params) ⇒ Object

Signs and broadcasts. Returns a BlockGiven::Transaction.



61
# File 'lib/block_given/wallet.rb', line 61

def send_transaction(**params) = signed_transaction(**params).broadcast

#sign_message(message) ⇒ Object

EIP-191 personal_sign. Returns the 65-byte signature as hex.



37
38
39
# File 'lib/block_given/wallet.rb', line 37

def sign_message(message)
  Utils.prefix_hex(@key.personal_sign(message))
end

#sign_transaction(**params) ⇒ Object

Signs and returns the raw tx hex.



58
# File 'lib/block_given/wallet.rb', line 58

def sign_transaction(**params) = signed_transaction(**params).raw

#sign_typed_data(typed_data) ⇒ Object

EIP-712 typed data (Hash with :types, :primaryType, :domain, :message).



42
43
44
# File 'lib/block_given/wallet.rb', line 42

def sign_typed_data(typed_data)
  Utils.prefix_hex(@key.sign_typed_data(typed_data))
end

#signed_transaction(**params) ⇒ Object

Fills in nonce / gas / fees from the client and signs, without broadcasting. Returns a BlockGiven::SignedTransaction: its #hash and #nonce are known before any network call (persist them, then #broadcast). Same keywords as prepare_transaction; pass gas_price: to build a legacy (type 0) transaction instead of EIP-1559.



50
51
52
53
54
55
# File 'lib/block_given/wallet.rb', line 50

def signed_transaction(**params)
  prepared = prepare_transaction(**params)
  tx = Eth::Tx.new(to_eth_tx_params(prepared))
  tx.sign(@key)
  SignedTransaction.new(raw: Utils.prefix_hex(tx.hex), params: prepared, wallet: self)
end

#to_sObject



96
# File 'lib/block_given/wallet.rb', line 96

def to_s = address

#with_client(client) ⇒ Object



30
# File 'lib/block_given/wallet.rb', line 30

def with_client(client) = self.class.new(private_key: private_key, client: client)