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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: afa7f2461a4f8f594fc3e8d57cf53d92e8540a55
4
- data.tar.gz: 88e3b4d2066800644e8f1b82a6afaf8eb876fa47
3
+ metadata.gz: fe82a37129944871e8670fbc4dde4818fdbc5c7a
4
+ data.tar.gz: 144ff632e0837e2eb72d64900aba51791250e26b
5
5
  SHA512:
6
- metadata.gz: 1caa864078b37ff71e1e48e779a6f595a42a31e8bd839bf8cd886a397ab90efd6dbee68f4f22e858df89aa227497450ef5a3d04c62a2aec48a0a85fc2d3a3294
7
- data.tar.gz: 28eaa1419c7cde34d3daadc452178d03baa92fd24b49a85b1768896c2b678438105ce4e0f4289ec828603d7c7f8fa5320d4cb6c29ebb11324ae161ce19b261dd
6
+ metadata.gz: dc154bb5b6c11573df10f15d8d05e3d08b4094970b9be4f36053e29d2c267357dc51c75c51db45656ab1435a1e3428a26855d1cdfc2537d4d049ca43ac8572ea
7
+ data.tar.gz: 0b0d6d9b5e79220ca4ecec3f93cb9cca1b91b63eaae5aa51e2aeaba2c2e8be4bd9dc1696b7b4872f05ed228fe097e068f78e9a62c73820aaa7a6fc4b7a5877d4
data/.gitignore CHANGED
@@ -9,3 +9,5 @@
9
9
  /tmp/
10
10
  /.*.swp
11
11
  /**/.*.swp
12
+
13
+ .idea
@@ -1,7 +1,9 @@
1
1
  dist: trusty
2
2
  language: ruby
3
3
  rvm:
4
- - 2.3.1
4
+ - 2.3.6
5
+ - 2.4.3
6
+ - 2.5.0
5
7
  env:
6
8
  - PARITY="1.6.10"
7
9
  cache:
@@ -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/ethcore/parity/releases) and install on your computer.
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
 
@@ -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", "~> 5.0"
34
+ spec.add_dependency "activesupport", ">= 4.0"
35
35
  spec.add_dependency "digest-sha3", "~> 1.1"
36
36
  end
@@ -73,7 +73,7 @@ module Ethereum
73
73
  end
74
74
 
75
75
  def get_nonce(address)
76
- eth_get_transaction_count(address, "latest")["result"].to_i(16)
76
+ eth_get_transaction_count(address, "pending")["result"].to_i(16)
77
77
  end
78
78
 
79
79
 
@@ -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
- def self.create(file: nil, client: Ethereum::Singleton.instance, code: nil, abi: nil, address: nil, name: nil, contract_index: nil)
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
- abi = JSON.parse(abi) if abi.is_a? String
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?
@@ -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
- if ssl
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
- http = ::Net::HTTP.new(@host, @port)
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
- return response.body
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
- return result.sort_by! { |c| c['id'] }
40
+ result.sort_by! { |c| c['id'] }
38
41
  end
39
42
  end
40
43
 
@@ -14,7 +14,7 @@ module Ethereum
14
14
 
15
15
  def initialize(bin_path = "solc")
16
16
  @bin_path = bin_path
17
- @args = "--bin --abi --add-std --optimize"
17
+ @args = "--bin --abi --optimize"
18
18
  end
19
19
 
20
20
  def compile(filename)
@@ -1,3 +1,3 @@
1
1
  module Ethereum
2
- VERSION = "2.1.8"
2
+ VERSION = "2.2"
3
3
  end
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.1.8
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: 2017-11-01 00:00:00.000000000 Z
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: '5.0'
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: '5.0'
96
+ version: '4.0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: digest-sha3
99
99
  requirement: !ruby/object:Gem::Requirement