eth 0.5.1 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
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.