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, HTTP/S, or WS/S).
Defined Under Namespace
Classes: ContractExecutionError, Http, Ipc, RpcError, Ws
Instance Attribute Summary collapse
-
#block_number ⇒ Object
The block number used for archive calls.
-
#chain_id ⇒ Integer
readonly
Gets the chain ID of the connected network.
-
#default_account ⇒ Eth::Address
Gets the default account (first account) of the connected client.
-
#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, defaults to Tx::DEFAULT_GAS_PRICE.
-
#max_priority_fee_per_gas ⇒ Object
The default transaction max priority fee per gas in Wei, defaults to Tx::DEFAULT_PRIORITY_FEE.
Class Method Summary collapse
-
.create(host) ⇒ Eth::Client::Ipc, ...
Creates a new RPC-Client, either by providing an HTTP/S host, WS/S host, or an IPC path.
Instance Method Summary collapse
-
#call(contract, function, *args, **kwargs) ⇒ Object
Calls a contract function without executing it (non-transactional contract read).
-
#close ⇒ void
Closes the client and releases any resources held by the underlying connection, such as persistent HTTP connections or an open WebSocket stream.
-
#closed? ⇒ Boolean
Checks whether the client has been closed via #close.
-
#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_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.
-
#resolve_ens(ens_name, registry = Ens::DEFAULT_ADDRESS, coin_type = Ens::CoinType::ETHEREUM) ⇒ Eth::Address
Resolves an ENS name to an Ethereum address on the connected chain.
-
#transact(contract, function, *args, **kwargs) ⇒ Object
Executes a contract function with a transaction (transactional contract read/write).
-
#transact_and_wait(contract, function, *args, **kwargs) ⇒ Object, Boolean
Executes a contract function with a transaction and waits for it to be mined (transactional contract read/write).
-
#transfer(destination, amount, **kwargs) ⇒ String
Simply transfer Ether to an account without any call data or access lists attached.
-
#transfer_and_wait(destination, amount, **kwargs) ⇒ String
Simply transfer Ether to an account and waits for it to be mined.
-
#transfer_erc20(erc20_contract, destination, amount, **kwargs) ⇒ Object
Transfers a token that implements the ERC20
transfer()interface. -
#transfer_erc20_and_wait(erc20_contract, destination, amount, **kwargs) ⇒ Object
Transfers a token that implements the ERC20
transfer()interface. -
#tx_mined?(hash) ⇒ Boolean
Checks whether a transaction is mined or not.
-
#tx_succeeded?(hash) ⇒ Boolean
Checks whether a contract transaction succeeded or not.
-
#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.
82 83 84 85 86 87 |
# File 'lib/eth/client.rb', line 82 def initialize(_) @id = 0 @closed = false @max_priority_fee_per_gas = Tx::DEFAULT_PRIORITY_FEE @max_fee_per_gas = Tx::DEFAULT_GAS_PRICE end |
Instance Attribute Details
#block_number ⇒ Object
The block number used for archive calls.
38 39 40 |
# File 'lib/eth/client.rb', line 38 def block_number @block_number end |
#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 (first account) of the connected client.
Note, that many remote providers (e.g., Infura) do not provide any accounts.
29 30 31 |
# File 'lib/eth/client.rb', line 29 def default_account @default_account 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, defaults to Tx::DEFAULT_GAS_PRICE.
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, defaults to Tx::DEFAULT_PRIORITY_FEE.
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, ...
Creates a new RPC-Client, either by providing an HTTP/S host, WS/S host, or an IPC path. Supports basic authentication with username and password.
Note, this sets the following gas defaults: Tx::DEFAULT_PRIORITY_FEE and Use {#max_priority_fee_per_gas and #max_fee_per_gas to set custom values prior to submitting transactions.
73 74 75 76 77 78 |
# File 'lib/eth/client.rb', line 73 def self.create(host) return Client::Ipc.new host if host.end_with? ".ipc" return Client::Http.new host if host.start_with? "http" return Client::Ws.new host if host.start_with? "ws" raise ArgumentError, "Unable to detect client type!" end |
Instance Method Details
#call(contract, function) ⇒ Object #call(contract, function, *args) ⇒ Object #call(contract, function, *args, **kwargs) ⇒ Object
Calls a contract function without executing it (non-transactional contract read).
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/eth/client.rb', line 276 def call(contract, function, *args, **kwargs) function = contract.function(function, args: args.size) output = function.decode_call_result( eth_call( { data: function.encode_call(*args), to: kwargs[:address] || contract.address, from: kwargs[:from], gas: kwargs[:gas], gasPrice: kwargs[:gas_price], value: kwargs[:value], }.compact )["result"] ) if output&.length == 1 output[0] else output end rescue RpcError => e raise ContractExecutionError, contract.decode_error(e) end |
#close ⇒ void
This method returns an undefined value.
Closes the client and releases any resources held by the underlying
connection, such as persistent HTTP connections or an open WebSocket
stream. Once closed, the client cannot be used anymore; any
subsequent request raises an IOError. Safe to call multiple times.
388 389 390 |
# File 'lib/eth/client.rb', line 388 def close @closed = true end |
#closed? ⇒ Boolean
Checks whether the client has been closed via #close.
395 396 397 |
# File 'lib/eth/client.rb', line 395 def closed? !!@closed end |
#deploy(contract) ⇒ String #deploy(contract, *args) ⇒ String #deploy(contract, *args, **kwargs) ⇒ String
Deploys a contract. Uses eth_accounts or external signer
if no sender key is provided.
Note, that many remote providers (e.g., Infura) do not provide
any accounts. Provide a sender_key: if you experience issues.
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'lib/eth/client.rb', line 237 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, } send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce]) end |
#deploy_and_wait(contract, *args, **kwargs) ⇒ String
Deploys a contract and waits for it to be mined. Uses
eth_accounts or external signer if no sender key is provided.
See #deploy for params and overloads.
211 212 213 214 215 |
# File 'lib/eth/client.rb', line 211 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.
111 112 113 |
# File 'lib/eth/client.rb', line 111 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.
119 120 121 |
# File 'lib/eth/client.rb', line 119 def get_nonce(address) eth_get_transaction_count(address, "pending")["result"].to_i 16 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.
365 366 367 368 369 370 371 372 |
# File 'lib/eth/client.rb', line 365 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.hex? hash signature = Util.hex_to_bin signature if Util.hex? signature magic = Util.hex_to_bin magic if Util.hex? magic result = call(contract, "isValidSignature", hash, signature) result === magic end |
#reset_id ⇒ Integer
Gives control over resetting the RPC request ID back to zero. Usually not needed.
378 379 380 |
# File 'lib/eth/client.rb', line 378 def reset_id @id = 0 end |
#resolve_ens(ens_name, registry = Ens::DEFAULT_ADDRESS, coin_type = Ens::CoinType::ETHEREUM) ⇒ Eth::Address
Resolves an ENS name to an Ethereum address on the connected chain.
129 130 131 132 |
# File 'lib/eth/client.rb', line 129 def resolve_ens(ens_name, registry = Ens::DEFAULT_ADDRESS, coin_type = Ens::CoinType::ETHEREUM) ens = Ens::Resolver.new(self, registry) ens.resolve(ens_name, coin_type) end |
#transact(contract, function) ⇒ Object #transact(contract, function, *args) ⇒ Object #transact(contract, function, *args, **kwargs) ⇒ Object
Executes a contract function with a transaction (transactional contract read/write).
Note, that many remote providers (e.g., Infura) do not provide
any accounts. Provide a sender_key: if you experience issues.
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/eth/client.rb', line 323 def transact(contract, function, *args, **kwargs) gas_limit = if kwargs[:gas_limit] kwargs[:gas_limit] else Tx.estimate_intrinsic_gas(contract.bin) end params = { value: kwargs[:tx_value] || 0, gas_limit: gas_limit, chain_id: chain_id, to: kwargs[:address] || contract.address, data: contract.function(function, args: args.size).encode_call(*args), } send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce]) end |
#transact_and_wait(contract, function, *args, **kwargs) ⇒ Object, Boolean
Executes a contract function with a transaction and waits for it to be mined (transactional contract read/write).
See #transact for params and overloads.
346 347 348 349 350 351 352 353 |
# File 'lib/eth/client.rb', line 346 def transact_and_wait(contract, function, *args, **kwargs) begin hash = wait_for_tx(transact(contract, function, *args, **kwargs)) return hash, tx_succeeded?(hash) rescue RpcError => e raise ContractExecutionError, contract.decode_error(e) end end |
#transfer(destination, amount) ⇒ String #transfer(destination, amount, **kwargs) ⇒ String
Simply transfer Ether to an account without any call data or
access lists attached. Uses eth_accounts and external signer
if no sender key is provided.
Note, that many remote providers (e.g., Infura) do not provide
any accounts. Provide a sender_key: if you experience issues.
162 163 164 165 166 167 168 169 170 |
# File 'lib/eth/client.rb', line 162 def transfer(destination, amount, **kwargs) params = { value: amount, to: destination, gas_limit: Tx::DEFAULT_GAS_LIMIT, chain_id: chain_id, } send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce]) end |
#transfer_and_wait(destination, amount, **kwargs) ⇒ String
Simply transfer Ether to an account and waits for it to be mined.
Uses eth_accounts and external signer if no sender key is
provided.
See #transfer for params and overloads.
141 142 143 |
# File 'lib/eth/client.rb', line 141 def transfer_and_wait(destination, amount, **kwargs) wait_for_tx(transfer(destination, amount, **kwargs)) end |
#transfer_erc20(erc20_contract, destination, amount) ⇒ Object #transfer_erc20(erc20_contract, destination, amount, **kwargs) ⇒ Object
Transfers a token that implements the ERC20 transfer() interface.
Note, that many remote providers (e.g., Infura) do not provide
any accounts. Provide a sender_key: if you experience issues.
200 201 202 203 |
# File 'lib/eth/client.rb', line 200 def transfer_erc20(erc20_contract, destination, amount, **kwargs) destination = destination.to_s if destination.instance_of? Eth::Address transact(erc20_contract, "transfer", destination, amount, **kwargs) end |
#transfer_erc20_and_wait(erc20_contract, destination, amount, **kwargs) ⇒ Object
Transfers a token that implements the ERC20 transfer() interface.
See #transfer_erc20 for params and overloads.
177 178 179 |
# File 'lib/eth/client.rb', line 177 def transfer_erc20_and_wait(erc20_contract, destination, amount, **kwargs) transact_and_wait(erc20_contract, "transfer", destination, amount, **kwargs) end |
#tx_mined?(hash) ⇒ Boolean
Checks whether a transaction is mined or not.
403 404 405 406 |
# File 'lib/eth/client.rb', line 403 def tx_mined?(hash) mined_tx = eth_get_transaction_by_hash hash !mined_tx.nil? && !mined_tx["result"].nil? && !mined_tx["result"]["blockNumber"].nil? end |
#tx_succeeded?(hash) ⇒ Boolean
Checks whether a contract transaction succeeded or not.
412 413 414 415 |
# File 'lib/eth/client.rb', line 412 def tx_succeeded?(hash) tx_receipt = eth_get_transaction_receipt(hash) !tx_receipt.nil? && !tx_receipt["result"].nil? && tx_receipt["result"]["status"] == "0x1" end |
#wait_for_tx(hash) ⇒ String
Waits for an transaction to be mined by the connected chain.
422 423 424 425 426 427 428 429 430 431 |
# File 'lib/eth/client.rb', line 422 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 tx_mined? hash sleep retry_rate end end |