etherlite 0.1.6 → 0.1.7
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/README.md +7 -92
- data/lib/etherlite.rb +2 -0
- data/lib/etherlite/abi.rb +3 -44
- data/lib/etherlite/commands/abi/load_contract.rb +19 -11
- data/lib/etherlite/commands/abi/load_function.rb +3 -1
- data/lib/etherlite/configuration.rb +2 -1
- data/lib/etherlite/contract/base.rb +17 -38
- data/lib/etherlite/contract/event_base.rb +4 -4
- data/lib/etherlite/contract/function.rb +7 -3
- data/lib/etherlite/contract/function_input.rb +18 -0
- data/lib/etherlite/nonce_manager.rb +7 -4
- data/lib/etherlite/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb97370a946be81259847d2b1f3c7a6da33593e2
|
4
|
+
data.tar.gz: 373696bf36bcf5b5b7889ffe7eadfd0bf3544881
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f45918243e835c01d28c6beb7f3c42ffb77fa35047aab3051aba1f555eb4a65c6e6d7dc0b218a4efbdcb1efef3b3fd3b0b2a1e79f4bd22f202e43085169fc5f7
|
7
|
+
data.tar.gz: 22a6b1e87b0b9778de84607fd2d7d6cc59e35b14c14928ef984d4bfa32901f82d549f7c90ee018071847c69c1ba887e6ec4216989456b5d98efd8c278679d362
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Etherlite
|
2
2
|
|
3
|
-
|
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
|
-
|
18
|
-
|
19
|
-
$ rails g etherlite:init
|
19
|
+
Or install it yourself as:
|
20
20
|
|
21
|
-
|
21
|
+
$ gem install etherlite
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
-
|
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
|
-
|
31
|
-
LoadContract.for artifact: artifact
|
10
|
+
load_contract JSON.parse File.read(_path)
|
32
11
|
end
|
33
12
|
|
34
|
-
|
35
|
-
|
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
|
-
|
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
|
56
|
-
event_class
|
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
|
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(
|
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
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
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
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
6
|
+
nil # To be implemented by sub classes
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.original_name
|
10
|
-
|
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 =
|
16
|
-
"#{
|
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
|
-
|
35
|
-
encoded_inputs = Etherlite::Support::Array.encode(@inputs, _values)
|
34
|
+
encoded_inputs = Etherlite::Support::Array.encode(@inputs.map(&:type), _values)
|
36
35
|
|
37
|
-
|
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
|
-
#
|
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
|
data/lib/etherlite/version.rb
CHANGED
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.
|
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:
|
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
|