etherlite 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cfe990bc167b90c43ca3ac261196ad69d527b01e
4
- data.tar.gz: b42eb161febc5b28e147bbfb77db9e8a7ff3c755
3
+ metadata.gz: cb97370a946be81259847d2b1f3c7a6da33593e2
4
+ data.tar.gz: 373696bf36bcf5b5b7889ffe7eadfd0bf3544881
5
5
  SHA512:
6
- metadata.gz: 2ecfa99699759f4fbadabf6cdbc8198f8efb75f52da7c5f3332f26eb5ae0378124c435726e2b000e043ecd1e2a541932065f8549be70be3d5ca34526db7cedb5
7
- data.tar.gz: 194c0493e1db6a1e32f7eec88f7ae1362b6758facc0ffb9ae216b2751a7d0c6bc75c7a7178a384d76215d61b12fcbc58fd54643febfb9f5bfedef76ad80bb604
6
+ metadata.gz: f45918243e835c01d28c6beb7f3c42ffb77fa35047aab3051aba1f555eb4a65c6e6d7dc0b218a4efbdcb1efef3b3fd3b0b2a1e79f4bd22f202e43085169fc5f7
7
+ data.tar.gz: 22a6b1e87b0b9778de84607fd2d7d6cc59e35b14c14928ef984d4bfa32901f82d549f7c90ee018071847c69c1ba887e6ec4216989456b5d98efd8c278679d362
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Etherlite
2
2
 
3
- Etherlite is a
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/etherlite`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
4
6
 
5
7
  ## Installation
6
8
 
@@ -14,101 +16,13 @@ And then execute:
14
16
 
15
17
  $ bundle
16
18
 
17
- Initialize the configuration file and `contracts` folder:
18
-
19
- $ rails g etherlite:init
19
+ Or install it yourself as:
20
20
 
21
- A `etherlite.yml` file will be added to your app's `config` folder, there you can configure the node locations and some other variables for each enviroment.
21
+ $ gem install etherlite
22
22
 
23
23
  ## Usage
24
24
 
25
- This gem is intended to be used with contract artifacts generated by Truffle. To start interacting with your contracts, drop the artifacts for the contracts you will be using in the application `contracts` folder. (You can find the contract artifacts on the `build/contracts` folder of your truffle proyect).
26
-
27
- Rails will automatically load the artifacts as ruby classes.
28
-
29
- For example, given the following contract:
30
-
31
- ```solidity
32
- contract BikeStore {
33
- event BikeSold(address buyer, string model);
34
- event BikeReturned(address buyer, string reason);
35
-
36
- function buyBike(string _model) payable {
37
- // some code
38
- }
39
-
40
- function getBikePrice(string _model) returns (uint price) {
41
- return _return;
42
- }
43
- }
44
- ```
45
-
46
- By copying the contract's artifact file in the `contracts` folder you will be able to call:
47
-
48
- ```ruby
49
- my_store = BikeStoreContract.at('0xAAAAAAAAAAAAAAAAA') # notice the "Contract" sufix
50
- price = my_store.get_bike_price
51
- ```
52
-
53
- ### Accounts
54
-
55
- Etherlite supports both node managed accounts and private key accounts (via the `eth` gem).
56
-
57
- By default etherlite will try to use the first account from the configured node.
58
-
59
- ```ruby
60
- Etherlite.default_account # this should reference the node's first account (if present)
61
- ```
62
-
63
- When executing operations that need to send a transaction you will need to provide the account's passphrase. You can set the `passphrase` option in the configuration file:
64
-
65
- ```yml
66
- development:
67
- passphrase: <%= ENV['STORE_PASSPHRASE'] %>
68
- ```
69
-
70
- or pass it on any transaction-triggering call:
71
-
72
- ```ruby
73
- my_store.buy_bike('mongoose', passphrase: 'verysecure')
74
- ```
75
-
76
- If you need to use a private key account, then set the `private_key` option in the configuration file with the account's private key. Etherlite will automatically switch to using a private key account instead of the node account:
77
-
78
- ```yml
79
- development:
80
- private_key: <%= ENV['STORE_PRIVATE_KEY'] %>
81
- ```
82
-
83
- ```ruby
84
- Etherlite.default_account.address # this should return the private_key corresponding address
85
- ```
86
-
87
- If you wish to programatically load a private key account you can call:
88
-
89
- ```ruby
90
- account = Etherlite.account_from_pk(your_private_key)
91
- ```
92
-
93
- ### Creating contract instances
94
-
95
-
96
-
97
- ### Calling contract methods
98
-
99
- #### Setting gas limit and price
100
-
101
- ### Transferring ether
102
-
103
-
104
- ### Waiting for transactions
105
-
106
-
107
- ### Searching event logs
108
-
109
- ### Testing with Etherlite, rspec and testrpc
110
-
111
- Etherlite (will) provides a set of rspec helpers to make
25
+ TODO: Write usage instructions here
112
26
 
113
27
  ## Development
114
28
 
@@ -120,6 +34,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
120
34
 
121
35
  Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/etherlite. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
122
36
 
37
+
123
38
  ## License
124
39
 
125
40
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/lib/etherlite.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "digest/sha3"
2
2
  require "active_support/all"
3
+ require "forwardable"
3
4
  require "power-types"
4
5
 
5
6
  require "etherlite/version"
@@ -25,6 +26,7 @@ require "etherlite/contract/base"
25
26
  require "etherlite/contract/event_base"
26
27
  require "etherlite/contract/event_input"
27
28
  require "etherlite/contract/function"
29
+ require "etherlite/contract/function_input"
28
30
 
29
31
  require "etherlite/errors"
30
32
  require "etherlite/configuration"
data/lib/etherlite/abi.rb CHANGED
@@ -3,58 +3,17 @@ require 'etherlite/commands/abi/load_contract'
3
3
  require 'etherlite/commands/abi/load_function'
4
4
 
5
5
  module Etherlite
6
- ##
7
- # A set of ABI related utilities
8
- #
9
6
  module Abi
10
7
  extend self
11
8
 
12
- ##
13
- # Load a contract artifact file.
14
- #
15
- # This method parses a truffle artifact file and loads a **contract** class.
16
- #
17
- # The generated contract class exposes a series of methods to interact with an instance
18
- # of the contract described by the artifact.
19
- #
20
- # ```
21
- # contract_class = Etherlite::Abi.load_contract_at('/path/to/Contract.json')
22
- # instance = contract_class.deploy(gas: 1000000)
23
- # ```
24
- #
25
- # @param _path (String) The artifact file absolute path
26
- #
27
- # @return [Object] the generated contract class.
28
- #
29
9
  def load_contract_at(_path)
