eth 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16b9900b6e0bd1a0bb51d055b545980685416a1e079590196fa9637e1c3e758e
4
- data.tar.gz: db3a4a72a0b14458dee3f4af836ac32bfc6ccf987b22e9f965551ddacecb09de
3
+ metadata.gz: 0375271f13b257337cbc0ab138c2cdf972de8f8e03cf60bb9b672b2221ac988d
4
+ data.tar.gz: 19e85a6be34904b5544d9c6845ae19231616d65f27a8146c747f66068f2c23db
5
5
  SHA512:
6
- metadata.gz: 90a00773ef20cdf765c19da284b09f20577fbaf26707d7b425f68109be9d559805603eabfd699c7ff68a56ac8d7619ecdaa841233a0d6af9bbd14521238a18e0
7
- data.tar.gz: 80127ab21325d386fbef1e57b13a1a5e769882d174114255b938babfd35c9e84ae3515ae28ace04300dfa38eb2059cb4eb529699378e9c354c5ed73d2ae0c6ce
6
+ metadata.gz: d5cc31684e8dfbff08b7250436a0bd4a94fa90942fd27b03292806cf2ac190673f1e507d8bcabd80b968a7206401a5210298c75c1ecc7c1d94765a7e94654d0d
7
+ data.tar.gz: 11b5a667d5cb333848aee384f4276fb3982a69104ae43c9650aec572ec3d093601c44b1b33167029b3ea37bcba6d8fd46da795ada67f6ad50fc39550fd092434
data/AUTHORS.txt CHANGED
@@ -2,6 +2,8 @@ The Ruby-Eth Contributors are:
2
2
  * Steve Ellis @se3000
3
3
  * Afri Schoedon @q9f
4
4
  * John Omar @chainoperator
5
+ * Joshua Peek @josh
6
+ * Yuta Kurotaki @kurotaky
5
7
 
6
8
  See also:
7
9
  * https://github.com/q9f/eth.rb/graphs/contributors
