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, defaults to Tx::DEFAULT_GAS_LIMIT.
-
#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 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_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, *args, **kwargs) ⇒ Object
Executes a contract function with a transaction (transactional contract read/write).
-
#transact_and_wait(contract, function, *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.
62 63 64 65 66 67 |
# 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 @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.
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 |
#gas_limit ⇒ Object
The default gas limit for the transaction, defaults to Tx::DEFAULT_GAS_LIMIT.
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, 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 or an IPC path. Supports basic authentication with username and password.
Note, this sets the folling gas defaults: Tx::DEFAULT_PRIORITY_FEE, Tx::DEFAULT_GAS_PRICE, and Tx::DEFAULT_GAS_LIMIT. Use #max_priority_fee_per_gas, #max_fee_per_gas, and #gas_limit to set custom values prior to submitting transactions.
53 54 55 56 57 58 |
# File 'lib/eth/client.rb', line 53 def self.create(host) return Client::Ipc.new host if host.end_with? ".ipc" return Client::HttpAuth.new host if Regexp.new(":.*@.*:", Regexp::IGNORECASE).match host 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).
259 260 261 262 263 264 265 266 267 268 |
# File 'lib/eth/client.rb', line 259 def call(contract, function, *args, **kwargs) func = contract.functions.select { |func| func.name == function }[0] raise ArgumentError, "this function 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) ⇒ String #deploy(contract, *args, **kwargs) ⇒ String
Deploys a contract. Uses eth_coinbase 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.
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 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/eth/client.rb', line 194 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_and_wait(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.
See #deploy for params and overloads.
169 170 171 172 173 |
# File 'lib/eth/client.rb', line 169 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_mined_tx?(hash) ⇒ Boolean
Checkes wether a transaction is mined or not.
376 377 378 379 |
# File 'lib/eth/client.rb', line 376 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.
355 356 357 358 359 360 361 362 |
# File 'lib/eth/client.rb', line 355 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.
368 369 370 |
# File 'lib/eth/client.rb', line 368 def reset_id @id = 0 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.
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 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/eth/client.rb', line 292 def transact(contract, function, *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 }[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, *args, **kwargs) ⇒ Object
Executes a contract function with a transaction and waits for it to be mined (transactional contract read/write).
See #transact for params and overloads.
341 342 343 |
# File 'lib/eth/client.rb', line 341 def transact_and_wait(contract, function, *args, **kwargs) wait_for_tx(transact(contract, function, *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.
Note, that many remote providers (e.g., Infura) do not provide
any accounts. Provide a sender_key if you experience issues.
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 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/eth/client.rb', line 125 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.
See #transfer for params and overloads.
109 110 111 |
# File 'lib/eth/client.rb', line 109 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.
386 387 388 389 390 391 392 393 394 395 |
# File 'lib/eth/client.rb', line 386 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 |