eth 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/eth/api.rb ADDED
@@ -0,0 +1,223 @@
1
+ # Copyright (c) 2016-2022 The Ruby-Eth Contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Provides the {Eth} module.
16
+ module Eth
17
+
18
+ # Provides the `Eth::Api` module grouping known RPC commands.
19
+ module Api
20
+
21
+ # Implements the available RPC-APIs provided by Geth version 1.10.15.
22
+ COMMANDS = [
23
+ "admin_addPeer",
24
+ "admin_addTrustedPeer",
25
+ "admin_clearHistory",
26
+ "admin_datadir",
27
+ "admin_exportChain",
28
+ "admin_getDatadir",
29
+ "admin_getNodeInfo",
30
+ "admin_getPeers",
31
+ "admin_importChain",
32
+ "admin_nodeInfo",
33
+ "admin_peers",
34
+ "admin_removePeer",
35
+ "admin_removeTrustedPeer",
36
+ "admin_sleep",
37
+ "admin_sleepBlocks",
38
+ "admin_startHTTP",
39
+ "admin_startRPC",
40
+ "admin_startWS",
41
+ "admin_stopHTTP",
42
+ "admin_stopRPC",
43
+ "admin_stopWS",
44
+ "clique_discard",
45
+ "clique_getProposals",
46
+ "clique_getSigner",
47
+ "clique_getSigners",
48
+ "clique_getSignersAtHash",
49
+ "clique_getSnapshot",
50
+ "clique_getSnapshotAtHash",
51
+ "clique_proposals",
52
+ "clique_propose",
53
+ "clique_status",
54
+ "debug_accountRange",
55
+ "debug_backtraceAt",
56
+ "debug_blockProfile",
57
+ "debug_chaindbCompact",
58
+ "debug_chaindbProperty",
59
+ "debug_cpuProfile",
60
+ "debug_dumpBlock",
61
+ "debug_freeOSMemory",
62
+ "debug_freezeClient",
63
+ "debug_gcStats",
64
+ "debug_getAccessibleState",
65
+ "debug_getBadBlocks",
66
+ "debug_getBlockRlp",
67
+ "debug_getHeaderRlp",
68
+ "debug_getModifiedAccountsByHash",
69
+ "debug_getModifiedAccountsByNumber",
70
+ "debug_goTrace",
71
+ "debug_intermediateRoots",
72
+ "debug_memStats",
73
+ "debug_mutexProfile",
74
+ "debug_preimage",
75
+ "debug_printBlock",
76
+ "debug_seedHash",
77
+ "debug_setBlockProfileRate",
78
+ "debug_setGCPercent",
79
+ "debug_setHead",
80
+ "debug_setMutexProfileFraction",
81
+ "debug_stacks",
82
+ "debug_standardTraceBadBlockToFile",
83
+ "debug_standardTraceBlockToFile",
84
+ "debug_startCPUProfile",
85
+ "debug_startGoTrace",
86
+ "debug_stopCPUProfile",
87
+ "debug_stopGoTrace",
88
+ "debug_storageRangeAt",
89
+ "debug_testSignCliqueBlock",
90
+ "debug_traceBadBlock",
91
+ "debug_traceBlock",
92
+ "debug_traceBlockByHash",
93
+ "debug_traceBlockByNumber",
94
+ "debug_traceBlockFromFile",
95
+ "debug_traceCall",
96
+ "debug_traceTransaction",
97
+ "debug_verbosity",
98
+ "debug_vmodule",
99
+ "debug_writeBlockProfile",
100
+ "debug_writeMemProfile",
101
+ "debug_writeMutexProfile",
102
+ "eth_accounts",
103
+ "eth_blockNumber",
104
+ "eth_call",
105
+ "eth_chainId",
106
+ "eth_coinbase",
107
+ "eth_compile",
108
+ "eth_contract",
109
+ "eth_createAccessList",
110
+ "eth_defaultAccount",
111
+ "eth_defaultBlock",
112
+ "eth_estimateGas",
113
+ "eth_feeHistory",
114
+ "eth_fillTransaction",
115
+ "eth_filter",
116
+ "eth_gasPrice",
117
+ "eth_getAccounts",
118
+ "eth_getBalance",
119
+ "eth_getBlock",
120
+ "eth_getBlockByHash",
121
+ "eth_getBlockByNumber",
122
+ "eth_getBlockNumber",
123
+ "eth_getBlockTransactionCount",
124
+ "eth_getBlockTransactionCountByHash",
125
+ "eth_getBlockTransactionCountByNumber",
126
+ "eth_getBlockUncleCount",
127
+ "eth_getCode",
128
+ "eth_getCoinbase",
129
+ "eth_getCompilers",
130
+ "eth_getFilterChanges",
131
+ "eth_getFilterLogs",
132
+ "eth_getGasPrice",
133
+ "eth_getHashrate",
134
+ "eth_getHeaderByHash",
135
+ "eth_getHeaderByNumber",
136
+ "eth_getLogs",
137
+ "eth_getMaxPriorityFeePerGas",
138
+ "eth_getMining",
139
+ "eth_getPendingTransactions",
140
+ "eth_getProof",
141
+ "eth_getProtocolVersion",
142
+ "eth_getRawTransaction",
143
+ "eth_getRawTransactionFromBlock",
144
+ "eth_getStorageAt",
145
+ "eth_getSyncing",
146
+ "eth_getTransaction",
147
+ "eth_getTransactionByBlockHashAndIndex",
148
+ "eth_getTransactionByBlockNumberAndIndex",
149
+ "eth_getTransactionByHash",
150
+ "eth_getTransactionCount",
151
+ "eth_getTransactionFromBlock",
152
+ "eth_getTransactionReceipt",
153
+ "eth_getUncle",
154
+ "eth_getUncleCountByBlockHash",
155
+ "eth_getUncleCountByBlockNumber",
156
+ "eth_getWork",
157
+ "eth_hashrate",
158
+ "eth_iban",
159
+ "eth_icapNamereg",
160
+ "eth_isSyncing",
161
+ "eth_maxPriorityFeePerGas",
162
+ "eth_mining",
163
+ "eth_namereg",
164
+ "eth_newBlockFilter",
165
+ "eth_newFilter",
166
+ "eth_newPendingTransactionFilter",
167
+ "eth_pendingTransactions",
168
+ "eth_protocolVersion",
169
+ "eth_resend",
170
+ "eth_sendIBANTransaction",
171
+ "eth_sendRawTransaction",
172
+ "eth_sendTransaction",
173
+ "eth_sign",
174
+ "eth_signTransaction",
175
+ "eth_submitHashrate",
176
+ "eth_submitTransaction",
177
+ "eth_submitWork",
178
+ "eth_subscribe",
179
+ "eth_syncing",
180
+ "eth_uninstallFilter",
181
+ "eth_unsubscribe",
182
+ "les_addBalance",
183
+ "les_clientInfo",
184
+ "les_getCheckpoint",
185
+ "les_getCheckpointContractAddress",
186
+ "les_latestCheckpoint",
187
+ "les_priorityClientInfo",
188
+ "les_serverInfo",
189
+ "les_setClientParams",
190
+ "les_setDefaultParams",
191
+ "miner_getHashrate",
192
+ "miner_setEtherbase",
193
+ "miner_setExtra",
194
+ "miner_setGasLimit",
195
+ "miner_setGasPrice",
196
+ "miner_start",
197
+ "miner_stop",
198
+ "personal_deriveAccount",
199
+ "personal_ecRecover",
200
+ "personal_getListAccounts",
201
+ "personal_getListWallets",
202
+ "personal_importRawKey",
203
+ "personal_initializeWallet",
204
+ "personal_listAccounts",
205
+ "personal_listWallets",
206
+ "personal_lockAccount",
207
+ "personal_newAccount",
208
+ "personal_openWallet",
209
+ "personal_sendTransaction",
210
+ "personal_sign",
211
+ "personal_signTransaction",
212
+ "personal_unlockAccount",
213
+ "personal_unpair",
214
+ "txpool_content",
215
+ "txpool_contentFrom",
216
+ "txpool_getContent",
217
+ "txpool_getInspect",
218
+ "txpool_getStatus",
219
+ "txpool_inspect",
220
+ "txpool_status",
221
+ ]
222
+ end
223
+ end
data/lib/eth/chain.rb CHANGED
@@ -12,82 +12,85 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- # Provides the `Eth` module.
15
+ # Provides the {Eth} module.
16
16
  module Eth
