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 +4 -4
- data/AUTHORS.txt +2 -0
- data/README.md +18 -0
- data/lib/eth/abi/event.rb +137 -0
- data/lib/eth/abi.rb +12 -0
- data/lib/eth/client.rb +236 -0
- data/lib/eth/contract/event.rb +41 -0
- data/lib/eth/contract/function.rb +56 -0
- data/lib/eth/contract/function_input.rb +36 -0
- data/lib/eth/contract/function_output.rb +32 -0
- data/lib/eth/contract/initializer.rb +46 -0
- data/lib/eth/contract.rb +120 -0
- data/lib/eth/tx.rb +3 -0
- data/lib/eth/version.rb +1 -1
- data/lib/eth.rb +6 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0375271f13b257337cbc0ab138c2cdf972de8f8e03cf60bb9b672b2221ac988d
|
4
|
+
data.tar.gz: 19e85a6be34904b5544d9c6845ae19231616d65f27a8146c747f66068f2c23db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5cc31684e8dfbff08b7250436a0bd4a94fa90942fd27b03292806cf2ac190673f1e507d8bcabd80b968a7206401a5210298c75c1ecc7c1d94765a7e94654d0d
|
7
|
+
data.tar.gz: 11b5a667d5cb333848aee384f4276fb3982a69104ae43c9650aec572ec3d093601c44b1b33167029b3ea37bcba6d8fd46da795ada67f6ad50fc39550fd092434
|
data/AUTHORS.txt
CHANGED
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
|
data/lib/eth/contract.rb
ADDED
@@ -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
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.
|
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-
|
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
|