ethereum.rb 2.1.8 → 2.2
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/.gitignore +2 -0
- data/.travis.yml +3 -1
- data/PREREQUISITES.md +1 -1
- data/README.md +48 -1
- data/ethereum.gemspec +1 -1
- data/lib/ethereum/client.rb +1 -1
- data/lib/ethereum/contract.rb +94 -5
- data/lib/ethereum/http_client.rb +13 -10
- data/lib/ethereum/solidity.rb +1 -1
- data/lib/ethereum/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe82a37129944871e8670fbc4dde4818fdbc5c7a
|
4
|
+
data.tar.gz: 144ff632e0837e2eb72d64900aba51791250e26b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc154bb5b6c11573df10f15d8d05e3d08b4094970b9be4f36053e29d2c267357dc51c75c51db45656ab1435a1e3428a26855d1cdfc2537d4d049ca43ac8572ea
|
7
|
+
data.tar.gz: 0b0d6d9b5e79220ca4ecec3f93cb9cca1b91b63eaae5aa51e2aeaba2c2e8be4bd9dc1696b7b4872f05ed228fe097e068f78e9a62c73820aaa7a6fc4b7a5877d4
|
data/.travis.yml
CHANGED
data/PREREQUISITES.md
CHANGED
@@ -16,7 +16,7 @@ Currently the only node supported by ethereum.rb is [parity](https://ethcore.io/
|
|
16
16
|
$ brew tap ethcore/ethcore
|
17
17
|
$ brew install parity --beta
|
18
18
|
|
19
|
-
To install parity on linux download latest package from [parity github](https://github.com/
|
19
|
+
To install parity on linux download latest package from [parity github](https://github.com/paritytech/parity/releases) and install on your computer.
|
20
20
|
|
21
21
|
It might work with [geth](https://github.com/ethereum/go-ethereum/wiki/geth) as well, but this configuration is not tested.
|
22
22
|
|
data/README.md
CHANGED
@@ -96,6 +96,21 @@ If you want to create new contract, that is not yet deployed from ABI definition
|
|
96
96
|
contract = Ethereum::Contract.create(name: "MyContract", abi: abi, code: "...")
|
97
97
|
```
|
98
98
|
|
99
|
+
### Simple Truffle integration
|
100
|
+
|
101
|
+
If you use Truffle to build and deploy contracts, you can pick up the Truffle artifacts to initialize
|
102
|
+
a contract. For example, if you have a MyContract in the Truffle directory at `/my/truffle/project`:
|
103
|
+
|
104
|
+
```
|
105
|
+
contract = Ethereum::Contract.create(name: "MyContract", truffle: { paths: [ '/my/truffle/project' ] }, client: client, address: '0x01a4d1A62F01ED966646acBfA8BB0b59960D06dd')
|
106
|
+
```
|
107
|
+
|
108
|
+
The contract factory will attempt to load the deployed address from the Truffle artifacts if the client's network is present:
|
109
|
+
|
110
|
+
```
|
111
|
+
contract = Ethereum::Contract.create(name: "MyContract", truffle: { paths: [ '/my/truffle/project' ] }, client: client)
|
112
|
+
```
|
113
|
+
|
99
114
|
### Interacting with contract
|
100
115
|
|
101
116
|
Functions defined in a contract are exposed using the following conventions:
|
@@ -109,11 +124,12 @@ contract.call.[function_name](params)
|
|
109
124
|
**Example Contract in Solidity**
|
110
125
|
```
|
111
126
|
contract SimpleRegistry {
|
112
|
-
|
127
|
+
event LogRegister(bytes32 key, string value);
|
113
128
|
mapping (bytes32 => string) public registry;
|
114
129
|
|
115
130
|
function register(bytes32 key, string value) {
|
116
131
|
registry[key] = value;
|
132
|
+
LogRegister(key, value);
|
117
133
|
}
|
118
134
|
|
119
135
|
function get(bytes32 key) public constant returns(string) {
|
@@ -145,6 +161,37 @@ Will call method of the contract and return result.
|
|
145
161
|
Note that no transaction need to be send to the network as method is read-only.
|
146
162
|
On the other hand `register` method will change contract state, so you need to use `transact` or `transact_and_wait` to call it.
|
147
163
|
|
164
|
+
### Receiving Contract Events
|
165
|
+
|
166
|
+
Using the example smart contract described above, one can listen for `LogRegister` events by using filters.
|
167
|
+
|
168
|
+
You can get a list of events from a certain block number to the latest:
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
require 'ostruct'
|
172
|
+
|
173
|
+
event_abi = contract.abi.find {|a| a['name'] == 'LogRegister'}
|
174
|
+
event_inputs = event_abi['inputs'].map {|i| OpenStruct.new(i)}
|
175
|
+
decoder = Ethereum::Decoder.new
|
176
|
+
|
177
|
+
filter_id = contract.new_filter.log_register(
|
178
|
+
{
|
179
|
+
from_block: '0x0',
|
180
|
+
to_block: 'latest',
|
181
|
+
address: '0x....',
|
182
|
+
topics: []
|
183
|
+
}
|
184
|
+
)
|
185
|
+
|
186
|
+
events = contract.get_filter_logs.log_register(filter_id)
|
187
|
+
|
188
|
+
events.each do |event|
|
189
|
+
transaction_id = event[:transactionHash]
|
190
|
+
transaction = ethereum.eth_get_transaction_receipt(transaction_id)
|
191
|
+
args = decoder.decode_arguments(event_inputs, entry['data'])
|
192
|
+
puts "#{transaction.inspect} with args: #{args}"
|
193
|
+
end
|
194
|
+
```
|
148
195
|
|
149
196
|
### IPC Client Connection
|
150
197
|
|
data/ethereum.gemspec
CHANGED
@@ -31,6 +31,6 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_development_dependency "pry", "~> 0.10"
|
32
32
|
spec.add_development_dependency "eth", "~> 0.4"
|
33
33
|
|
34
|
-
spec.add_dependency "activesupport", "
|
34
|
+
spec.add_dependency "activesupport", ">= 4.0"
|
35
35
|
spec.add_dependency "digest-sha3", "~> 1.1"
|
36
36
|
end
|
data/lib/ethereum/client.rb
CHANGED
data/lib/ethereum/contract.rb
CHANGED
@@ -5,7 +5,7 @@ module Ethereum
|
|
5
5
|
|
6
6
|
attr_reader :address
|
7
7
|
attr_accessor :key
|
8
|
-
attr_accessor :gas_limit, :gas_price
|
8
|
+
attr_accessor :gas_limit, :gas_price, :nonce
|
9
9
|
attr_accessor :code, :name, :abi, :class_object, :sender, :deployment, :client
|
10
10
|
attr_accessor :events, :functions, :constructor_inputs
|
11
11
|
attr_accessor :call_raw_proxy, :call_proxy, :transact_proxy, :transact_and_wait_proxy
|
@@ -25,7 +25,34 @@ module Ethereum
|
|
25
25
|
@gas_price = @client.gas_price
|
26
26
|
end
|
27
27
|
|
28
|
-
|
28
|
+
# Creates a contract wrapper.
|
29
|
+
# This method attempts to instantiate a contract object from a Solidity source file, from a Truffle
|
30
|
+
# artifacts file, or from explicit API and bytecode.
|
31
|
+
# - If *:file* is present, the method compiles it and looks up the contract factory at *:contract_index*
|
32
|
+
# (0 if not provided). *:abi* and *:code* are ignored.
|
33
|
+
# - If *:truffle* is present, the method looks up the Truffle artifacts data for *:name* and uses
|
34
|
+
# those data to build a contract instance.
|
35
|
+
# - Otherwise, the method uses *:name*, *:code*, and *:abi* to build the contract instance.
|
36
|
+
#
|
37
|
+
# @param opts [Hash] Options to the method.
|
38
|
+
# @option opts [String] :file Path to the Solidity source that contains the contract code.
|
39
|
+
# @option opts [Ethereum::Singleton] :client The client to use.
|
40
|
+
# @option opts [String] :code The hex representation of the contract's bytecode.
|
41
|
+
# @option opts [Array,String] :abi The contract's ABI; a string is assumed to contain a JSON representation
|
42
|
+
# of the ABI.
|
43
|
+
# @option opts [String] :address The contract's address; if not present and +:truffle+ is present,
|
44
|
+
# the method attempts to determine the address from the artifacts' +networks+ key and the client's
|
45
|
+
# network id.
|
46
|
+
# @option opts [String] :name The contract name.
|
47
|
+
# @option opts [Integer] :contract_index The index of the contract data in the compiled file.
|
48
|
+
# @option opts [Hash] :truffle If this parameter is present, the method uses Truffle information to
|
49
|
+
# generate the contract wrapper.
|
50
|
+
# - *:paths* An array of strings containing the list of paths where to look up Truffle artifacts files.
|
51
|
+
# See also {#find_truffle_artifacts}.
|
52
|
+
#
|
53
|
+
# @return [Ethereum::Contract] Returns a contract wrapper.
|
54
|
+
|
55
|
+
def self.create(file: nil, client: Ethereum::Singleton.instance, code: nil, abi: nil, address: nil, name: nil, contract_index: nil, truffle: nil)
|
29
56
|
contract = nil
|
30
57
|
if file.present?
|
31
58
|
contracts = Ethereum::Initializer.new(file, client).build_all
|
@@ -36,7 +63,29 @@ module Ethereum
|
|
36
63
|
contract = contracts.first.class_object.new
|
37
64
|
end
|
38
65
|
else
|
39
|
-
|
66
|
+
if truffle.present? && truffle.is_a?(Hash)
|
67
|
+
artifacts = find_truffle_artifacts(name, (truffle[:paths].is_a?(Array)) ? truffle[:paths] : [])
|
68
|
+
if artifacts
|
69
|
+
abi = artifacts['abi']
|
70
|
+
# The truffle artifacts store bytecodes with a 0x tag, which we need to remove
|
71
|
+
# this may need to be 'deployedBytecode'
|
72
|
+
code_key = artifacts['bytecode'].present? ? 'bytecode' : 'unlinked_binary'
|
73
|
+
code = (artifacts[code_key].start_with?('0x')) ? artifacts[code_key][2, artifacts[code_key].length] : artifacts[code_key]
|
74
|
+
unless address
|
75
|
+
address = if client
|
76
|
+
network_id = client.net_version['result']
|
77
|
+
(artifacts['networks'][network_id]) ? artifacts['networks'][network_id]['address'] : nil
|
78
|
+
else
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
else
|
83
|
+
abi = nil
|
84
|
+
code = nil
|
85
|
+
end
|
86
|
+
else
|
87
|
+
abi = JSON.parse(abi) if abi.is_a? String
|
88
|
+
end
|
40
89
|
contract = Ethereum::Contract.new(name, code, abi, client)
|
41
90
|
contract.build
|
42
91
|
contract = contract.class_object.new
|
@@ -114,7 +163,7 @@ module Ethereum
|
|
114
163
|
end
|
115
164
|
|
116
165
|
def call_payload(fun, args)
|
117
|
-
"0x" + fun.signature + @encoder.encode_arguments(fun.inputs, args)
|
166
|
+
"0x" + fun.signature + (@encoder.encode_arguments(fun.inputs, args).presence || "0"*64)
|
118
167
|
end
|
119
168
|
|
120
169
|
def call_args(fun, args)
|
@@ -199,7 +248,7 @@ module Ethereum
|
|
199
248
|
extend Forwardable
|
200
249
|
def_delegators :parent, :deploy_payload, :deploy_args, :call_payload, :call_args
|
201
250
|
def_delegators :parent, :signed_deploy, :key, :key=
|
202
|
-
def_delegators :parent, :gas_limit, :gas_price, :gas_limit=, :gas_price=
|
251
|
+
def_delegators :parent, :gas_limit, :gas_price, :gas_limit=, :gas_price=, :nonce, :nonce=
|
203
252
|
def_delegators :parent, :abi, :deployment, :events
|
204
253
|
def_delegators :parent, :estimate, :deploy, :deploy_and_wait
|
205
254
|
def_delegators :parent, :address, :address=, :sender, :sender=
|
@@ -219,6 +268,46 @@ module Ethereum
|
|
219
268
|
@class_object = class_methods
|
220
269
|
end
|
221
270
|
|
271
|
+
# Get the list of paths where to look up Truffle artifacts files.
|
272
|
+
#
|
273
|
+
# @return [Array<String>] Returns the array containing the list of lookup paths.
|
274
|
+
|
275
|
+
def self.truffle_paths()
|
276
|
+
@truffle_paths = [] unless @truffle_paths
|
277
|
+
@truffle_paths
|
278
|
+
end
|
279
|
+
|
280
|
+
# Set the list of paths where to look up Truffle artifacts files.
|
281
|
+
#
|
282
|
+
# @param paths [Array<String>, nil] The array containing the list of lookup paths; a `nil` value is
|
283
|
+
# converted to the empty array.
|
284
|
+
|
285
|
+
def self.truffle_paths=(paths)
|
286
|
+
@truffle_paths = (paths.is_a?(Array)) ? paths : []
|
287
|
+
end
|
288
|
+
|
289
|
+
# Looks up and loads a Truffle artifacts file.
|
290
|
+
# This method iterates over the `truffle_path` elements, looking for an artifact file in the
|
291
|
+
# `build/contracts` subdirectory.
|
292
|
+
#
|
293
|
+
# @param name [String] The name of the contract whose artifacts to look up.
|
294
|
+
# @param paths [Array<String>] An additional list of paths to look up; this list, if present, is
|
295
|
+
# prepended to the `truffle_path`.
|
296
|
+
#
|
297
|
+
# @return [Hash,nil] Returns a hash containing the parsed JSON from the artifacts file; if no file
|
298
|
+
# was found, returns `nil`.
|
299
|
+
|
300
|
+
def self.find_truffle_artifacts(name, paths = [])
|
301
|
+
subpath = File.join('build', 'contracts', "#{name}.json")
|
302
|
+
|
303
|
+
found = paths.concat(truffle_paths).find { |p| File.file?(File.join(p, subpath)) }
|
304
|
+
if (found)
|
305
|
+
JSON.parse(IO.read(File.join(found, subpath)))
|
306
|
+
else
|
307
|
+
nil
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
222
311
|
private
|
223
312
|
def add_gas_options_args(args)
|
224
313
|
args[:gas] = @client.int_to_hex(gas_limit) if gas_limit.present?
|
data/lib/ethereum/http_client.rb
CHANGED
@@ -2,25 +2,28 @@ require 'net/http'
|
|
2
2
|
require 'json'
|
3
3
|
module Ethereum
|
4
4
|
class HttpClient < Client
|
5
|
-
attr_accessor :host, :port, :uri, :ssl
|
5
|
+
attr_accessor :host, :port, :uri, :ssl, :proxy
|
6
6
|
|
7
|
-
def initialize(host, log = false)
|
7
|
+
def initialize(host, proxy = nil, log = false)
|
8
8
|
super(log)
|
9
9
|
uri = URI.parse(host)
|
10
10
|
raise ArgumentError unless ['http', 'https'].include? uri.scheme
|
11
11
|
@host = uri.host
|
12
12
|
@port = uri.port
|
13
|
+
@proxy = proxy
|
13
14
|
|
14
15
|
@ssl = uri.scheme == 'https'
|
15
|
-
|
16
|
-
@uri = URI("https://#{@host}:#{@port}")
|
17
|
-
else
|
18
|
-
@uri = URI("http://#{@host}:#{@port}")
|
19
|
-
end
|
16
|
+
@uri = URI("#{uri.scheme}://#{@host}:#{@port}#{uri.path}")
|
20
17
|
end
|
21
18
|
|
22
19
|
def send_single(payload)
|
23
|
-
|
20
|
+
if @proxy.present?
|
21
|
+
_, p_username, p_password, p_host, p_port = @proxy.gsub(/(:|\/|@)/,' ').squeeze(' ').split
|
22
|
+
http = ::Net::HTTP.new(@host, @port, p_host, p_port, p_username, p_password)
|
23
|
+
else
|
24
|
+
http = ::Net::HTTP.new(@host, @port)
|
25
|
+
end
|
26
|
+
|
24
27
|
if @ssl
|
25
28
|
http.use_ssl = true
|
26
29
|
end
|
@@ -28,13 +31,13 @@ module Ethereum
|
|
28
31
|
request = ::Net::HTTP::Post.new(uri, header)
|
29
32
|
request.body = payload
|
30
33
|
response = http.request(request)
|
31
|
-
|
34
|
+
response.body
|
32
35
|
end
|
33
36
|
|
34
37
|
def send_batch(batch)
|
35
38
|
result = send_single(batch.to_json)
|
36
39
|
result = JSON.parse(result)
|
37
|
-
|
40
|
+
result.sort_by! { |c| c['id'] }
|
38
41
|
end
|
39
42
|
end
|
40
43
|
|
data/lib/ethereum/solidity.rb
CHANGED
data/lib/ethereum/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ethereum.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: '2.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marek Kirejczyk
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -84,16 +84,16 @@ dependencies:
|
|
84
84
|
name: activesupport
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '4.0'
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '4.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: digest-sha3
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|