data/README.md CHANGED
@@ -46,6 +46,7 @@ Contents:
46
46
  - [2.6. Ethereum RLP Encoder and Decoder](#26-ethereum-rlp-encoder-and-decoder)
47
47
  - [2.7. Ethereum RPC-Client](#27-ethereum-rpc-client)
48
48
  - [2.8 Solidity Compiler Bindings](#28-solidity-compiler-bindings)
49
+ - [2.9 Interact with Smart Contract](#29-interact-with-smart-contract)
49
50
  - [3. Documentation](#3-documentation)
50
51
  - [4. Testing](#4-testing)
51
52
  - [5. Contributing](#5-contributing)
@@ -253,6 +254,23 @@ contract = solc.compile "spec/fixtures/contracts/greeter.sol"
253
254
 
254
255
  The `contract["Greeter"]["bin"]` could be directly used to deploy the contract as `Eth::Tx` payload. Check out the [Documentation](https://q9f.github.io/eth.rb/) for more details.
255
256
 
257
+ ### 2.9 Interact with Smart Contract
258
+
259
+ Functions to interact with smart contract.
260
+
261
+ ```ruby
262
+ contract = Eth::Contract.create(file: 'spec/fixtures/contracts/dummy.sol')
263
+ # => #<Eth::Contract::Dummy:0x00007fbeee936598>
264
+ cli = Eth::Client.create "/tmp/geth.ipc"
265
+ # => #<Eth::Client::Ipc:0x00007fbeee946128 @gas_limit=21000, @id=0, @max_fee_per_gas=0.2e11, @max_priority_fee_per_gas=0, @path="/tmp/geth.ipc">
266
+ address = cli.deploy_and_wait(contract)
267
+ # => "0x2f2faa160420cee087ded96bad52475147136bd8"
268
+ cli.transact_and_wait(contract, "set", 1234)
269
+ # => "0x49ca4c0a5729da19a1d2574de9a444a9cd3219bdad81745b54f9cf3bb83b6a06"
270
+ cli.call(contract, "get")
271
+ # => 1234
272
+ ```
273
+
256
274
  ## 3. Documentation
257
275
  The documentation can be found at: https://q9f.github.io/eth.rb
258
276
 
@@ -0,0 +1,137 @@
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 a Ruby implementation of the Ethereum Applicatoin Binary Interface (ABI).
21
+ module Abi
22
+
23
+ # Provides a module to decode transaction log events.
24
+ module Event
25
+ extend self
26
+
27
+ # Compute topic for ABI event interface.
28
+ #
29
+ # @param interface [Hash] ABI event interface.
30
+ # @return [String] a hex-string topic.
31
+ def compute_topic(interface)
32
+ sig = Abi.signature(interface)
33
+ Util.prefix_hex(Util.bin_to_hex(Util.keccak256(sig)))
34
+ end
35
+
36
+ # A decoded event log.
37
+ class LogDescription
38
+ # The event ABI interface used to decode the log.
39
+ attr_accessor :event_interface
40
+
41
+ # The the input argument of the event.
42
+ attr_accessor :args
43
+
44
+ # The named input argument of the event.
45
+ attr_accessor :kwargs
46
+
47
+ # The topic hash.
48
+ attr_accessor :topic
49
+
50
+ # Decodes event log argument values.
51
+ #
52
+ # @param event_interface [Hash] event ABI type.
53
+ # @param log [Hash] transaction receipt log
54
+ def initialize(event_interface, log)
55
+ @event_interface = event_interface
56
+
57
+ inputs = event_interface.fetch("inputs")
58
+ data = log.fetch("data")
59
+ topics = log.fetch("topics", [])
60
+ anonymous = event_interface.fetch("anonymous", false)
61
+
62
+ @topic = topics[0] if !anonymous
63
+ @args, @kwargs = Event.decode_log(inputs, data, topics, anonymous)
64
+ end
65
+
66
+ # The event name. (e.g. Transfer)
67
+ def name
68
+ @name ||= event_interface.fetch("name")
69
+ end
70
+
71
+ # The event signature. (e.g. Transfer(address,address,uint256))
72
+ def signature
73
+ @signature ||= Abi.signature(event_interface)
74
+ end
75
+ end
76
+
77
+ # Decodes a stream of receipt logs with a set of ABI interfaces.
78
+ #
79
+ # @param interfaces [Array] event ABI types.
80
+ # @param logs [Array] transaction receipt logs
81
+ # @return [Hash] an enumerator of LogDescription objects.
82
+ def decode_logs(interfaces, logs)
83
+ Enumerator.new do |y|
84
+ topic_to_interfaces = Hash[interfaces.map { |i| [compute_topic(i), i] }]
85
+
86
+ logs.each do |log|
87
+ topic = log.fetch("topics", [])[0]
88
+ if topic && interface = topic_to_interfaces[topic]
89
+ y << [log, LogDescription.new(interface, log)]
90
+ else
91
+ y << [log, nil]
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ # Decodes event log argument values.
98
+ #
99
+ # @param inputs [Array] event ABI types.
100
+ # @param data [String] ABI event data to be decoded.
101
+ # @param topics [Array] ABI event topics to be decoded.
102
+ # @param anonymous [Boolean] If event signature is excluded from topics.
103
+ # @return [[Array, Hash]] decoded positional arguments and decoded keyword arguments.
104
+ # @raise [DecodingError] if decoding fails for type.
105
+ def decode_log(inputs, data, topics, anonymous = false)
106
+ topic_inputs, data_inputs = inputs.partition { |i| i["indexed"] }
107
+
108
+ topic_types = topic_inputs.map { |i| i["type"] }
109
+ data_types = data_inputs.map { |i| i["type"] }
110
+
111
+ # If event is anonymous, all topics are arguments. Otherwise, the first
112
+ # topic will be the event signature.
113
+ if anonymous == false
114
+ topics = topics[1..-1]
115
+ end
116
+
117
+ decoded_topics = topics.map.with_index { |t, i| Abi.decode([topic_types[i]], t)[0] }
118
+ decoded_data = Abi.decode(data_types, data)
119
+
120
+ args = []
121
+ kwargs = {}
122
+
123
+ inputs.each_with_index do |input, index|
124
+ if input["indexed"]
125
+ value = decoded_topics[topic_inputs.index(input)]
126
+ else
127
+ value = decoded_data[data_inputs.index(input)]
128
+ end
129
+ args[index] = value
130
+ kwargs[input["name"].to_sym] = value
131
+ end
132
+
133
+ return args, kwargs
134
+ end
135
+ end
136
+ end
137
+ end
data/lib/eth/abi.rb CHANGED
@@ -16,6 +16,7 @@
16
16
 
17
17
  require "konstructor"
18
18
 
19
+ require "eth/abi/event"
19
20
  require "eth/abi/type"
20
21
 
21
22
  # Provides the {Eth} module.
@@ -290,6 +291,17 @@ module Eth
290
291
  end
291
292
  end
292
293
 
294
+ # Build event signature string from ABI interface.
295
+ #
296
+ # @param interface [Hash] ABI event interface.
297
+ # @return [String] interface signature string.
298
+ def signature(interface)
299
+ name = interface.fetch("name")
300
+ inputs = interface.fetch("inputs", [])
301
+ types = inputs.map { |i| i.fetch("type") }
302
+ "#{name}(#{types.join(",")})"
303
+ end
304
+
293
305
  private
294
306
 
295
307
  # Properly encodes unsigned integers.
data/lib/eth/client.rb CHANGED
@@ -149,6 +149,242 @@ module Eth
149
149
  end
150
150
  end
151
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
+
152
388
  # Gives control over resetting the RPC request ID back to zero.
153
389
  # Usually not needed.
154
390
  #
@@ -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,120 @@
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.
41
+ #
42
+ # @param file [String] solidity file path.
43
+ # @param bin [String] contract bin string.
44
+ # @param abi [String] contract abi string.
45
+ # @param address [String] contract address.
46
+ # @param name [String] name of contract.
47
+ # @param contract_index [Number] specify contract.
48
+ # @return [Eth::Contract::Object] Returns the class of the smart contract.
49
+ # @raise [JSON::ParserError] if the json format is wrong.
50
+ # @raise [ArgumentError] if argument is incorrect.
51
+ def self.create(file: nil, bin: nil, abi: nil, address: nil, name: nil, contract_index: nil)
52
+ if File.exist?(file.to_s)
53
+ contracts = Eth::Contract::Initializer.new(file).build_all
54
+ raise "No contracts compiled" if contracts.empty?
55
+ if contract_index
56
+ contract = contracts[contract_index].class_object.new
57
+ else
58
+ contract = contracts.first.class_object.new
59
+ end
60
+ elsif ![name, bin, abi].include? nil
61
+ begin
62
+ abi = abi.is_a?(Array) ? abi : JSON.parse(abi)
63
+ rescue JSON::ParserError => e
64
+ raise e
65
+ end
66
+ contract = Eth::Contract.new(name, bin, abi)
67
+ contract.build
68
+ contract = contract.class_object.new
69
+ else
70
+ raise ArgumentError, "The argument is incorrect."
71
+ end
72
+ contract.address = address
73
+ contract
74
+ end
75
+
76
+ # Set the address of the smart contract
77
+ def address=(addr)
78
+ @address = addr.nil? ? nil : Eth::Address.new(addr).address
79
+ @events.each do |event|
80
+ event.set_address(@address)
81
+ end
82
+ end
83
+
84
+ # Create classes for smart contracts
85
+ def build
86
+ class_name = @name
87
+ parent = self
88
+ class_methods = Class.new do
89
+ extend Forwardable
90
+ def_delegators :parent, :key, :key=
91
+ def_delegators :parent, :name, :abi, :bin
92
+ def_delegators :parent, :gas_limit, :gas_price, :gas_limit=, :gas_price=, :nonce, :nonce=
93
+ def_delegators :parent, :max_fee_per_gas, :max_fee_per_gas=, :max_priority_fee_per_gas, :max_priority_fee_per_gas=
94
+ def_delegators :parent, :events
95
+ def_delegators :parent, :address, :address=
96
+ def_delegator :parent, :functions
97
+ define_method :parent do
98
+ parent
99
+ end
100
+ end
101
+ Eth::Contract.send(:remove_const, class_name) if Eth::Contract.const_defined?(class_name, false)
102
+ Eth::Contract.const_set(class_name, class_methods)
103
+ @class_object = class_methods
104
+ end
105
+
106
+ private
107
+
108
+ def parse_abi(abi)
109
+ constructor = abi.detect { |x| x["type"] == "constructor" }
110
+ if !constructor.nil?
111
+ constructor_inputs = constructor["inputs"].map { |input| Eth::Contract::FunctionInput.new(input) }
112
+ else
113
+ constructor_inputs = []
114
+ end
115
+ functions = abi.select { |x| x["type"] == "function" }.map { |fun| Eth::Contract::Function.new(fun) }
116
+ events = abi.select { |x| x["type"] == "event" }.map { |evt| Eth::Contract::Event.new(evt) }
117
+ [constructor_inputs, functions, events]
118
+ end
119
+ end
120
+ end
data/lib/eth/tx.rb CHANGED
@@ -69,6 +69,9 @@ module Eth
69
69
  # The zero byte is 0x00.
70
70
  ZERO_BYTE = "\x00".freeze
71
71
 
72
+ # Smart contract transaction gas cost
73
+ CREATE_GAS = 32_000.freeze
74
+
72
75
  # Creates a new transaction of any type for given parameters and chain ID.
73
76
  # Required parameters are (optional in brackets):
74
77
  # - EIP-1559: chain_id, nonce, priority_fee, max_gas_fee, gas_limit(, from, to,
data/lib/eth/version.rb CHANGED
@@ -16,5 +16,5 @@
16
16
  module Eth
17
17
 
18
18
  # Defines the version of the {Eth} module.
19
- VERSION = "0.5.2".freeze
19
+ VERSION = "0.5.3".freeze
20
20
  end
data/lib/eth.rb CHANGED
@@ -22,6 +22,12 @@ require "eth/api"
22
22
  require "eth/address"
23
23
  require "eth/chain"
24
24
  require "eth/constant"
25
+ require "eth/contract"
26
+ require "eth/contract/event"
27
+ require "eth/contract/function"
28
+ require "eth/contract/function_input"
29
+ require "eth/contract/function_output"
30
+ require "eth/contract/initializer"
25
31
  require "eth/client"
26
32
  require "eth/client/http"
27
33
  require "eth/client/ipc"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Ellis
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2022-03-28 00:00:00.000000000 Z
12
+ date: 2022-05-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: keccak
@@ -108,6 +108,7 @@ files:
108
108
  - eth.gemspec
109
109
  - lib/eth.rb
110
110
  - lib/eth/abi.rb
111
+ - lib/eth/abi/event.rb
111
112
  - lib/eth/abi/type.rb
112
113
  - lib/eth/address.rb
113
114
  - lib/eth/api.rb
@@ -116,6 +117,12 @@ files:
116
117
  - lib/eth/client/http.rb
117
118
  - lib/eth/client/ipc.rb
118
119
  - lib/eth/constant.rb
120
+ - lib/eth/contract.rb
121
+ - lib/eth/contract/event.rb
122
+ - lib/eth/contract/function.rb
123
+ - lib/eth/contract/function_input.rb
124
+ - lib/eth/contract/function_output.rb
125
+ - lib/eth/contract/initializer.rb
119
126
  - lib/eth/eip712.rb
120
127
  - lib/eth/key.rb
121
128
  - lib/eth/key/decrypter.rb