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
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.
62 63 64 65 66 |
# File 'lib/eth/client.rb', line 62 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.
54 55 56 57 58 |
# File 'lib/eth/client.rb', line 54 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).
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/eth/client.rb', line 254 def call(contract, function, *args, **kwargs) func = contract.functions.select { |func| func.name == function } raise ArgumentError, "this function does not exist!" if func.nil? || func.size === 0 selected_func = func.first func.each do |f| if f.inputs.size === args.size selected_func = f end end output = call_raw(contract, selected_func, *args, **kwargs) if output&.length == 1 output[0] else output end 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.
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/eth/client.rb', line 216 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.
190 191 192 193 194 |
# File 'lib/eth/client.rb', line 190 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.
90 91 92 |
# File 'lib/eth/client.rb', line 90 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.
98 99 100 |
# File 'lib/eth/client.rb', line 98 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.
338 339 340 341 342 343 344 345 |
# File 'lib/eth/client.rb', line 338 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.
351 352 353 |
# File 'lib/eth/client.rb', line 351 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.
108 109 110 111 |
# File 'lib/eth/client.rb', line 108 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.
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/eth/client.rb', line 295 def transact(contract, function, *args, **kwargs) gas_limit = if kwargs[:gas_limit] kwargs[:gas_limit] else Tx.estimate_intrinsic_gas(contract.bin) end fun = contract.functions.select { |func| func.name == function }[0] params = { value: kwargs[:tx_value] || 0, gas_limit: gas_limit, chain_id: chain_id, to: kwargs[:address] || contract.address, data: call_payload(fun, 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.
319 320 321 322 323 324 325 326 |
# File 'lib/eth/client.rb', line 319 def transact_and_wait(contract, function, *args, **kwargs) begin hash = wait_for_tx(transact(contract, function, *args, **kwargs)) return hash, tx_succeeded?(hash) rescue IOError => e raise ContractExecutionError, 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.
141 142 143 144 145 146 147 148 149 |
# File 'lib/eth/client.rb', line 141 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.
120 121 122 |
# File 'lib/eth/client.rb', line 120 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.
179 180 181 182 |
# File 'lib/eth/client.rb', line 179 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.
156 157 158 |
# File 'lib/eth/client.rb', line 156 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.
359 360 361 362 |
# File 'lib/eth/client.rb', line 359 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.
368 369 370 371 |
# File 'lib/eth/client.rb', line 368 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.
378 379 380 381 382 383 384 385 386 387 |
# File 'lib/eth/client.rb', line 378 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 |