eth 0.5.1 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/eth/client.rb CHANGED
@@ -149,6 +149,203 @@ module Eth
149
149
  end
150
150
  end
151
151
 
152
+ # Deploys a contract and waits for it to be mined. Uses
153
+ # `eth_coinbase` or external signer if no sender key is provided.
154
+ #
155
+ # @overload deploy(contract)
156
+ # @param contract [Eth::Contract] contracts to deploy.
157
+ # @overload deploy(contract, sender_key)
158
+ # @param contract [Eth::Contract] contracts to deploy.
159
+ # @param sender_key [Eth::Key] the sender private key.
160
+ # @overload deploy(contract, sender_key, legacy)
161
+ # @param contract [Eth::Contract] contracts to deploy.
162
+ # @param sender_key [Eth::Key] the sender private key.
163
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
164
+ # @return [String] the contract address.
165
+ def deploy_and_wait(contract, sender_key: nil, legacy: false)
166
+ hash = wait_for_tx(deploy(contract, sender_key: sender_key, legacy: legacy))
167
+ addr = eth_get_transaction_receipt(hash)["result"]["contractAddress"]
168
+ contract.address = Address.new(addr).to_s
169
+ end
170
+
171
+ # Deploys a 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
+ # @raise [ArgumentError] in case the contract does not have any source.
185
+ def deploy(contract, sender_key: nil, legacy: false)
186
+ raise ArgumentError, "Cannot deploy contract without source or binary!" if contract.bin.nil?
187
+ gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
188
+ params = {
189
+ value: 0,
190
+ gas_limit: gas_limit,
191
+ chain_id: chain_id,
192
+ data: contract.bin,
193
+ }
194
+ if legacy
195
+ params.merge!({
196
+ gas_price: max_fee_per_gas,
197
+ })
198
+ else
199
+ params.merge!({
200
+ priority_fee: max_priority_fee_per_gas,
201
+ max_gas_fee: max_fee_per_gas,
202
+ })
203
+ end
204
+ unless sender_key.nil?
205
+ # use the provided key as sender and signer
206
+ params.merge!({
207
+ from: sender_key.address,
208
+ nonce: get_nonce(sender_key.address),
209
+ })
210
+ tx = Eth::Tx.new(params)
211
+ tx.sign sender_key
212
+ return eth_send_raw_transaction(tx.hex)["result"]
213
+ else
214
+ # use the default account as sender and external signer
215
+ params.merge!({
216
+ from: default_account,
217
+ nonce: get_nonce(default_account),
218
+ })
219
+ return eth_send_transaction(params)["result"]
220
+ end
221
+ end
222
+
223
+ # Calls a contract function without executing it
224
+ # (non-transactional contract read).
225
+ #
226
+ # @overload call(contract, function_name)
227
+ # @param contract [Eth::Contract] subject contract to call.
228
+ # @param function_name [String] method name to be called.
229
+ # @overload call(contract, function_name, value)
230
+ # @param contract [Eth::Contract] subject contract to call.
231
+ # @param function_name [String] method name to be called.
232
+ # @param value [Integer|String] function arguments.
233
+ # @overload call(contract, function_name, value, sender_key, legacy)
234
+ # @param contract [Eth::Contract] subject contract to call.
235
+ # @param function_name [String] method name to be called.
236
+ # @param value [Integer|String] function arguments.
237
+ # @param sender_key [Eth::Key] the sender private key.
238
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
239
+ # @return [Object] returns the result of the call.
240
+ def call(contract, function_name, *args, **kwargs)
241
+ func = contract.functions.select { |func| func.name == function_name }[0]
242
+ raise ArgumentError, "function_name does not exist!" if func.nil?
243
+ output = call_raw(contract, func, *args, **kwargs)
244
+ if output.length == 1
245
+ return output[0]
246
+ else
247
+ return output
248
+ end
249
+ end
250
+
251
+ # Executes a contract function with a transaction (transactional
252
+ # contract read/write).
253
+ #
254
+ # @overload transact(contract, function_name)
255
+ # @param contract [Eth::Contract] subject contract to call.
256
+ # @param function_name [String] method name to be called.
257
+ # @overload transact(contract, function_name, value)
258
+ # @param contract [Eth::Contract] subject contract to call.
259
+ # @param function_name [String] method name to be called.
260
+ # @param value [Integer|String] function arguments.
261
+ # @overload transact(contract, function_name, value, sender_key, legacy, address)
262
+ # @param contract [Eth::Contract] subject contract to call.
263
+ # @param function_name [String] method name to be called.
264
+ # @param value [Integer|String] function arguments.
265
+ # @param sender_key [Eth::Key] the sender private key.
266
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
267
+ # @param address [String] contract address.
268
+ # @return [Object] returns the result of the call.
269
+ def transact(contract, function_name, *args, **kwargs)
270
+ gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
271
+ fun = contract.functions.select { |func| func.name == function_name }[0]
272
+ params = {
273
+ value: 0,
274
+ gas_limit: gas_limit,
275
+ chain_id: chain_id,
276
+ to: kwargs[:address] || contract.address,
277
+ data: call_payload(fun, args),
278
+ }
279
+ if kwargs[:legacy]
280
+ params.merge!({
281
+ gas_price: max_fee_per_gas,
282
+ })
283
+ else
284
+ params.merge!({
285
+ priority_fee: max_priority_fee_per_gas,
286
+ max_gas_fee: max_fee_per_gas,
287
+ })
288
+ end
289
+ unless kwargs[:sender_key].nil?
290
+ # use the provided key as sender and signer
291
+ params.merge!({
292
+ from: kwargs[:sender_key].address,
293
+ nonce: get_nonce(kwargs[:sender_key].address),
294
+ })
295
+ tx = Eth::Tx.new(params)
296
+ tx.sign kwargs[:sender_key]
297
+ return eth_send_raw_transaction(tx.hex)["result"]
298
+ else
299
+ # use the default account as sender and external signer
300
+ params.merge!({
301
+ from: default_account,
302
+ nonce: get_nonce(default_account),
303
+ })
304
+ return eth_send_transaction(params)["result"]
305
+ end
306
+ end
307
+
308
+ # Executes a contract function with a transaction and waits for it
309
+ # to be mined (transactional contract read/write).
310
+ #
311
+ # @overload transact_and_wait(contract, function_name)
312
+ # @param contract [Eth::Contract] subject contract to call.
313
+ # @param function_name [String] method name to be called.
314
+ # @overload transact_and_wait(contract, function_name, value)
315
+ # @param contract [Eth::Contract] subject contract to call.
316
+ # @param function_name [String] method name to be called.
317
+ # @param value [Integer|String] function arguments.
318
+ # @overload transact_and_wait(contract, function_name, value, sender_key, legacy, address)
319
+ # @param contract [Eth::Contract] subject contract to call.
320
+ # @param function_name [String] method name to be called.
321
+ # @param value [Integer|String] function arguments.
322
+ # @param sender_key [Eth::Key] the sender private key.
323
+ # @param legacy [Boolean] enables legacy transactions (pre-EIP-1559).
324
+ # @param address [String] contract address.
325
+ # @return [Object] returns the result of the call.
326
+ def transact_and_wait(contract, function_name, *args, **kwargs)
327
+ wait_for_tx(transact(contract, function_name, *args, **kwargs))
328
+ end
329
+
330
+ # Provides an interface to call `isValidSignature` as per EIP-1271 on a given
331
+ # smart contract to verify the given hash and signature matching the magic
332
+ # value.
333
+ #
334
+ # @param contract [Eth::Contract] a deployed contract implementing EIP-1271.
335
+ # @param hash [String] the message hash to be checked against the signature.
336
+ # @param signature [String] the signature to be recovered by the contract.
337
+ # @param magic [String] the expected magic value (defaults to `1626ba7e`).
338
+ # @return [Boolean] true if magic matches and signature is valid.
339
+ # @raise [ArgumentError] in case the contract cannot be called yet.
340
+ def is_valid_signature(contract, hash, signature, magic = "1626ba7e")
341
+ raise ArgumentError, "Contract not deployed yet." if contract.address.nil?
342
+ hash = Util.hex_to_bin hash if Util.is_hex? hash
343
+ signature = Util.hex_to_bin signature if Util.is_hex? signature
344
+ magic = Util.hex_to_bin magic if Util.is_hex? magic
345
+ result = call(contract, "isValidSignature", hash, signature)
346
+ return result === magic
347
+ end
348
+
152
349
  # Gives control over resetting the RPC request ID back to zero.