30
- artifact = JSON.parse File.read(_path)
31
- LoadContract.for artifact: artifact
10
+ load_contract JSON.parse File.read(_path)
32
11
  end
33
12
 
34
- ##
35
- # This method is similar to `load_contract_at` but takes a hash instead of the raw file.
36
- #
37
- # @param _artifact (Hash) The artifact hash
38
- #
39
- # @return [Object] the generated contract class.
40
- #
41
- def load_contract(_artifact)
42
- LoadContract.for artifact: _artifact
13
+ def load_contract(_json)
14
+ LoadContract.for artifact: _json
43
15
  end
44
16
 
45
- ##
46
- # This method is used internally to load a function definition given a function signature.
47
- #
48
- # The function definition can be later used to call a contract's function without need of
49
- # the artifact file.
50
- #
51
- # @param _signature (String) A string that matches:
52
- #
53
- # (payable|onchain|<type>) <name>(<type>, <type>, ...), where <name> is the function's name
54
- # and <type> is any valid solidity type.
55
- #
56
- # @return [Object] the generated function definition.
57
- #
58
17
  def load_function(_signature)
59
18
  LoadFunction.for signature: _signature
60
19
  end
@@ -3,7 +3,7 @@ module Etherlite::Abi
3
3
  def perform
4
4
  klass = Class.new(Etherlite::Contract::Base)
5
5
 
6
- define_class_methods klass
6
+ define_class_getter klass, 'unlinked_binary', unlinked_binary
7
7
 
8
8
  abi_definitions.each do |definition|
9
9
  case definition['type']
@@ -11,6 +11,8 @@ module Etherlite::Abi
11
11
  define_function klass, definition
12
12
  when 'event'
13
13
  define_event klass, definition
14
+ when 'constructor'
15
+ define_class_getter klass, 'constructor', build_function_from_definition(definition)
14
16
  end
15
17
  end
16
18
 
@@ -21,6 +23,10 @@ module Etherlite::Abi
21
23
 
22
24
  private
23
25
 
26
+ def unlinked_binary
27
+ @artifact['unlinked_binary'] || ''
28
+ end
29
+
24
30
  def abi_definitions
25
31
  @artifact['abi'] || []
26
32
  end
@@ -52,28 +58,30 @@ module Etherlite::Abi
52
58
  end
53
59
  end
54
60
 
55
- event_class.instance_variable_set(:@original_name, _definition['name'])
56
- event_class.instance_variable_set(:@inputs, event_inputs)
61
+ define_class_getter event_class, 'original_name', _definition['name']
62
+ define_class_getter event_class, 'inputs', event_inputs
57
63
 
58
64
  _class.events << event_class
59
65
  _class.const_set(_definition['name'], event_class)
60
66
  end
61
67
 
62
- def define_class_methods(_class)
63
- unlinked_binary = @artifact['unlinked_binary'] || ''
64
-
68
+ def define_class_getter(_class, _name, _value)
65
69
  _class.class_eval do