17
17
 
18
- # Encapsulates `Chain` IDs and utilities for EIP-155 compatibility.
19
- # ref: https://eips.ethereum.org/EIPS/eip-155
18
+ # Encapsulates {Eth::Chain} IDs and utilities for EIP-155 compatibility.
19
+ # Ref: https://eips.ethereum.org/EIPS/eip-155
20
20
  module Chain
21
21
  extend self
22
22
 
23
23
  # Provides a special replay protection error if EIP-155 is violated.
24
24
  class ReplayProtectionError < StandardError; end
25
25
 
26
- # Chain ID for Ethereum mainnet
26
+ # Chain ID for Ethereum mainnet.
27
27
  ETHEREUM = 1.freeze
28
28
 
29
- # Chain ID for Expanse mainnet
29
+ # Chain ID for Expanse mainnet.
30
30
  EXPANSE = 2.freeze
31
31
 
32
- # Chain ID for Optimistic Ethereum mainnet
32
+ # Chain ID for Optimistic Ethereum mainnet.
33
33
  OPTIMISM = 10.freeze
34
34
 
35
- # Chain ID for Ethereum Classic mainnet
35
+ # Chain ID for Ethereum Classic mainnet.
36
36
  CLASSIC = 61.freeze
37
37
 
38
- # Chain ID for POA Network mainnet
38
+ # Chain ID for POA Network mainnet.
39
39
  POA_NET = 99.freeze