153
350
  # Usually not needed.
154
351
  #
@@ -169,7 +366,7 @@ module Eth
169
366
  # Waits for an transaction to be mined by the connected chain.
170
367
  #
171
368
  # @param hash [String] the transaction hash.
172
- # @return [String] the transactin hash once the transaction is mined.
369
+ # @return [String] the transaction hash once the transaction is mined.
173
370
  # @raise [Timeout::Error] if it's not mined within 5 minutes.
174
371
  def wait_for_tx(hash)
175
372
  start_time = Time.now
@@ -193,6 +390,48 @@ module Eth
193
390
 
194
391
  private
195
392
 
393
+ # Non-transactional function call called from call().
394
+ def call_raw(contract, func, *args, **kwargs)
395
+ gas_limit = Tx.estimate_intrinsic_gas(contract.bin) + Tx::CREATE_GAS
396
+ params = {
397
+ gas_limit: gas_limit,
398
+ chain_id: chain_id,
399
+ data: call_payload(func, args),
400
+ }
401
+ if kwargs[:address] || contract.address
402
+ params.merge!({ to: kwargs[:address] || contract.address })
403
+ end
404
+ if kwargs[:legacy]
405
+ params.merge!({
406
+ gas_price: max_fee_per_gas,
407
+ })
408
+ else
409
+ params.merge!({
410
+ priority_fee: max_priority_fee_per_gas,
411
+ max_gas_fee: max_fee_per_gas,
412
+ })
413
+ end
414
+ unless kwargs[:sender_key].nil?
415
+ # use the provided key as sender and signer
416
+ params.merge!({
417
+ from: kwargs[:sender_key].address,
418
+ nonce: get_nonce(kwargs[:sender_key].address),
419
+ })
420
+ tx = Eth::Tx.new(params)
421
+ tx.sign kwargs[:sender_key]
422
+ end
423
+ raw_result = eth_call(params)["result"]
424
+ types = func.outputs.map { |i| i.type }
425
+ Eth::Abi.decode(types, raw_result)
426
+ end
427
+
428
+ # Encodes function call payloads.
429
+ def call_payload(fun, args)
430
+ types = fun.inputs.map { |i| i.type }
431
+ encoded_str = Util.bin_to_hex(Eth::Abi.encode(types, args))
432
+ "0x" + fun.signature + (encoded_str.empty? ? "0" * 64 : encoded_str)
433
+ end
434
+
196
435
  # Prepares parameters and sends the command to the client.