66
- define_singleton_method('unlinked_binary') { unlinked_binary }
70
+ define_singleton_method(_name) { _value }
67
71
  end
68
72
  end
69
73
 
70
74
  def build_function_from_definition(_definition)
71
75
  Etherlite::Contract::Function.new(
72
76
  _definition['name'],
73
- _definition['inputs'].map { |input| LoadType.for signature: input['type'] },
74
- _definition['outputs'].map { |input| LoadType.for signature: input['type'] },
75
- _definition['payable'],
76
- _definition['constant']
77
+ _definition['inputs'].map do |input|
78
+ Etherlite::Contract::FunctionInput.new(
79
+ input['name'], LoadType.for(signature: input['type'])
80
+ )
81
+ end,
82
+ (_definition['outputs'] || []).map { |input| LoadType.for signature: input['type'] },
83
+ _definition.fetch('payable', false),
84
+ _definition.fetch('constant', false)
77
85
  )
78
86
  end
79
87
  end
@@ -6,7 +6,9 @@ module Etherlite::Abi
6
6
  parts = MATCHER.match @signature
7
7
  raise ArgumentError, 'invalid method signature' if parts.nil?
8
8
 
9
- inputs = parts[3].split(',').map { |a| LoadType.for(signature: a.strip) }
9
+ inputs = parts[3].split(',').map do |a|
10
+ Etherlite::Contract::FunctionInput.new nil, LoadType.for(signature: a.strip)
11
+ end
10
12
 
11
13
  case parts[1]
12
14
  when 'payable'
@@ -2,11 +2,12 @@ module Etherlite
2
2
  class Configuration
3
3
  DEFAULTS = {
4
4
  url: 'http://127.0.0.1:8545',
5
+ enable_nonce_cache: false,
5
6
  chain_id: nil, # any chain
6
7
  logger: nil # set by method
7
8
  }
8
9
 
9
- attr_accessor :url, :chain_id, :logger
10
+ attr_accessor :url, :chain_id, :logger, :enable_nonce_cache
10
11
 
11
12
  def initialize
12
13
  assign_attributes DEFAULTS
@@ -1,21 +1,23 @@
1
1
  module Etherlite::Contract
2
- ##
3
- # The base class for etherlite contract classes
4
- #
5
2
  class Base
6
3
  include Etherlite::Api::Address
7
4
 
8
- # The contract's registered functions
9
5
  def self.functions
10
6
  @functions ||= []
11
7
  end
12
8
 
13
- # The contract's registered events
14
9
  def self.events
15
10
  @events ||= []
16
11
  end
17
12
 
18
- # The contract's compiled bytecode in binary format (stored as hex)
13
+ def self.unlinked_binary
14
+ '0x0'
15
+ end
16
+
17
+ def self.constructor
18
+ nil
19
+ end
20
+
19
21
  def self.binary
20
22
  @binary ||= begin
21
23
  if /__[^_]+_+/ === unlinked_binary
@@ -26,37 +28,19 @@ module Etherlite::Contract
26
28
  end
27
29
  end
28
30
 
29
- ##
30
- # Deploys the contract and waits for the creation transaction to be mined.
31
- #
32
- # This method can be given a source account or client. If an account is given, the it will be
33
- # used to send the creation transaction. If instead a client is given, the client
34
- # `default_account` will be used to send the transaction. If no account nor client is given,
35
- # then the default_account from the default client will be used (if configured).
36
- #
37
- # @param as (Object) The source account
38
- # @param client (Object) The source client (no effect if :as is given)
39
- # @param timeout (Integer) The transaction mining timeout in seconds, defaults to 120 seconds.
40
- #
41
- # This method also takes any parameters the underlying account.send_transaction accepts, like
42
- # :gas, :gas_price, etc.
43
- #
44
- # @return [Object] instance of the contract class pointing the newly deployed contract address.
45
- #
46
- def self.deploy(_options = {})
47
- as = _options[:as] || _options[:client].try(:default_account) || Etherlite.default_account
48
-
49
- tx = as.send_transaction({ data: binary }.merge(_options))
50
- if tx.wait_for_block(timeout: _options.fetch(:timeout, 120))
31
+ def self.deploy(*_args)
32
+ options = _args.last.is_a?(Hash) ? _args.pop : {}
33
+ as = options[:as] || options[:client].try(:default_account) || Etherlite.default_account
34
+
35
+ tx_data = binary
36
+ tx_data += constructor.encode(_args) unless constructor.nil?
37
+
38
+ tx = as.send_transaction({ data: tx_data }.merge(options))
39
+ if tx.wait_for_block(timeout: options.fetch(:timeout, 120))
51
40
  at tx.contract_address, as: as
52
41
  end