40
40
 
41
- # Chain ID for xDAI mainnet
41
+ # Chain ID for Gnosis mainnet.
42
42
  XDAI = 100.freeze
43
43
 
44
- # Chain ID for Arbitrum mainnet
44
+ # Chain ID for Arbitrum mainnet.
45
45
  ARBITRUM = 42161.freeze
46
46
 
47
- # Chain ID for Morden (Ethereum) testnet
47
+ # Chain ID for Morden (Ethereum) testnet.
48
48
  MORDEN = 2.freeze
49
49
 
50
- # Chain ID for Ropsten testnet
50
+ # Chain ID for Ropsten testnet.
51
51
  ROPSTEN = 3.freeze
52
52
 
53
- # Chain ID for Rinkeby testnet
53
+ # Chain ID for Rinkeby testnet.
54
54
  RINKEBY = 4.freeze
55
55
 
56
- # Chain ID for Goerli testnet
56
+ # Chain ID for Goerli testnet.
57
57
  GOERLI = 5.freeze
58
58
 
59
- # Chain ID for Kotti testnet
59
+ # Chain ID for Kotti testnet.
60
60
  KOTTI = 6.freeze
61
61
 
62
- # Chain ID for Kovan testnet
62
+ # Chain ID for Kovan testnet.
63
63
  KOVAN = 42.freeze
64
64
 
65
- # Chain ID for Morden (Classic) testnet
65
+ # Chain ID for Morden (Classic) testnet.
66
66
  MORDEN_CLASSIC = 62.freeze
67
67
 
68
- # Chain ID for Mordor testnet
68
+ # Chain ID for Mordor testnet.
69
69
  MORDOR = 63.freeze
70
70
 
71
- # Chain ID for Optimistik Kovan testnet
71
+ # Chain ID for Optimistik Kovan testnet.
72
72
  KOVAN_OPTIMISM = 69.freeze
73
73
 
74
- # Chain ID for Arbitrum xDAI testnet
74
+ # Chain ID for Arbitrum xDAI testnet.
75
75
  XDAI_ARBITRUM = 200.freeze
76
76
 
77
- # Chain ID for Optimistic Goerli testnet
77
+ # Chain ID for Optimistic Goerli testnet.
78
78
  GOERLI_OPTIMISM = 420.freeze
79
79
 
80
- # Chain ID for Arbitrum Rinkeby testnet
80
+ # Chain ID for Arbitrum Rinkeby testnet.
81
81
  RINKEBY_ARBITRUM = 421611.freeze
82
82
 
83
- # Chain ID for the geth private network preset
83
+ # Chain ID for Sepolia testnet.
84
+ SEPOLIA = 11155111.freeze
85
+
86
+ # Chain ID for the geth private network preset.
84
87
  PRIVATE_GETH = 1337.freeze
85
88
 