197
436
  def send_command(command, args)
198
437
  args << "latest" if ["eth_getBalance", "eth_call"].include? command
@@ -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
@@ -0,0 +1,36 @@
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 function input.
20
+ class Contract::FunctionInput
21
+ attr_accessor :type, :name
22
+
23
+ # Constructor of the {Eth::Contract::FunctionInput} class.
24
+ #
25
+ # @param data [Hash] contract abi data.
26
+ def initialize(data)
27
+ @type = Eth::Abi::Type.parse(data["type"])
28
+ @name = data["name"]
29
+ end
30
+
31
+ # Returns types like uint256
32
+ def type
33
+ @type.base_type + @type.sub_type
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,32 @@
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 function output.
20
+ class Contract::FunctionOutput
21
+ attr_accessor :type, :name
22
+
23
+ def initialize(data)
24
+ @type = Eth::Abi::Type.parse(data["type"])
25
+ @name = data["name"]
26
+ end
27
+
28
+ def type
29
+ @type.base_type + @type.sub_type
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,46 @@
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 initializer.
20
+ class Contract::Initializer
21
+ attr_accessor :contracts, :file
22
+
23
+ # Constructor of the {Eth::Contract::Initializer} class.
24
+ #
25
+ # @param file [String] file path to solidity code.
26
+ def initialize(file)
27
+ sol_output = Eth::Solidity.new.compile(file)
28
+ contracts = sol_output.keys
29
+
30
+ @contracts = []
31
+ contracts.each do |contract|
32
+ abi = sol_output[contract]["abi"]
33
+ name = contract
34
+ code = sol_output[contract]["bin"]
35
+ @contracts << Contract.new(name, code, abi)
36
+ end
37
+ end
38
+
39
+ # Build and return all contracts.
40
+ def build_all
41
+ @contracts.each do |contract|
42
+ contract.build
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,135 @@
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 classes to access smart contracts
21
+ class Contract
22
+ attr_reader :address
23
+ attr_accessor :key
24
+ attr_accessor :gas_limit, :gas_price, :max_fee_per_gas, :max_priority_fee_per_gas, :nonce
25
+ attr_accessor :bin, :name, :abi, :class_object
26
+ attr_accessor :events, :functions, :constructor_inputs
27
+
28
+ # Constructor of the {Eth::Contract} class.
29
+ #
30
+ # @param name [String] contract name.
31
+ # @param bin [String] contract bin string.
32
+ # @param abi [String] contract abi string.
33
+ def initialize(name, bin, abi)
34
+ @name = name
35
+ @bin = bin
36
+ @abi = abi
37
+ @constructor_inputs, @functions, @events = parse_abi(abi)
38
+ end
39
+
40
+ # Creates a contract wrapper from a Solidity file.
41
+ #
42
+ # @param file [String] solidity file path.
43
+ # @param contract_index [Number] specify contract.
44
+ # @return [Eth::Contract::Object] Returns the class of the smart contract.
45
+ # @raise [ArgumentError] if the file path is empty or no contracts were compiled.
46
+ def self.from_file(file:, contract_index: 0)
47
+ raise ArgumentError, "Cannot find the contract at #{file.to_s}!" if !File.exist?(file.to_s)
48
+ contracts = Eth::Contract::Initializer.new(file).build_all
49
+ raise ArgumentError, "No contracts compiled." if contracts.empty?
50
+ contracts[contract_index].class_object.new
51
+ end
52
+
53
+ # Creates a contract wrapper from ABI and address.
54
+ #
55
+ # @param abi [String] contract abi string.
56
+ # @param address [String] contract address.
57
+ # @param name [String] name of contract.
58
+ # @return [Eth::Contract::Object] Returns the class of the smart contract.
59
+ # @raise [JSON::ParserError] if the json format is wrong.
60
+ # @raise [ArgumentError] if ABI, address, or name is missing.
61
+ def self.from_abi(abi:, address:, name:)
62
+ abi = abi.is_a?(Array) ? abi : JSON.parse(abi)
63
+ contract = Eth::Contract.new(name, nil, abi)
64
+ contract.build
65
+ contract = contract.class_object.new
66
+ contract.address = address
67
+ contract
68
+ end
69
+
70
+ # Creates a contract wrapper from binary and ABI.
71
+ #
72
+ # @param bin [String] contract bin string.
73
+ # @param abi [String] contract abi string.
74
+ # @param name [String] name of contract.
75
+ # @return [Eth::Contract::Object] Returns the class of the smart contract.
76
+ # @raise [JSON::ParserError] if the json format is wrong.
77
+ # @raise [ArgumentError] if ABI, binary, or name is missing.
78
+ def self.from_bin(bin:, abi:, name:)
79
+ abi = abi.is_a?(Array) ? abi : JSON.parse(abi)
80
+ contract = Eth::Contract.new(name, bin, abi)
81
+ contract.build
82
+ contract.class_object.new
83
+ end
84
+
85
+ # Sets the address of the smart contract.
86
+ #
87
+ # @param addr [String|Eth::Address] contract address string.
88
+ def address=(addr)
89
+ if addr.is_a? Eth::Address
90
+ @address = addr.to_s
91
+ else
92
+ @address = Eth::Address.new(addr).to_s
93
+ end
94
+ @events.each do |event|
95
+ event.set_address(@address)
96
+ end
97
+ end
98
+
99
+ # Create meta classes for smart contracts.
100
+ def build
101
+ class_name = @name
102
+ parent = self
103
+ class_methods = Class.new do
104
+ extend Forwardable
105
+ def_delegators :parent, :key, :key=
106
+ def_delegators :parent, :name, :abi, :bin
107
+ def_delegators :parent, :gas_limit, :gas_price, :gas_limit=, :gas_price=, :nonce, :nonce=
108
+ def_delegators :parent, :max_fee_per_gas, :max_fee_per_gas=, :max_priority_fee_per_gas, :max_priority_fee_per_gas=
109
+ def_delegators :parent, :events
110
+ def_delegators :parent, :address, :address=
111
+ def_delegator :parent, :functions
112
+ define_method :parent do
113
+ parent
114
+ end
115
+ end
116
+ Eth::Contract.send(:remove_const, class_name) if Eth::Contract.const_defined?(class_name, false)
117
+ Eth::Contract.const_set(class_name, class_methods)
118
+ @class_object = class_methods
119
+ end
120
+
121
+ private
122
+
123
+ def parse_abi(abi)
124
+ constructor = abi.detect { |x| x["type"] == "constructor" }
125
+ if !constructor.nil?
126
+ constructor_inputs = constructor["inputs"].map { |input| Eth::Contract::FunctionInput.new(input) }
127
+ else
128
+ constructor_inputs = []
129
+ end
130
+ functions = abi.select { |x| x["type"] == "function" }.map { |fun| Eth::Contract::Function.new(fun) }
131
+ events = abi.select { |x| x["type"] == "event" }.map { |evt| Eth::Contract::Event.new(evt) }
132
+ [constructor_inputs, functions, events]
133
+ end
134
+ end
135
+ end
@@ -63,7 +63,7 @@ module Eth
63
63
  # short string
