eth 0.5.0 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/codeql.yml +4 -0
  3. data/.github/workflows/spec.yml +14 -3
  4. data/.yardopts +1 -0
  5. data/AUTHORS.txt +14 -1
  6. data/CHANGELOG.md +63 -13
  7. data/README.md +121 -18
  8. data/bin/console +2 -1
  9. data/bin/setup +3 -4
  10. data/codecov.yml +6 -0
  11. data/eth.gemspec +5 -7
  12. data/lib/eth/abi/event.rb +137 -0
  13. data/lib/eth/abi/type.rb +8 -7
  14. data/lib/eth/abi.rb +29 -11
  15. data/lib/eth/address.rb +12 -5
  16. data/lib/eth/api.rb +223 -0
  17. data/lib/eth/chain.rb +31 -28
  18. data/lib/eth/client/http.rb +63 -0
  19. data/lib/eth/client/ipc.rb +50 -0
  20. data/lib/eth/client.rb +468 -0
  21. data/lib/eth/constant.rb +71 -0
  22. data/lib/eth/contract/event.rb +41 -0
  23. data/lib/eth/contract/function.rb +56 -0
  24. data/lib/eth/contract/function_input.rb +36 -0
  25. data/lib/eth/contract/function_output.rb +32 -0
  26. data/lib/eth/contract/initializer.rb +46 -0
  27. data/lib/eth/contract.rb +120 -0
  28. data/lib/eth/eip712.rb +2 -2
  29. data/lib/eth/key/decrypter.rb +16 -13
  30. data/lib/eth/key/encrypter.rb +27 -25
  31. data/lib/eth/key.rb +21 -16
  32. data/lib/eth/rlp/decoder.rb +114 -0
  33. data/lib/eth/rlp/encoder.rb +78 -0
  34. data/lib/eth/rlp/sedes/big_endian_int.rb +66 -0
  35. data/lib/eth/rlp/sedes/binary.rb +97 -0
  36. data/lib/eth/rlp/sedes/list.rb +84 -0
  37. data/lib/eth/rlp/sedes.rb +74 -0
  38. data/lib/eth/rlp.rb +63 -0
  39. data/lib/eth/signature.rb +11 -8
  40. data/lib/eth/solidity.rb +75 -0
  41. data/lib/eth/tx/eip1559.rb +22 -14
  42. data/lib/eth/tx/eip2930.rb +23 -15
  43. data/lib/eth/tx/legacy.rb +14 -10
  44. data/lib/eth/tx.rb +28 -34
  45. data/lib/eth/unit.rb +1 -1
  46. data/lib/eth/util.rb +68 -11
  47. data/lib/eth/version.rb +3 -3
  48. data/lib/eth.rb +15 -2
  49. metadata +31 -23
  50. data/lib/eth/abi/constant.rb +0 -63
