eth 0.5.2 → 0.5.3

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.
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