86
89
  # Indicates wether the given `v` indicates a legacy chain value
87
90
  # without EIP-155 replay protection.
88
91
  #
89
- # @param v [Integer] the signature's `v` value
90
- # @return [Boolean] true if legacy value
92
+ # @param v [Integer] the signature's `v` value.
93
+ # @return [Boolean] true if legacy value.
91
94
  def is_legacy?(v)
92
95
  [27, 28].include? v
93
96
  end
@@ -95,7 +98,7 @@ module Eth
95
98
  # Convert a given `v` value to an ECDSA recovery id for the given
96
99
  # EIP-155 chain ID.
97
100
  #
98
- # @param v [Integer] the signature's `v` value
101
+ # @param v [Integer] the signature's `v` value.
99
102
  # @param chain_id [Integer] the chain id the signature was generated on.
100
103
  # @return [Integer] the recovery id corresponding to `v`.
101
104
  # @raise [ReplayProtectionError] if the given `v` is invalid.
@@ -134,10 +137,10 @@ module Eth
134
137
  end
135
138
 
136
139
  # Converst a `v` value into a chain ID. This does not work for legacy signatures
137
- # with v < 36 that do not conform with EIP-155.
140
+ # with `v < 36` that do not conform with EIP-155.
138
141
  #
139
142
  # @param v [Integer] the signature's `v` value.
140
- # @return [Integer] the chain id as per EIP-155 or nil if there is no replay protection.
143
+ # @return [Integer] the chain id as per EIP-155 or `nil` if there is no replay protection.
141
144
  def to_chain_id(v)
142
145
  return nil if v < 36
143
146
  chain_id = (v - 35) / 2
