class Eth::Client
network’s RPC-API endpoints (IPC or HTTP).
Provides the {Eth::Client} super-class to connect to Ethereum
def self.create(host)
-
(ArgumentError)
- in case it cannot determine the client type.
Returns:
-
(Eth::Client::Http)
- an HTTP client. -
(Eth::Client::Ipc)
- an IPC client.
Parameters:
-
host
(String
) -- either an HTTP/S host or an IPC path.
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
def call(contract, function_name, *args, **kwargs)
-
(Object)
- returns the result of the call.
Parameters:
-
legacy
(Boolean
) -- enables legacy transactions (pre-EIP-1559). -
sender_key
(Eth::Key
) -- the sender private key. -
value
(Integer|String
) -- function arguments. -
function_name
(String
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call. -
value
(Integer|String
) -- function arguments. -
function_name
(String
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call. -
function_name
(String
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call.
Overloads:
-
call(contract, function_name, value, sender_key, legacy)
-
call(contract, function_name, value)
-
call(contract, function_name)
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
def call_payload(fun, args)
def call_payload(fun, args) types = fun.inputs.map { |i| i.type } encoded_str = Util.bin_to_hex(Eth::Abi.encode(types, args)) "0x" + fun.signature + (encoded_str.empty? ? "0" * 64 : encoded_str) end
def call_raw(contract, func, *args, **kwargs)
-
(Object)
- returns the result of the call.
Parameters:
-
legacy
(Boolean
) -- enables legacy transactions (pre-EIP-1559). -
sender_key
(Eth::Key
) -- the sender private key. -
value
(Integer|String
) -- function arguments. -
func
(Eth::Contract::Function
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call. -
value
(Integer|String
) -- function arguments. -
func
(Eth::Contract::Function
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call. -
func
(Eth::Contract::Function
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call.
Overloads:
-
call_raw(contract, func, value, sender_key, legacy)
-
call_raw(contract, func, value)
-
call_raw(contract, func)
def call_raw(contract, func, *args, **kwargs) gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS params = { gas_limit: gas_limit, chain_id: chain_id, data: call_payload(func, args), } if kwargs[:address] || contract.address params.merge!({ to: kwargs[:address] || contract.address }) end 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] else # use the default account as sender and external signer params.merge!({ from: default_account, nonce: get_nonce(default_account), }) end raw_result = eth_call(params)["result"] types = func.outputs.map { |i| i.type } Eth::Abi.decode(types, raw_result) end
def chain_id
-
(Integer)
- the chain ID.
def chain_id @chain_id ||= eth_chain_id["result"].to_i 16 end
def default_account
-
(Eth::Address)
- the coinbase account address.
def default_account @default_account ||= Address.new eth_coinbase["result"] end
def deploy(contract, sender_key: nil, legacy: false)
-
(String)
- the transaction hash.
Parameters:
-
legacy
(Boolean
) -- enables legacy transactions (pre-EIP-1559). -
sender_key
(Eth::Key
) -- the sender private key. -
contract
(Eth::Contract
) -- contracts to deploy. -
sender_key
(Eth::Key
) -- the sender private key. -
contract
(Eth::Contract
) -- contracts to deploy. -
contract
(Eth::Contract
) -- contracts to deploy.
Overloads:
-
deploy(contract, sender_key, legacy)
-
deploy(contract, sender_key)
-
deploy(contract)
def deploy(contract, sender_key: nil, legacy: false) gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS params = { value: 0, gas_limit: gas_limit, chain_id: chain_id, data: contract.bin, } 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
def deploy_and_wait(contract, sender_key: nil, legacy: false)
-
(String)
- the contract address.
Parameters:
-
legacy
(Boolean
) -- enables legacy transactions (pre-EIP-1559). -
sender_key
(Eth::Key
) -- the sender private key. -
contract
(Eth::Contract
) -- contracts to deploy. -
sender_key
(Eth::Key
) -- the sender private key. -
contract
(Eth::Contract
) -- contracts to deploy. -
contract
(Eth::Contract
) -- contracts to deploy.
Overloads:
-
deploy(contract, sender_key, legacy)
-
deploy(contract, sender_key)
-
deploy(contract)
def deploy_and_wait(contract, sender_key: nil, legacy: false) hash = wait_for_tx(deploy(contract, sender_key: sender_key, legacy: legacy)) contract.address = eth_get_transaction_receipt(hash)["result"]["contractAddress"] end
def get_balance(address)
-
(Integer)
- the balance in Wei.
Parameters:
-
address
(Eth::Address
) -- the address to get the balance for.
def get_balance(address) eth_get_balance(address)["result"].to_i 16 end
def get_nonce(address)
-
(Integer)
- the next nonce to be used.
Parameters:
-
address
(Eth::Address
) -- the address to get the nonce for.
def get_nonce(address) eth_get_transaction_count(address, "pending")["result"].to_i 16 end
def initialize(_)
Constructor for the {Eth::Client} super-class. Should not be used;
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
def is_mined_tx?(hash)
-
(Boolean)
- true if included in a block.
Parameters:
-
hash
(String
) -- the transaction hash.
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
def marshal(params)
def marshal(params) if params.is_a? Array return params.map! { |param| marshal(param) } elsif params.is_a? Hash return params.transform_values! { |param| marshal(param) } elsif params.is_a? Numeric return Util.prefix_hex "#{params.to_i.to_s(16)}" elsif params.is_a? Address return params.to_s elsif Util.is_hex? params return Util.prefix_hex params else return params end end
def next_id
def next_id @id += 1 end
def reset_id
-
(Integer)
- 0
def reset_id @id = 0 end
def send_command(command, args)
def send_command(command, args) args << "latest" if ["eth_getBalance", "eth_call"].include? command payload = { jsonrpc: "2.0", method: command, params: marshal(args), id: next_id, } output = JSON.parse(send(payload.to_json)) raise IOError, output["error"]["message"] unless output["error"].nil? return output end
def transact(contract, function_name, *args, **kwargs)
-
(Object)
- returns the result of the call.
Parameters:
-
address
(String
) -- contract address. -
legacy
(Boolean
) -- enables legacy transactions (pre-EIP-1559). -
sender_key
(Eth::Key
) -- the sender private key. -
value
(Integer|String
) -- function arguments. -
function_name
(String
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call. -
value
(Integer|String
) -- function arguments. -
function_name
(String
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call. -
function_name
(String
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call.
Overloads:
-
transact(contract, function_name, value, sender_key, legacy, address)
-
transact(contract, function_name, value)
-
transact(contract, function_name)
def transact(contract, function_name, *args, **kwargs) gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS 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
def transact_and_wait(contract, function_name, *args, **kwargs)
-
(Object)
- returns the result of the call.
Parameters:
-
address
(String
) -- contract address. -
legacy
(Boolean
) -- enables legacy transactions (pre-EIP-1559). -
sender_key
(Eth::Key
) -- the sender private key. -
value
(Integer|String
) -- function arguments. -
function_name
(String
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call. -
value
(Integer|String
) -- function arguments. -
function_name
(String
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call. -
function_name
(String
) -- method name to be called. -
contract
(Eth::Contract
) -- subject contract to call.
Overloads:
-
transact_and_wait(contract, function_name, value, sender_key, legacy, address)
-
transact_and_wait(contract, function_name, value)
-
transact_and_wait(contract, function_name)
def transact_and_wait(contract, function_name, *args, **kwargs) wait_for_tx(transact(contract, function_name, *args, **kwargs)) end
def transfer(destination, amount, sender_key = nil, legacy = false)
-
(String)
- the transaction hash.
Parameters:
-
legacy
(Boolean
) -- enables legacy transactions (pre-EIP-1559). -
sender_key
(Eth::Key
) -- the sender private key. -
amount
(Integer
) -- the transfer amount in Wei. -
destination
(Eth::Address
) -- the destination address.
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
def transfer_and_wait(destination, amount, sender_key = nil, legacy = false)
-
(String)
- the transaction hash.
Parameters:
-
legacy
(Boolean
) -- enables legacy transactions (pre-EIP-1559). -
sender_key
(Eth::Key
) -- the sender private key. -
amount
(Integer
) -- the transfer amount in Wei. -
destination
(Eth::Address
) -- the destination address.
def transfer_and_wait(destination, amount, sender_key = nil, legacy = false) wait_for_tx(transfer(destination, amount, sender_key, legacy)) end
def wait_for_tx(hash)
-
(Timeout::Error)
- if it's not mined within 5 minutes.
Returns:
-
(String)
- the transactin hash once the transaction is mined.
Parameters:
-
hash
(String
) -- the transaction hash.
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