53
42
  end
54
43
 
55
- ##
56
- # Creates a new instance of the contract class that points to a given address.
57
- #
58
- #
59
- #
60
44
  def self.at(_address, client: nil, as: nil)
61
45
  _address = Etherlite::Utils.normalize_address_param _address
62
46
 
@@ -76,11 +60,6 @@ module Etherlite::Contract
76
60
  @default_account = _default_account
77
61
  end
78
62
 
79
- ##
80
- # Searches for event logs emitted by this contract
81
- #
82
- #
83
- #
84
63
  def get_logs(events: nil, from_block: :earliest, to_block: :latest)
85
64
  params = {
86
65
  address: json_encoded_address,
@@ -3,17 +3,17 @@ require 'etherlite/commands/contract/event_base/decode_log_inputs'
3
3
  module Etherlite::Contract
4
4
  class EventBase
5
5
  def self.inputs
6
- @inputs
6
+ nil # To be implemented by sub classes
7
7
  end
8
8
 
9
9
  def self.original_name
10
- @original_name
10
+ nil # To be implemented by sub classes
11
11
  end
12
12
 
13
13
  def self.signature
14
14
  @signature ||= begin
15
- input_sig = @inputs.map { |i| i.type.signature }
16
- "#{@original_name}(#{input_sig.join(',')})"
15
+ input_sig = inputs.map { |i| i.type.signature }
16
+ "#{original_name}(#{input_sig.join(',')})"
17
17
  end
18
18
  end
19
19
 
@@ -31,10 +31,14 @@ module Etherlite::Contract
31
31
  raise ArgumentError, "Expected #{@inputs.count} arguments, got #{_values.length} "
32
32
  end
33
33
 
34
- signature_hash = Etherlite::Utils.sha3 signature
35
- encoded_inputs = Etherlite::Support::Array.encode(@inputs, _values)
34
+ encoded_inputs = Etherlite::Support::Array.encode(@inputs.map(&:type), _values)
36
35
 
37
- '0x' + signature_hash[0..7] + encoded_inputs
36
+ if @name
37
+ signature_hash = Etherlite::Utils.sha3 signature
38
+ '0x' + signature_hash[0..7] + encoded_inputs
39
+ else
40
+ encoded_inputs # if no name is provided, just render arguments
41
+ end
38
42
  end
39
43
 
40
44
  def decode(_connection, _data)
@@ -0,0 +1,18 @@
1
+ module Etherlite::Contract
2
+ class FunctionInput
3
+ attr_reader :original_name, :type
4
+
5
+ def initialize(_original_name, _type)
6
+ @original_name = _original_name
7
+ @type = _type
8
+ end
9
+
10
+ def signature
11
+ @type.signature
12
+ end
13
+
14
+ def name
15
+ @name ||= @original_name.underscore
16
+ end
17
+ end
18
+ end
@@ -23,11 +23,11 @@ module Etherlite
23
23
 
24
24
  begin
25
25
  result = yield next_nonce
26
- @@nonce_cache[_normalized_address] = next_nonce
26
+ @@nonce_cache[_normalized_address] = next_nonce if caching_enabled?
27
27
  return result
28
28
  rescue
29
29
  # if yield fails, cant be sure about transaction status so must rely again on observing.
30
- @@nonce_cache.delete _normalized_address
30
+ @@nonce_cache.delete _normalized_address if caching_enabled?
31
31
  raise
32
32
  end
33
33
  end
@@ -36,9 +36,12 @@ module Etherlite
36
36
  private
37
37
 
38
38
  def last_observed_nonce_for(_normalized_address)
39
- # TODO: support using tx_pool API to improve this:
40
- # http://qnimate.com/calculating-nonce-for-raw-transactions-in-geth/
39
+ # https://github.com/ethereum/go-ethereum/issues/2736
41
40
  @connection.eth_get_transaction_count('0x' + _normalized_address, 'pending') - 1
42
41
  end
42
+
43
+ def caching_enabled?
44
+ Etherlite.config.enable_nonce_cache
45
+ end
43
46
  end
44
47
  end
@@ -1,3 +1,3 @@
1
1
  module Etherlite
2
- VERSION = "0.1.6"
2
+ VERSION = "0.1.7"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: etherlite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ignacio Baixas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-08-07 00:00:00.000000000 Z
11
+ date: 2018-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: digest-sha3
@@ -205,6 +205,7 @@ files:
205
205
  - lib/etherlite/contract/event_base.rb
206
206
  - lib/etherlite/contract/event_input.rb
207
207
  - lib/etherlite/contract/function.rb
208
+ - lib/etherlite/contract/function_input.rb
208
209
  - lib/etherlite/errors.rb
209
210
  - lib/etherlite/nonce_manager.rb
210
211
  - lib/etherlite/railtie.rb