64
64
  [:str, b0 - Constant::PRIMITIVE_PREFIX_OFFSET, start + 1]
65
65
  elsif b0 < Constant::LIST_PREFIX_OFFSET
66
- raise DecodingError, "Length starts with zero bytes" if rlp.slice(start + 1) == Constant::BYTE_ZERO
66
+ enforce_no_zero_bytes rlp, start
67
67
 
68
68
  # long string
69
69
  ll = b0 - Constant::PRIMITIVE_PREFIX_OFFSET - Constant::SHORT_LENGTH_LIMIT + 1
@@ -75,7 +75,7 @@ module Eth
75
75
  # short list
76
76
  [:list, b0 - Constant::LIST_PREFIX_OFFSET, start + 1]
77
77
  else
78
- raise DecodingError, "Length starts with zero bytes" if rlp.slice(start + 1) == Constant::BYTE_ZERO
78
+ enforce_no_zero_bytes rlp, start
79
79
 
80
80
  # long list
81
81
  ll = b0 - Constant::LIST_PREFIX_OFFSET - Constant::SHORT_LENGTH_LIMIT + 1
@@ -85,6 +85,11 @@ module Eth
85
85
  end
86
86
  end
87
87
 
88
+ # Enforce RLP slices to not start with empty bytes.
89
+ def enforce_no_zero_bytes(rlp, start)
90
+ raise DecodingError, "Length starts with zero bytes" if rlp.slice(start + 1) == Constant::BYTE_ZERO
91
+ end
92
+
88
93
  # Consume an RLP payload at the given position of given type and size.
89
94
  def consume_payload(rlp, start, type, length)
90
95
  case type
@@ -29,7 +29,7 @@ module Eth
29
29
  # A singleton class for binary values of fixed length.
30
30
  class << self
31
31
 
32
- # Create a serializable bianry of fixed size.
32
+ # Create a serializable binary of fixed size.
33
33
  #
34
34
  # @param l [Integer] the fixed size of the binary.
35
35
  # @param allow_empty [Boolean] indicator wether empty binaries should be allowed.
@@ -47,7 +47,7 @@ module Eth
47
47
  end
48
48
  end
49
49
 
50
- # Create a serializable bianry of variable size.
50
+ # Create a serializable binary of variable size.
51
51
  #
52
52
  # @param min_length [Integer] the minimum size of the binary.
53
53
  # @param max_length [Integer] the maximum size of the binary.