Class: Eth::Client
- Inherits:
-
Object
- Object
- Eth::Client
- Defined in:
- lib/eth/client.rb
Overview
Provides the Client super-class to connect to Ethereum network's RPC-API endpoints (IPC or HTTP).
Defined Under Namespace
Instance Attribute Summary collapse
-
#chain_id ⇒ Integer
readonly
Gets the chain ID of the connected network.
-
#default_account ⇒ Eth::Address
Gets the default account (coinbase) of the connected client.
-
#gas_limit ⇒ Object
The default gas limit for the transaction.
-
#id ⇒ Object
readonly
The client's RPC-request ID starting at 0.
-
#max_fee_per_gas ⇒ Object
The default transaction max fee per gas in Wei.
-
#max_priority_fee_per_gas ⇒ Object
The default transaction max priority fee per gas in Wei.
Class Method Summary collapse
-
.create(host) ⇒ Eth::Client::Ipc, Eth::Client::Http
Creates a new RPC-Client, either by providing an HTTP/S host or an IPC path.
Instance Method Summary collapse
-
#call(contract, function_name, *args, **kwargs) ⇒ Object
Calls a contract function without executing it (non-transactional contract read).
-
#deploy(contract, *args, **kwargs) ⇒ String
Deploys a contract.
-
#deploy_and_wait(contract, *args, **kwargs) ⇒ String
Deploys a contract and waits for it to be mined.
-
#get_balance(address) ⇒ Integer
Gets the balance for an address.
-
#get_nonce(address) ⇒ Integer
Gets the next nonce for an address used to draft new transactions.
-
#initialize(_) ⇒ Client
constructor
Constructor for the Client super-class.
-
#is_mined_tx?(hash) ⇒ Boolean
Checkes wether a transaction is mined or not.
-
#is_valid_signature(contract, hash, signature, magic = "1626ba7e") ⇒ Boolean
Provides an interface to call
isValidSignatureas per EIP-1271 on a given smart contract to verify the given hash and signature matching the magic value. -
#reset_id ⇒ Integer
Gives control over resetting the RPC request ID back to zero.
-
#transact(contract, function_name, *args, **kwargs) ⇒ Object
Executes a contract function with a transaction (transactional contract read/write).
-
#transact_and_wait(contract, function_name, *args, **kwargs) ⇒ Object
Executes a contract function with a transaction and waits for it to be mined (transactional contract read/write).
-
#transfer(destination, amount, sender_key = nil, legacy = false) ⇒ String
Simply transfer Ether to an account without any call data or access lists attached.
-
#transfer_and_wait(destination, amount, sender_key = nil, legacy = false) ⇒ String
Simply transfer Ether to an account and waits for it to be mined.
-
#wait_for_tx(hash) ⇒ String
Waits for an transaction to be mined by the connected chain.
Constructor Details
#initialize(_) ⇒ Client
Constructor for the Eth::Client super-class. Should not be used; use create intead.
55 56 57 58 59 60 |
# File 'lib/eth/client.rb', line 55 def initialize(_) @id = 0 @max_priority_fee_per_gas = 0 @max_fee_per_gas = Tx::DEFAULT_GAS_PRICE @gas_limit = Tx::DEFAULT_GAS_LIMIT end |
Instance Attribute Details
#chain_id ⇒ Integer (readonly)
Gets the chain ID of the connected network.
26 27 28 |
# File 'lib/eth/client.rb', line 26 def chain_id @chain_id end |
#default_account ⇒ Eth::Address
Gets the default account (coinbase) of the connected client.
29 30 31 |
# File 'lib/eth/client.rb', line 29 def default_account @default_account end |
#gas_limit ⇒ Object
The default gas limit for the transaction.
38 39 40 |
# File 'lib/eth/client.rb', line 38 def gas_limit @gas_limit end |
#id ⇒ Object (readonly)
The client's RPC-request ID starting at 0.
23 24 25 |
# File 'lib/eth/client.rb', line 23 def id @id end |
#max_fee_per_gas ⇒ Object
The default transaction max fee per gas in Wei.
35 36 37 |
# File 'lib/eth/client.rb', line 35 def max_fee_per_gas @max_fee_per_gas end |
#max_priority_fee_per_gas ⇒ Object
The default transaction max priority fee per gas in Wei.
32 33 34 |
# File 'lib/eth/client.rb', line 32 def max_priority_fee_per_gas @max_priority_fee_per_gas end |
Class Method Details
.create(host) ⇒ Eth::Client::Ipc, Eth::Client::Http
Creates a new RPC-Client, either by providing an HTTP/S host or an IPC path.
47 48 49 50 51 |
# File 'lib/eth/client.rb', line 47 def self.create(host) return Client::Ipc.new host if host.end_with? ".ipc" return Client::Http.new host if host.start_with? "http" raise ArgumentError, "Unable to detect client type!" end |
Instance Method Details
#call(contract, function_name) ⇒ Object #call(contract, function_name, value) ⇒ Object #call(contract, function_name, value, sender_key, legacy, gas_limit) ⇒ Object
Calls a contract function without executing it (non-transactional contract read).
248 249 250 251 252 253 254 255 256 257 |
# File 'lib/eth/client.rb', line 248 def call(contract, function_name, *args, **kwargs) func = contract.functions.select { |func| func.name == function_name }[0] raise ArgumentError, "function_name does not exist!" if func.nil? output = call_raw(contract, func, *args, **kwargs) if output&.length == 1 return output[0] else return output end end |
#deploy(contract) ⇒ String #deploy(contract, *args, **kwargs) ⇒ String
Deploys a contract. Uses eth_coinbase or external signer
if no sender key is provided.
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/eth/client.rb', line 183 def deploy(contract, *args, **kwargs) raise ArgumentError, "Cannot deploy contract without source or binary!" if contract.bin.nil? raise ArgumentError, "Missing contract constructor params!" if contract.constructor_inputs.length != args.length data = contract.bin unless args.empty? data += encode_constructor_params(contract, args) end gas_limit = if kwargs[:gas_limit] kwargs[:gas_limit] else Tx.estimate_intrinsic_gas(data) + Tx::CREATE_GAS end params = { value: 0, gas_limit: gas_limit, chain_id: chain_id, data: data, } if kwargs[:legacy] params.merge!({ gas_price: max_fee_per_gas, }) else params.merge!({ priority_fee: max_priority_fee_per_gas, max_gas_fee: max_fee_per_gas, }) end unless kwargs[:sender_key].nil? # Uses the provided key as sender and signer params.merge!({ from: kwargs[:sender_key].address, nonce: get_nonce(kwargs[:sender_key].address), }) tx = Eth::Tx.new(params) tx.sign kwargs[:sender_key] return eth_send_raw_transaction(tx.hex)["result"] else # Uses the default account as sender and external signer params.merge!({ from: default_account, nonce: get_nonce(default_account), }) return eth_send_transaction(params)["result"] end end |
#deploy(contract) ⇒ String #deploy(contract, *args, **kwargs) ⇒ String
Deploys a contract and waits for it to be mined. Uses
eth_coinbase or external signer if no sender key is provided.
164 165 166 167 168 |
# File 'lib/eth/client.rb', line 164 def deploy_and_wait(contract, *args, **kwargs) hash = wait_for_tx(deploy(contract, *args, **kwargs)) addr = eth_get_transaction_receipt(hash)["result"]["contractAddress"] contract.address = Address.new(addr).to_s end |
#get_balance(address) ⇒ Integer
Gets the balance for an address.
80 81 82 |
# File 'lib/eth/client.rb', line 80 def get_balance(address) eth_get_balance(address)["result"].to_i 16 end |
#get_nonce(address) ⇒ Integer
Gets the next nonce for an address used to draft new transactions.
88 89 90 |
# File 'lib/eth/client.rb', line 88 def get_nonce(address) eth_get_transaction_count(address, "pending")["result"].to_i 16 end |
#is_mined_tx?(hash) ⇒ Boolean
Checkes wether a transaction is mined or not.
374 375 376 377 |
# File 'lib/eth/client.rb', line 374 def is_mined_tx?(hash) mined_tx = eth_get_transaction_by_hash hash !mined_tx.nil? && !mined_tx["result"].nil? && !mined_tx["result"]["blockNumber"].nil? end |
#is_valid_signature(contract, hash, signature, magic = "1626ba7e") ⇒ Boolean
Provides an interface to call isValidSignature as per EIP-1271 on a given
smart contract to verify the given hash and signature matching the magic
value.
353 354 355 356 357 358 359 360 |
# File 'lib/eth/client.rb', line 353 def is_valid_signature(contract, hash, signature, magic = "1626ba7e") raise ArgumentError, "Contract not deployed yet." if contract.address.nil? hash = Util.hex_to_bin hash if Util.is_hex? hash signature = Util.hex_to_bin signature if Util.is_hex? signature magic = Util.hex_to_bin magic if Util.is_hex? magic result = call(contract, "isValidSignature", hash, signature) return result === magic end |
#reset_id ⇒ Integer
Gives control over resetting the RPC request ID back to zero. Usually not needed.
366 367 368 |
# File 'lib/eth/client.rb', line 366 def reset_id @id = 0 end |
#transact(contract, function_name) ⇒ Object #transact(contract, function_name, value) ⇒ Object #transact(contract, function_name, value, sender_key, legacy, address, gas_limit) ⇒ Object
Executes a contract function with a transaction (transactional contract read/write).
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/eth/client.rb', line 278 def transact(contract, function_name, *args, **kwargs) gas_limit = if kwargs[:gas_limit] kwargs[:gas_limit] else Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS end fun = contract.functions.select { |func| func.name == function_name }[0] params = { value: 0, gas_limit: gas_limit, chain_id: chain_id, to: kwargs[:address] || contract.address, data: call_payload(fun, args), } if kwargs[:legacy] params.merge!({ gas_price: max_fee_per_gas, }) else params.merge!({ priority_fee: max_priority_fee_per_gas, max_gas_fee: max_fee_per_gas, }) end unless kwargs[:sender_key].nil? # use the provided key as sender and signer params.merge!({ from: kwargs[:sender_key].address, nonce: get_nonce(kwargs[:sender_key].address), }) tx = Eth::Tx.new(params) tx.sign kwargs[:sender_key] return eth_send_raw_transaction(tx.hex)["result"] else # use the default account as sender and external signer params.merge!({ from: default_account, nonce: get_nonce(default_account), }) return eth_send_transaction(params)["result"] end end |
#transact_and_wait(contract, function_name) ⇒ Object #transact_and_wait(contract, function_name, value) ⇒ Object #transact_and_wait(contract, function_name, value, sender_key, legacy, address) ⇒ Object
Executes a contract function with a transaction and waits for it to be mined (transactional contract read/write).
339 340 341 |
# File 'lib/eth/client.rb', line 339 def transact_and_wait(contract, function_name, *args, **kwargs) wait_for_tx(transact(contract, function_name, *args, **kwargs)) end |
#transfer(destination, amount, sender_key = nil, legacy = false) ⇒ String
Simply transfer Ether to an account without any call data or
access lists attached. Uses eth_coinbase and external signer
if no sender key is provided.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/eth/client.rb', line 114 def transfer(destination, amount, sender_key = nil, legacy = false) params = { value: amount, to: destination, gas_limit: gas_limit, chain_id: chain_id, } if legacy params.merge!({ gas_price: max_fee_per_gas, }) else params.merge!({ priority_fee: max_priority_fee_per_gas, max_gas_fee: max_fee_per_gas, }) end unless sender_key.nil? # use the provided key as sender and signer params.merge!({ from: sender_key.address, nonce: get_nonce(sender_key.address), }) tx = Eth::Tx.new(params) tx.sign sender_key return eth_send_raw_transaction(tx.hex)["result"] else # use the default account as sender and external signer params.merge!({ from: default_account, nonce: get_nonce(default_account), }) return eth_send_transaction(params)["result"] end end |
#transfer_and_wait(destination, amount, sender_key = nil, legacy = false) ⇒ String
Simply transfer Ether to an account and waits for it to be mined.
Uses eth_coinbase and external signer if no sender key is
provided.
101 102 103 |
# File 'lib/eth/client.rb', line 101 def transfer_and_wait(destination, amount, sender_key = nil, legacy = false) wait_for_tx(transfer(destination, amount, sender_key, legacy)) end |
#wait_for_tx(hash) ⇒ String
Waits for an transaction to be mined by the connected chain.
384 385 386 387 388 389 390 391 392 393 |
# File 'lib/eth/client.rb', line 384 def wait_for_tx(hash) start_time = Time.now timeout = 300 retry_rate = 0.1 loop do raise Timeout::Error if ((Time.now - start_time) > timeout) return hash if is_mined_tx? hash sleep retry_rate end end |