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
Classes: ContractExecutionError, Http, Ipc, RpcError
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, 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, *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_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. -
#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.
77 78 79 80 81 |
# File 'lib/eth/client.rb', line 77 def initialize(_) @id = 0 @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, Eth::Client::Http
Creates a new RPC-Client, either by providing an HTTP/S host or an IPC path. Supports basic authentication with username and password.
Note, this sets the folling 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.
69 70 71 72 73 |
# File 'lib/eth/client.rb', line 69 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) ⇒ Object #call(contract, function, *args) ⇒ Object #call(contract, function, *args, **kwargs) ⇒ Object
Calls a contract function without executing it (non-transactional contract read).
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/eth/client.rb', line 270 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 |
#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.
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/eth/client.rb', line 231 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.
205 206 207 208 209 |
# File 'lib/eth/client.rb', line 205 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.
105 106 107 |
# File 'lib/eth/client.rb', line 105 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.
113 114 115 |
# File 'lib/eth/client.rb', line 113 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.
359 360 361 362 363 364 365 366 |
# File 'lib/eth/client.rb', line 359 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.
372 373 374 |
# File 'lib/eth/client.rb', line 372 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.
123 124 125 126 |
# File 'lib/eth/client.rb', line 123 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.
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/eth/client.rb', line 317 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.
340 341 342 343 344 345 346 347 |
# File 'lib/eth/client.rb', line 340 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.
156 157 158 159 160 161 162 163 164 |
# File 'lib/eth/client.rb', line 156 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.
135 136 137 |
# File 'lib/eth/client.rb', line 135 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.
194 195 196 197 |
# File 'lib/eth/client.rb', line 194 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.
171 172 173 |
# File 'lib/eth/client.rb', line 171 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.
380 381 382 383 |
# File 'lib/eth/client.rb', line 380 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.
389 390 391 392 |
# File 'lib/eth/client.rb', line 389 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.
399 400 401 402 403 404 405 406 407 408 |
# File 'lib/eth/client.rb', line 399 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 |