@@ -0,0 +1,50 @@
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
+ until read.end_with?("\n")
44
+ read = read << socket.recvmsg(nil)[0]
45
+ end
46
+ socket.close
47
+ return read
48
+ end
49
+ end
50
+ end
data/lib/eth/client.rb ADDED
@@ -0,0 +1,468 @@
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
+ # Deploy contract and waits for it to be mined.
153
+ # Uses `eth_coinbase` or external signer
154
+ # if no sender key is provided.
155
+ #
156
+ # @overload deploy(contract)
157
+ # @param contract [Eth::Contract] contracts to deploy.
158
+ # @overload deploy(contract, sender_key)
159
+ # @param contract [Eth::Contract] contracts to deploy.
160
+ # @param sender_key [Eth::Key] the sender private key.
161
+ # @overload deploy(contract, sender_key, legacy)
162
+ # @param contract [Eth::Contract] contracts to deploy.
163
+ # @param sender_key [Eth::Key] the sender private key.
164
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
165
+ # @return [String] the contract address.
166
+ def deploy_and_wait(contract, sender_key: nil, legacy: false)
167
+ hash = wait_for_tx(deploy(contract, sender_key: sender_key, legacy: legacy))
168
+ contract.address = eth_get_transaction_receipt(hash)["result"]["contractAddress"]
169
+ end
170
+
171
+ # Deploy contract. Uses `eth_coinbase` or external signer
172
+ # if no sender key is provided.
173
+ #
174
+ # @overload deploy(contract)
175
+ # @param contract [Eth::Contract] contracts to deploy.
176
+ # @overload deploy(contract, sender_key)
177
+ # @param contract [Eth::Contract] contracts to deploy.
178
+ # @param sender_key [Eth::Key] the sender private key.
179
+ # @overload deploy(contract, sender_key, legacy)
180
+ # @param contract [Eth::Contract] contracts to deploy.
181
+ # @param sender_key [Eth::Key] the sender private key.
182
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
183
+ # @return [String] the transaction hash.
184
+ def deploy(contract, sender_key: nil, legacy: false)
185
+ gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
186
+ params = {
187
+ value: 0,
188
+ gas_limit: gas_limit,
189
+ chain_id: chain_id,
190
+ data: contract.bin,
191
+ }
192
+ if legacy
193
+ params.merge!({
194
+ gas_price: max_fee_per_gas,
195
+ })
196
+ else
197
+ params.merge!({
198
+ priority_fee: max_priority_fee_per_gas,
199
+ max_gas_fee: max_fee_per_gas,
200
+ })
201
+ end
202
+ unless sender_key.nil?
203
+ # use the provided key as sender and signer
204
+ params.merge!({
205
+ from: sender_key.address,
206
+ nonce: get_nonce(sender_key.address),
207
+ })
208
+ tx = Eth::Tx.new(params)
209
+ tx.sign sender_key
210
+ return eth_send_raw_transaction(tx.hex)["result"]
211
+ else
212
+ # use the default account as sender and external signer
213
+ params.merge!({
214
+ from: default_account,
215
+ nonce: get_nonce(default_account),
216
+ })
217
+ return eth_send_transaction(params)["result"]
218
+ end
219
+ end
220
+
221
+ # Encoding for function calls.
222
+ def call_payload(fun, args)
223
+ types = fun.inputs.map { |i| i.type }
224
+ encoded_str = Util.bin_to_hex(Eth::Abi.encode(types, args))
225
+ "0x" + fun.signature + (encoded_str.empty? ? "0" * 64 : encoded_str)
226
+ end
227
+
228
+ # Non-transactional function call called from call().
229
+ #
230
+ # @overload call_raw(contract, func)
231
+ # @param contract [Eth::Contract] subject contract to call.
232
+ # @param func [Eth::Contract::Function] method name to be called.
233
+ # @overload call_raw(contract, func, value)
234
+ # @param contract [Eth::Contract] subject contract to call.
235
+ # @param func [Eth::Contract::Function] method name to be called.
236
+ # @param value [Integer|String] function arguments.
237
+ # @overload call_raw(contract, func, value, sender_key, legacy)
238
+ # @param contract [Eth::Contract] subject contract to call.
239
+ # @param func [Eth::Contract::Function] method name to be called.
240
+ # @param value [Integer|String] function arguments.
241
+ # @param sender_key [Eth::Key] the sender private key.
242
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
243
+ # @return [Object] returns the result of the call.
244
+ def call_raw(contract, func, *args, **kwargs)
245
+ gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
246
+ params = {
247
+ gas_limit: gas_limit,
248
+ chain_id: chain_id,
249
+ data: call_payload(func, args),
250
+ }
251
+ if kwargs[:address] || contract.address
252
+ params.merge!({ to: kwargs[:address] || contract.address })
253
+ end
254
+ if kwargs[:legacy]
255
+ params.merge!({
256
+ gas_price: max_fee_per_gas,
257
+ })
258
+ else
259
+ params.merge!({
260
+ priority_fee: max_priority_fee_per_gas,
261
+ max_gas_fee: max_fee_per_gas,
262
+ })
263
+ end
264
+ unless kwargs[:sender_key].nil?
265
+ # use the provided key as sender and signer
266
+ params.merge!({
267
+ from: kwargs[:sender_key].address,
268
+ nonce: get_nonce(kwargs[:sender_key].address),
269
+ })
270
+ tx = Eth::Tx.new(params)
271
+ tx.sign kwargs[:sender_key]
272
+ else
273
+ # use the default account as sender and external signer
274
+ params.merge!({
275
+ from: default_account,
276
+ nonce: get_nonce(default_account),
277
+ })
278
+ end
279
+ raw_result = eth_call(params)["result"]
280
+ types = func.outputs.map { |i| i.type }
281
+ Eth::Abi.decode(types, raw_result)
282
+ end
283
+
284
+ # Non-transactional function calls.
285
+ #
286
+ # @overload call(contract, function_name)
287
+ # @param contract [Eth::Contract] subject contract to call.
288
+ # @param function_name [String] method name to be called.
289
+ # @overload call(contract, function_name, value)
290
+ # @param contract [Eth::Contract] subject contract to call.
291
+ # @param function_name [String] method name to be called.
292
+ # @param value [Integer|String] function arguments.
293
+ # @overload call(contract, function_name, value, sender_key, legacy)
294
+ # @param contract [Eth::Contract] subject contract to call.
295
+ # @param function_name [String] method name to be called.
296
+ # @param value [Integer|String] function arguments.
297
+ # @param sender_key [Eth::Key] the sender private key.
298
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
299
+ # @return [Object] returns the result of the call.
300
+ def call(contract, function_name, *args, **kwargs)
301
+ func = contract.functions.select { |func| func.name == function_name }[0]
302
+ raise ArgumentError, "function_name does not exist!" if func.nil?
303
+ output = call_raw(contract, func, *args, **kwargs)
304
+ if output.length == 1
305
+ return output[0]
306
+ else
307
+ return output
308
+ end
309
+ end
310
+
311
+ # Function call with transaction.
312
+ #
313
+ # @overload transact(contract, function_name)
314
+ # @param contract [Eth::Contract] subject contract to call.
315
+ # @param function_name [String] method name to be called.
316
+ # @overload transact(contract, function_name, value)
317
+ # @param contract [Eth::Contract] subject contract to call.
318
+ # @param function_name [String] method name to be called.
319
+ # @param value [Integer|String] function arguments.
320
+ # @overload transact(contract, function_name, value, sender_key, legacy, address)
321
+ # @param contract [Eth::Contract] subject contract to call.
322
+ # @param function_name [String] method name to be called.
323
+ # @param value [Integer|String] function arguments.
324
+ # @param sender_key [Eth::Key] the sender private key.
325
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
326
+ # @param address [String] contract address.
327
+ # @return [Object] returns the result of the call.
328
+ def transact(contract, function_name, *args, **kwargs)
329
+ gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
330
+ fun = contract.functions.select { |func| func.name == function_name }[0]
331
+ params = {
332
+ value: 0,
333
+ gas_limit: gas_limit,
334
+ chain_id: chain_id,
335
+ to: kwargs[:address] || contract.address,
336
+ data: call_payload(fun, args),
337
+ }
338
+ if kwargs[:legacy]
339
+ params.merge!({
340
+ gas_price: max_fee_per_gas,
341
+ })
342
+ else
343
+ params.merge!({
344
+ priority_fee: max_priority_fee_per_gas,
345
+ max_gas_fee: max_fee_per_gas,
346
+ })
347
+ end
348
+ unless kwargs[:sender_key].nil?
349
+ # use the provided key as sender and signer
350
+ params.merge!({
351
+ from: kwargs[:sender_key].address,
352
+ nonce: get_nonce(kwargs[:sender_key].address),
353
+ })
354
+ tx = Eth::Tx.new(params)
355
+ tx.sign kwargs[:sender_key]
356
+ return eth_send_raw_transaction(tx.hex)["result"]
357
+ else
358
+ # use the default account as sender and external signer
359
+ params.merge!({
360
+ from: default_account,
361
+ nonce: get_nonce(default_account),
362
+ })
363
+ return eth_send_transaction(params)["result"]
364
+ end
365
+ end
366
+
367
+ # Function call with transaction and waits for it to be mined.
368
+ #
369
+ # @overload transact_and_wait(contract, function_name)
370
+ # @param contract [Eth::Contract] subject contract to call.
371
+ # @param function_name [String] method name to be called.
372
+ # @overload transact_and_wait(contract, function_name, value)
373
+ # @param contract [Eth::Contract] subject contract to call.
374
+ # @param function_name [String] method name to be called.
375
+ # @param value [Integer|String] function arguments.
376
+ # @overload transact_and_wait(contract, function_name, value, sender_key, legacy, address)
377
+ # @param contract [Eth::Contract] subject contract to call.
378
+ # @param function_name [String] method name to be called.
379
+ # @param value [Integer|String] function arguments.
380
+ # @param sender_key [Eth::Key] the sender private key.
381
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
382
+ # @param address [String] contract address.
383
+ # @return [Object] returns the result of the call.
384
+ def transact_and_wait(contract, function_name, *args, **kwargs)
385
+ wait_for_tx(transact(contract, function_name, *args, **kwargs))
386
+ end
387
+
388
+ # Gives control over resetting the RPC request ID back to zero.
389
+ # Usually not needed.
390
+ #
391
+ # @return [Integer] 0
392
+ def reset_id
393
+ @id = 0
394
+ end
395
+
396
+ # Checkes wether a transaction is mined or not.
397
+ #
398
+ # @param hash [String] the transaction hash.
399
+ # @return [Boolean] true if included in a block.
400
+ def is_mined_tx?(hash)
401
+ mined_tx = eth_get_transaction_by_hash hash
402
+ !mined_tx.nil? && !mined_tx["result"].nil? && !mined_tx["result"]["blockNumber"].nil?
403
+ end
404
+
405
+ # Waits for an transaction to be mined by the connected chain.
406
+ #
407
+ # @param hash [String] the transaction hash.
408
+ # @return [String] the transactin hash once the transaction is mined.
409
+ # @raise [Timeout::Error] if it's not mined within 5 minutes.
410
+ def wait_for_tx(hash)
411
+ start_time = Time.now
412
+ timeout = 300
413
+ retry_rate = 0.1
414
+ loop do
415
+ raise Timeout::Error if ((Time.now - start_time) > timeout)
416
+ return hash if is_mined_tx? hash
417
+ sleep retry_rate
418
+ end
419
+ end
420
+
421
+ # Metafunction to provide all known RPC commands defined in
422
+ # Eth::Api as snake_case methods to the Eth::Client classes.
423
+ Api::COMMANDS.each do |cmd|
424
+ method_name = cmd.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
425
+ define_method method_name do |*args|
426
+ send_command cmd, args
427
+ end
428
+ end
429
+
430
+ private
431
+
432
+ # Prepares parameters and sends the command to the client.
433
+ def send_command(command, args)
434
+ args << "latest" if ["eth_getBalance", "eth_call"].include? command
435
+ payload = {
436
+ jsonrpc: "2.0",
437
+ method: command,
438
+ params: marshal(args),
439
+ id: next_id,
440
+ }
441
+ output = JSON.parse(send(payload.to_json))
442
+ raise IOError, output["error"]["message"] unless output["error"].nil?
443
+ return output
444
+ end
445
+
446
+ # Increments the request id.
447
+ def next_id
448
+ @id += 1
449
+ end
450
+
451
+ # Recursively marshals all request parameters.
452
+ def marshal(params)
453
+ if params.is_a? Array
454
+ return params.map! { |param| marshal(param) }
455
+ elsif params.is_a? Hash
456
+ return params.transform_values! { |param| marshal(param) }
457
+ elsif params.is_a? Numeric
458
+ return Util.prefix_hex "#{params.to_i.to_s(16)}"
459
+ elsif params.is_a? Address
460
+ return params.to_s
461
+ elsif Util.is_hex? params
462
+ return Util.prefix_hex params
463
+ else
464
+ return params
465
+ end
466
+ end
467
+ end
468
+ end
@@ -0,0 +1,71 @@
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
+ # -*- encoding : ascii-8bit -*-
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+
20
+ # Provides commonly used constants, such as zero bytes or zero keys.
21
+ module Constant
22
+
23
+ # The empty byte is defined as "".
24
+ BYTE_EMPTY = "".freeze
25
+
26
+ # The zero byte is 0x00.
27
+ BYTE_ZERO = "\x00".freeze
28
+
29
+ # The byte one is 0x01.
30
+ BYTE_ONE = "\x01".freeze
31
+
32
+ # The size of a 32-bit number.
33
+ TT32 = (2 ** 32).freeze
34
+
35
+ # The size of a 256-bit number.
36
+ TT256 = (2 ** 256).freeze
37
+
38
+ # The maximum possible value of an UInt256.
39
+ UINT_MAX = (2 ** 256 - 1).freeze
40
+
41
+ # The minimum possible value of an UInt256.
42
+ UINT_MIN = 0.freeze
43
+
44
+ # The maximum possible value of an Int256.
45
+ INT_MAX = (2 ** 255 - 1).freeze
46
+
47
+ # The minimum possible value of an Int256.
48
+ INT_MIN = (-2 ** 255).freeze
49
+
50
+ # A hash containing only zeros.
51
+ HASH_ZERO = ("\x00" * 32).freeze
52
+
53
+ # The RLP short length limit.
54
+ SHORT_LENGTH_LIMIT = 56.freeze
55
+
56
+ # The RLP long length limit.
57
+ LONG_LENGTH_LIMIT = (256 ** 8).freeze
58
+
59
+ # The RLP primitive type offset.
60
+ PRIMITIVE_PREFIX_OFFSET = 0x80.freeze
61
+
62
+ # The RLP array type offset.
63
+ LIST_PREFIX_OFFSET = 0xc0.freeze
64
+
65
+ # The binary encoding is ASCII (8-bit).
66
+ BINARY_ENCODING = "ASCII-8BIT".freeze
67
+
68
+ # Infinity as constant for convenience.
69
+ INFINITY = (1.0 / 0.0).freeze
70
+ end
71
+ end
@@ -0,0 +1,41 @@
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
+ # -*- encoding : ascii-8bit -*-
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+ # Provide classes for contract event.
20
+ class Contract::Event
21
+ attr_accessor :name, :signature, :input_types, :inputs, :event_string, :address
22
+
23
+ # Constructor of the {Eth::Contract::Event} class.
24
+ #
25
+ # @param data [Hash] contract event data.
26
+ def initialize(data)
27
+ @name = data["name"]
28
+ @input_types = data["inputs"].collect { |x| x["type"] }
29
+ @inputs = data["inputs"].collect { |x| x["name"] }
30
+ @event_string = "#{@name}(#{@input_types.join(",")})"
31
+ @signature = Digest::Keccak.hexdigest(@event_string, 256)
32
+ end
33
+
34
+ # Set the address of the smart contract
35
+ #
36
+ # @param address [String] contract address.
37
+ def set_address(address)
38
+ @address = address.nil? ? nil : Eth::Address.new(address).address
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,56 @@
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
+ # -*- encoding : ascii-8bit -*-
16
+
17
+ # Provides the {Eth} module.
18
+ module Eth
19
+ # Provides the methods for smart contract function.
20
+ class Contract::Function
21
+ attr_accessor :name, :inputs, :outputs, :signature, :constant, :function_string
22
+
23
+ # Constructor of the {Eth::Function} class.
24
+ #
25
+ # @param data [Hash] function input and output data.
26
+ def initialize(data)
27
+ @name = data["name"]
28
+ @constant = data["constant"]
29
+ @inputs = data["inputs"].map do |input|
30
+ Eth::Contract::FunctionInput.new(input)
31
+ end
32
+ @outputs = data["outputs"].collect do |output|
33
+ Eth::Contract::FunctionOutput.new(output)
34
+ end
35
+ @function_string = self.class.calc_signature(@name, @inputs)
36
+ @signature = self.class.encoded_function_signature(@function_string)
37
+ end
38
+
39
+ # Create function strings.
40
+ #
41
+ # @param name [String] function name.
42
+ # @param inputs [Array<Eth::Contract::FunctionInput>] function input class list.
43
+ # @return [String] function string.
44
+ def self.calc_signature(name, inputs)
45
+ "#{name}(#{inputs.collect { |x| x.type }.join(",")})"
46
+ end
47
+
48
+ # encode function signature.
49
+ #
50
+ # @param signature [String] function signature.
51
+ # @return [String] encoded function signature string.
52
+ def self.encoded_function_signature(signature)
53
+ Digest::Keccak.hexdigest(signature, 256)[0..7]
54
+ end
55
+ end
56
+ end