@@ -0,0 +1,63 @@
1
+ # Copyright (c) 2016-2022 The Ruby-Eth Contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require "net/http"
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+
20
+ # Provides an HTTP/S-RPC client.
21
+ class Client::Http < Client
22
+
23
+ # The host of the HTTP endpoint.
24
+ attr_reader :host
25
+
26
+ # The port of the HTTP endpoint.
27
+ attr_reader :port
28
+
29
+ # The full URI of the HTTP endpoint, including path.
30
+ attr_reader :uri
31
+
32
+ # Attribute indicator for SSL.
33
+ attr_reader :ssl
34
+
35
+ # Constructor for the HTTP Client. Should not be used; use
36
+ # {Client.create} intead.
37
+ #
38
+ # @param host [String] an URI pointing to an HTTP RPC-API.
39
+ def initialize(host)
40
+ super
41
+ uri = URI.parse(host)
42
+ raise ArgumentError, "Unable to parse the HTTP-URI!" unless ["http", "https"].include? uri.scheme
43
+ @host = uri.host
44
+ @port = uri.port
45
+ @ssl = uri.scheme == "https"
46
+ @uri = URI("#{uri.scheme}://#{@host}:#{@port}#{uri.path}")
47
+ end
48
+
49
+ # Sends an RPC request to the connected HTTP client.
50
+ #
51
+ # @param payload [Hash] the RPC request parameters.
52
+ # @return [String] a JSON-encoded response.
53
+ def send(payload)
54
+ http = Net::HTTP.new(@host, @port)
55
+ http.use_ssl = @ssl
56
+ header = { "Content-Type" => "application/json" }
57
+ request = Net::HTTP::Post.new(@uri, header)
58
+ request.body = payload
59
+ response = http.request(request)
60
+ response.body
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,47 @@
1
+ # Copyright (c) 2016-2022 The Ruby-Eth Contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require "socket"
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+
20
+ # Provides an IPC-RPC client.
21
+ class Client::Ipc < Client
22
+
23
+ # The path of the IPC socket.
24
+ attr_accessor :path
25
+
26
+ # Constructor for the IPC Client. Should not be used; use
27
+ # {Client.create} intead.
28
+ #
29
+ # @param path [String] an URI pointing to an IPC RPC-API.
30
+ def initialize(path)
31
+ super
32
+ @path = path
33
+ end
34
+
35
+ # Sends an RPC request to the connected IPC socket.
36
+ #
37
+ # @param payload [Hash] the RPC request parameters.
38
+ # @return [String] a JSON-encoded response.
39
+ def send(payload)
40
+ socket = UNIXSocket.new(@path)
41
+ socket.puts(payload)
42
+ read = socket.recvmsg(nil)[0]
43
+ socket.close
44
+ return read
45
+ end
46
+ end
47
+ end
data/lib/eth/client.rb ADDED
@@ -0,0 +1,232 @@
1
+ # Copyright (c) 2016-2022 The Ruby-Eth Contributors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Provides the {Eth} module.
16
+ module Eth
17
+
18
+ # Provides the {Eth::Client} super-class to connect to Ethereum
19
+ # network's RPC-API endpoints (IPC or HTTP).
20
+ class Client
21
+
22
+ # The client's RPC-request ID starting at 0.
23
+ attr_reader :id
24
+
25
+ # The connected network's chain ID.
26
+ attr_reader :chain_id
27
+
28
+ # The connected network's client coinbase.
29
+ attr_accessor :default_account
30
+
31
+ # The default transaction max priority fee per gas in Wei.
32
+ attr_accessor :max_priority_fee_per_gas
33
+
34
+ # The default transaction max fee per gas in Wei.
35
+ attr_accessor :max_fee_per_gas
36
+
37
+ # The default gas limit for the transaction.
38
+ attr_accessor :gas_limit
39
+
40
+ # Creates a new RPC-Client, either by providing an HTTP/S host or
41
+ # an IPC path.
42
+ #
43
+ # @param host [String] either an HTTP/S host or an IPC path.
44
+ # @return [Eth::Client::Ipc] an IPC client.
45
+ # @return [Eth::Client::Http] an HTTP client.
46
+ # @raise [ArgumentError] in case it cannot determine the client type.
47
+ def self.create(host)
48
+ return Client::Ipc.new host if host.end_with? ".ipc"
49
+ return Client::Http.new host if host.start_with? "http"
50
+ raise ArgumentError, "Unable to detect client type!"
51
+ end
52
+
53
+ # Constructor for the {Eth::Client} super-class. Should not be used;
54
+ # use {Client.create} intead.
55
+ def initialize(_)
56
+ @id = 0
57
+ @max_priority_fee_per_gas = 0
58
+ @max_fee_per_gas = Tx::DEFAULT_GAS_PRICE
59
+ @gas_limit = Tx::DEFAULT_GAS_LIMIT
60
+ end
61
+
62
+ # Gets the default account (coinbase) of the connected client.
63
+ #
64
+ # @return [Eth::Address] the coinbase account address.
65
+ def default_account
66
+ @default_account ||= Address.new eth_coinbase["result"]
67
+ end
68
+
69
+ # Gets the chain ID of the connected network.
70
+ #
71
+ # @return [Integer] the chain ID.
72
+ def chain_id
73
+ @chain_id ||= eth_chain_id["result"].to_i 16
74
+ end
75
+
76
+ # Gets the balance for an address.
77
+ #
78
+ # @param address [Eth::Address] the address to get the balance for.
79
+ # @return [Integer] the balance in Wei.
80
+ def get_balance(address)
81
+ eth_get_balance(address)["result"].to_i 16
82
+ end
83
+
84
+ # Gets the next nonce for an address used to draft new transactions.
85
+ #
86
+ # @param address [Eth::Address] the address to get the nonce for.
87
+ # @return [Integer] the next nonce to be used.
88
+ def get_nonce(address)
89
+ eth_get_transaction_count(address, "pending")["result"].to_i 16
90
+ end
91
+
92
+ # Simply transfer Ether to an account and waits for it to be mined.
93
+ # Uses `eth_coinbase` and external signer if no sender key is
94
+ # provided.
95
+ #
96
+ # @param destination [Eth::Address] the destination address.
97
+ # @param amount [Integer] the transfer amount in Wei.
98
+ # @param sender_key [Eth::Key] the sender private key.
99
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
100
+ # @return [String] the transaction hash.
101
+ def transfer_and_wait(destination, amount, sender_key = nil, legacy = false)
102
+ wait_for_tx(transfer(destination, amount, sender_key, legacy))
103
+ end
104
+
105
+ # Simply transfer Ether to an account without any call data or
106
+ # access lists attached. Uses `eth_coinbase` and external signer
107
+ # if no sender key is provided.
108
+ #
109
+ # @param destination [Eth::Address] the destination address.
110
+ # @param amount [Integer] the transfer amount in Wei.
111
+ # @param sender_key [Eth::Key] the sender private key.
112
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
113
+ # @return [String] the transaction hash.
114
+ def transfer(destination, amount, sender_key = nil, legacy = false)
115
+ params = {
116
+ value: amount,
117
+ to: destination,
118
+ gas_limit: gas_limit,
119
+ chain_id: chain_id,
120
+ }
121
+ if legacy
122
+ params.merge!({
123
+ gas_price: max_fee_per_gas,
124
+ })
125
+ else
126
+ params.merge!({
127
+ priority_fee: max_priority_fee_per_gas,
128
+ max_gas_fee: max_fee_per_gas,
129
+ })
130
+ end
131
+ unless sender_key.nil?
132
+
133
+ # use the provided key as sender and signer
134
+ params.merge!({
135
+ from: sender_key.address,
136
+ nonce: get_nonce(sender_key.address),
137
+ })
138
+ tx = Eth::Tx.new(params)
139
+ tx.sign sender_key
140
+ return eth_send_raw_transaction(tx.hex)["result"]
141
+ else
142
+
143
+ # use the default account as sender and external signer
144
+ params.merge!({
145
+ from: default_account,
146
+ nonce: get_nonce(default_account),
147
+ })
148
+ return eth_send_transaction(params)["result"]
149
+ end
150
+ end
151
+
152
+ # Gives control over resetting the RPC request ID back to zero.
153
+ # Usually not needed.
154
+ #
155
+ # @return [Integer] 0
156
+ def reset_id
157
+ @id = 0
158
+ end
159
+
160
+ # Checkes wether a transaction is mined or not.
161
+ #
162
+ # @param hash [String] the transaction hash.
163
+ # @return [Boolean] true if included in a block.
164
+ def is_mined_tx?(hash)
165
+ mined_tx = eth_get_transaction_by_hash hash
166
+ !mined_tx.nil? && !mined_tx["result"].nil? && !mined_tx["result"]["blockNumber"].nil?
167
+ end
168
+
169
+ # Waits for an transaction to be mined by the connected chain.
170
+ #
171
+ # @param hash [String] the transaction hash.
172
+ # @return [String] the transactin hash once the transaction is mined.
173
+ # @raise [Timeout::Error] if it's not mined within 5 minutes.
174
+ def wait_for_tx(hash)
175
+ start_time = Time.now
176
+ timeout = 300
177
+ retry_rate = 0.1
178
+ loop do
179
+ raise Timeout::Error if ((Time.now - start_time) > timeout)
180
+ return hash if is_mined_tx? hash
181
+ sleep retry_rate
182
+ end
183
+ end
184
+
185
+ # Metafunction to provide all known RPC commands defined in
186
+ # Eth::Api as snake_case methods to the Eth::Client classes.
187
+ Api::COMMANDS.each do |cmd|
188
+ method_name = cmd.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
189
+ define_method method_name do |*args|
190
+ send_command cmd, args
191
+ end
192
+ end
193
+
194
+ private
195
+
196
+ # Prepares parameters and sends the command to the client.
197
+ def send_command(command, args)
198
+ args << "latest" if ["eth_getBalance", "eth_call"].include? command
199
+ payload = {
200
+ jsonrpc: "2.0",
201
+ method: command,
202
+ params: marshal(args),
203
+ id: next_id,
204
+ }
205
+ output = JSON.parse(send(payload.to_json))
206
+ raise IOError, output["error"]["message"] unless output["error"].nil?
207
+ return output
208
+ end
209
+
210
+ # Increments the request id.
211
+ def next_id
212
+ @id += 1
213
+ end
214
+
215
+ # Recursively marshals all request parameters.
216
+ def marshal(params)
217
+ if params.is_a? Array
218
+ return params.map! { |param| marshal(param) }
219
+ elsif params.is_a? Hash
220
+ return params.transform_values! { |param| marshal(param) }
221
+ elsif params.is_a? Numeric
222
+ return Util.prefix_hex "#{params.to_i.to_s(16)}"
223
+ elsif params.is_a? Address
224
+ return params.to_s
225
+ elsif Util.is_hex? params
226
+ return Util.prefix_hex params
227
+ else
228
+ return params
229
+ end
230
+ end
231
+ end
232
+ end