ethereum.rb 1.6.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +9 -3
- data/README.md +35 -36
- data/bin/install_parity +15 -23
- data/lib/ethereum.rb +3 -0
- data/lib/ethereum/abi.rb +18 -0
- data/lib/ethereum/contract.rb +177 -232
- data/lib/ethereum/contract_event.rb +1 -2
- data/lib/ethereum/contract_initializer.rb +1 -1
- data/lib/ethereum/decoder.rb +58 -0
- data/lib/ethereum/deployment.rb +2 -2
- data/lib/ethereum/encoder.rb +71 -0
- data/lib/ethereum/formatter.rb +9 -1
- data/lib/ethereum/function.rb +18 -2
- data/lib/ethereum/http_client.rb +1 -1
- data/lib/ethereum/initializer.rb +2 -2
- data/lib/ethereum/ipc_client.rb +0 -1
- data/lib/ethereum/singleton.rb +1 -1
- data/lib/ethereum/solidity.rb +1 -1
- data/lib/ethereum/version.rb +1 -1
- data/lib/tasks/ethereum_contract.rake +5 -5
- data/lib/tasks/ethereum_node.rake +5 -5
- data/lib/tasks/ethereum_transaction.rake +2 -2
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4bfce795e5c0ba5c86e8731f2f7c04504b53000
|
4
|
+
data.tar.gz: 52feb089966aabb77b7a1002d5a8aff8433049dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40ff1fe3ac72b9f4676eb5350c1ada0e31ebcf9778eeab3b5d80036f5d4a8c0cc854d3988f4a11d044788ffadee6482ca5d0bf2e7c31e600521de045f9d93902
|
7
|
+
data.tar.gz: 69eb7f4a2a31e04a883d0b266211fd13e411790afc9105e884494464e508db60fd4363e9f970aef9afb26b153501e1f35c89e4bf8dc456951c5f2da569d31b6b
|
data/.travis.yml
CHANGED
@@ -2,6 +2,12 @@ dist: trusty
|
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
4
|
- 2.3.1
|
5
|
+
env:
|
6
|
+
- PARITY="1.4.8"
|
7
|
+
- PARITY="1.5.0"
|
8
|
+
matrix:
|
9
|
+
allow_failures:
|
10
|
+
- env: PARITY="1.5.0"
|
5
11
|
cache:
|
6
12
|
bundler: true
|
7
13
|
directories:
|
@@ -10,15 +16,15 @@ before_install:
|
|
10
16
|
- sudo bin/install_parity
|
11
17
|
- gem install bundler -v 1.11.2
|
12
18
|
before_script:
|
13
|
-
- parity --chain ~/.parity/ropsten.json --warp --password ~/.parity/pass --unlock
|
19
|
+
- parity --chain ~/.parity/ropsten.json --warp --password ~/.parity/pass --unlock 3089630d06fD90Ef48a0c43f000971587c1F3247 --author 3089630d06fD90Ef48a0c43f000971587c1F3247 daemon ~/.parity.pid --log-file ~/.parity.log
|
20
|
+
- cat ~/.parity.log
|
14
21
|
- sleep 5
|
15
22
|
- parity --chain ~/.parity/ropsten.json account list
|
16
23
|
- cat ~/.parity.log
|
17
24
|
- bundle exec rake ethereum:node:waitforsync
|
18
25
|
- bundle exec rake ethereum:test:setup
|
19
26
|
script:
|
20
|
-
- bundle exec rspec --tag ~
|
21
|
-
- bundle exec rspec --tag slow
|
27
|
+
- bundle exec rspec --tag ~blockchain && bundle exec rspec --tag blockchain
|
22
28
|
before_cache:
|
23
29
|
- killall parity
|
24
30
|
- sleep 3
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
# Ethereum.rb
|
1
|
+
# Ethereum Ruby library - Ethereum.rb
|
2
2
|
|
3
|
-
[![Build Status](https://travis-ci.org/marekkirejczyk/ethereum.rb.svg?branch=master)](https://travis-ci.org/marekkirejczyk/ethereum.rb) [![security](https://hakiri.io/github/NullVoxPopuli/MetaHash/master.svg)](https://hakiri.io/github/NullVoxPopuli/MetaHash/master) [![Dependency Status](https://gemnasium.com/marekkirejczyk/ethereum.rb.svg)](https://gemnasium.com/marekkirejczyk/ethereum.rb)
|
3
|
+
[![Build Status](https://travis-ci.org/marekkirejczyk/ethereum.rb.svg?branch=master)](https://travis-ci.org/marekkirejczyk/ethereum.rb) [![security](https://hakiri.io/github/NullVoxPopuli/MetaHash/master.svg)](https://hakiri.io/github/NullVoxPopuli/MetaHash/master) [![Dependency Status](https://gemnasium.com/marekkirejczyk/ethereum.rb.svg)](https://gemnasium.com/marekkirejczyk/ethereum.rb) [![Code Climate](https://codeclimate.com/github/marekkirejczyk/ethereum.rb/badges/gpa.svg)](https://codeclimate.com/github/marekkirejczyk/ethereum.rb)
|
4
4
|
|
5
|
-
A
|
5
|
+
A ruby gem for interacting with Ethereum.
|
6
6
|
|
7
7
|
## Highlights
|
8
8
|
|
@@ -36,7 +36,7 @@ To be able to compile [solidity](https://github.com/ethereum/solidity) contracts
|
|
36
36
|
Add this line to your application's Gemfile:
|
37
37
|
|
38
38
|
```ruby
|
39
|
-
gem 'ethereum'
|
39
|
+
gem 'ethereum.rb'
|
40
40
|
```
|
41
41
|
|
42
42
|
And then execute:
|
@@ -45,7 +45,7 @@ And then execute:
|
|
45
45
|
|
46
46
|
Or install it yourself as:
|
47
47
|
|
48
|
-
$ gem install ethereum
|
48
|
+
$ gem install ethereum.eb
|
49
49
|
|
50
50
|
## Basic Usage
|
51
51
|
|
@@ -72,44 +72,47 @@ By default logging is on and logs are saved in "/tmp/ethereum_ruby_http.log".
|
|
72
72
|
You can create contract from solidity source and deploy it to the blockchain.
|
73
73
|
|
74
74
|
```ruby
|
75
|
-
contract = Ethereum::Contract.
|
76
|
-
|
75
|
+
contract = Ethereum::Contract.create(file: "mycontract.sol", client: client)
|
76
|
+
address = contract.deploy_and_wait
|
77
77
|
```
|
78
78
|
|
79
79
|
Deployment may take up to couple minutes.
|
80
|
-
|
80
|
+
If you want to complie multiple contracts at once, you can create new instances using newly declared clasess:
|
81
81
|
|
82
82
|
```ruby
|
83
|
-
|
84
|
-
init.build_all
|
83
|
+
Ethereum::Contract.create(file: "mycontract.sol", client: client)
|
85
84
|
contract = MyContract.new
|
86
|
-
|
85
|
+
contract = contract.deploy_and_wait
|
87
86
|
```
|
88
87
|
|
89
|
-
Note that contract variable holds the reference to contract code, while contract_instance holds refernce to contract deployed to the blockchain. You can call contract functions on contract_instance as described in sections "Transacting and Calling Solidity Functions".
|
90
|
-
|
91
88
|
All names used to name contract in solidity source will transalte to name of classes in ruby (camelized).
|
92
89
|
If class of given name exist it will be undefined first to avoid name collision.
|
93
90
|
|
94
91
|
### Get contract from blockchain
|
95
92
|
|
96
93
|
The other way to obtain contract instance is get one form the blockchain. To do so you need a contract name, contract address and ABI definition.
|
94
|
+
`client` parameter is optional.
|
97
95
|
|
98
96
|
```ruby
|
99
|
-
|
100
|
-
|
97
|
+
contract = Ethereum::Contract.create(name: "MyContract", address: "0x01a4d1A62F01ED966646acBfA8BB0b59960D06dd ", abi: abi, client: client)
|
101
98
|
```
|
102
99
|
|
103
100
|
Note that you need to specify contract name, that will be used to define new class in ruby, as it is not a part of ABI definition.
|
104
101
|
|
102
|
+
If you want to create new contract, that is not yet deployed from ABI definition you need to supply binary code too:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
contract = Ethereum::Contract.create(name: "MyContract", address: "0x01a4d1A62F01ED966646acBfA8BB0b59960D06dd ", abi: abi, code: "...")
|
106
|
+
```
|
107
|
+
|
105
108
|
### Transacting and Calling Solidity Functions
|
106
109
|
|
107
|
-
|
110
|
+
Functions defined in a contract are exposed using the following conventions:
|
108
111
|
|
109
112
|
```
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
+
contract.transact.[function_name](params)
|
114
|
+
contract.transact_and_wait.[function_name](params)
|
115
|
+
contract.call.[function_name](params)
|
113
116
|
```
|
114
117
|
|
115
118
|
**Example Contract in Solidity**
|
@@ -127,27 +130,23 @@ contract SimpleNameRegistry {
|
|
127
130
|
}
|
128
131
|
```
|
129
132
|
|
130
|
-
|
131
|
-
simple_name_registry_instance.transact_and_wait_register("0x5b6cb65d40b0e27fab87a2180abcab22174a2d45", "minter.contract.dgx")
|
132
|
-
simple_name_registry_instance.transact_register("0x385acafdb80b71ae001f1dbd0d65e62ec2fff055", "anthony@eufemio.dgx")
|
133
|
-
simple_name_registry_instance.call_get_some_value("0x385acafdb80b71ae001f1dbd0d65e62ec2fff055")
|
134
|
-
simple_name_registry_instance.call_my_mapping("0x385acafdb80b71ae001f1dbd0d65e62ec2fff055")
|
135
|
-
```
|
136
|
-
|
137
|
-
### Run contracts using a different address
|
133
|
+
And here is how to access it's methods:
|
138
134
|
|
139
135
|
```ruby
|
140
|
-
|
136
|
+
contract.transact_and_wait.register("0x5b6cb65d40b0e27fab87a2180abcab22174a2d45", "minter.contract.dgx")
|
137
|
+
contract.transact.register("0x385acafdb80b71ae001f1dbd0d65e62ec2fff055", "anthony@eufemio.dgx")
|
138
|
+
contract.call.get_some_value("0x385acafdb80b71ae001f1dbd0d65e62ec2fff055")
|
141
139
|
```
|
142
140
|
|
143
|
-
|
141
|
+
Note that as `register` method will change contract state, so you need to use `transact` family of methods (or `transact_and_wait` for synchronous version) to call it.
|
142
|
+
|
143
|
+
For method like `getSomeValue`, that does not affect state of the contract (and don't need to propagate change to blockchain therefore) you can use call method. It will retrive value from local copy of the blockchain.
|
144
144
|
|
145
|
-
```ruby
|
146
|
-
simple_name_registry_instance.at("0x734533083b5fc0cd14b7cb8c8eb6ed0c9bd184d3")
|
147
|
-
```
|
148
145
|
|
149
146
|
## Utils rake tasks
|
150
147
|
|
148
|
+
There is plenty of useful rake tasks for interacting with blockchain:
|
149
|
+
|
151
150
|
```ruby
|
152
151
|
rake ethereum:contract:compile[path] # Compile a contract / Compile and deploy contract
|
153
152
|
rake ethereum:node:mine # Mine ethereum testing environment for ethereum node
|
@@ -160,7 +159,7 @@ rake ethereum:transaction:send[address,amount] # Send [amount of] ether to an a
|
|
160
159
|
```
|
161
160
|
|
162
161
|
## Debbuging
|
163
|
-
Logs from communication
|
162
|
+
Logs from communication between ruby app and node are available under following path:
|
164
163
|
```
|
165
164
|
/tmp/ethereum_ruby_http.log
|
166
165
|
```
|
@@ -171,13 +170,13 @@ After checking out the repo, run `bin/setup` to install dependencies.
|
|
171
170
|
Make sure `rake ethereum:test:setup` passes before running tests.
|
172
171
|
Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
173
172
|
|
173
|
+
You need ethereum node up and running for tests to pass and it needs to be working on testnet (Ropsten).
|
174
|
+
|
174
175
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
175
176
|
|
176
|
-
##
|
177
|
+
## Acknowledgements and license
|
177
178
|
|
178
179
|
This library has been forked from [ethereum-ruby](https://github.com/DigixGlobal/ethereum-ruby) by DigixGlobal Pte Ltd (https://dgx.io).
|
179
180
|
|
180
|
-
## License
|
181
|
-
|
182
181
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
183
182
|
|
data/bin/install_parity
CHANGED
@@ -1,29 +1,21 @@
|
|
1
1
|
#!/bin/bash
|
2
2
|
|
3
|
-
install_parity () {
|
4
|
-
sudo add-apt-repository ppa:ethereum/ethereum -y
|
5
|
-
sudo apt-get update
|
6
|
-
sudo apt-get install solc
|
7
|
-
wget http://d1h4xl4cr1h0mo.cloudfront.net/v1.4.8/x86_64-unknown-linux-gnu/parity_1.4.8_amd64.deb
|
8
|
-
sudo dpkg -i parity_1.4.8_amd64.deb
|
9
|
-
}
|
10
|
-
|
11
|
-
|
12
|
-
|
13
3
|
echo "Installing parity..."
|
4
|
+
sudo add-apt-repository ppa:ethereum/ethereum -y
|
5
|
+
sudo apt-get update
|
6
|
+
sudo apt-get install solc
|
7
|
+
sudo apt-get -y install libssl-dev
|
8
|
+
echo "Solc version"
|
9
|
+
solc --version
|
10
|
+
wget http://d1h4xl4cr1h0mo.cloudfront.net/v$PARITY\/x86_64-unknown-linux-gnu/parity_$PARITY\_amd64.deb
|
11
|
+
sudo dpkg -i parity_$PARITY\_amd64.deb
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
wget https://raw.githubusercontent.com/ethcore/parity/master/ethcore/res/ethereum/ropsten.json --output-document ~/.parity/ropsten.json
|
23
|
-
echo $pass > ~/.parity/pass
|
24
|
-
mkdir -p ~/.parity/keys
|
25
|
-
echo $wallet > ~/.parity/keys/10890ad6-5171-4945-be95-984d83394120
|
26
|
-
chown -R travis:travis ~/.parity
|
27
|
-
fi
|
13
|
+
echo "Setuping parity..."
|
14
|
+
mkdir -p ~/.parity
|
15
|
+
wget https://raw.githubusercontent.com/ethcore/parity/master/ethcore/res/ethereum/ropsten.json --output-document ~/.parity/ropsten.json
|
16
|
+
echo $pass > ~/.parity/pass
|
17
|
+
mkdir -p ~/.parity/keys
|
18
|
+
echo $wallet > ~/.parity/keys/UTC--2017-01-08T21-02-29.039669388Z--3089630d06fd90ef48a0c43f000971587c1f3247
|
19
|
+
chown -R travis:travis ~/.parity
|
28
20
|
|
29
21
|
ls -la ~/.parity
|
data/lib/ethereum.rb
CHANGED
@@ -4,6 +4,7 @@ require 'active_support/core_ext'
|
|
4
4
|
require 'digest/sha3'
|
5
5
|
|
6
6
|
module Ethereum
|
7
|
+
require 'ethereum/abi'
|
7
8
|
require 'ethereum/client'
|
8
9
|
require 'ethereum/ipc_client'
|
9
10
|
require 'ethereum/http_client'
|
@@ -15,6 +16,8 @@ module Ethereum
|
|
15
16
|
require 'ethereum/function_input'
|
16
17
|
require 'ethereum/function_output'
|
17
18
|
require 'ethereum/contract_event'
|
19
|
+
require 'ethereum/encoder'
|
20
|
+
require 'ethereum/decoder'
|
18
21
|
require 'ethereum/formatter'
|
19
22
|
require 'ethereum/transaction'
|
20
23
|
require 'ethereum/deployment'
|
data/lib/ethereum/abi.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Ethereum
|
2
|
+
class Abi
|
3
|
+
|
4
|
+
def self.parse_abi(abi)
|
5
|
+
constructor_inputs = abi.detect {|x| x["type"] == "constructor"}["inputs"] rescue nil
|
6
|
+
functions = abi.select {|x| x["type"] == "function" }.map { |fun| Ethereum::Function.new(fun) }
|
7
|
+
events = abi.select {|x| x["type"] == "event" }.map { |evt| Ethereum::ContractEvent.new(evt) }
|
8
|
+
[constructor_inputs, functions, events]
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.parse_type(type)
|
12
|
+
raise NotImplementedError if type.ends_with?("]")
|
13
|
+
match = /(\D+)(\d.+)?/.match(type)
|
14
|
+
[match[1], match[2]]
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
data/lib/ethereum/contract.rb
CHANGED
@@ -1,266 +1,211 @@
|
|
1
1
|
module Ethereum
|
2
2
|
class Contract
|
3
3
|
|
4
|
-
|
4
|
+
attr_reader :address
|
5
|
+
attr_accessor :code, :name, :abi, :class_object, :sender, :deployment, :client
|
6
|
+
attr_accessor :events, :functions, :constructor_inputs
|
7
|
+
attr_accessor :call_raw_proxy, :call_proxy, :transact_proxy, :transact_and_wait_proxy
|
8
|
+
attr_accessor :new_filter_proxy, :get_filter_logs_proxy, :get_filter_change_proxy
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
attr_accessor :code, :name, :functions, :abi, :constructor_inputs, :events, :class_object
|
9
|
-
|
10
|
-
def initialize(name, code, abi)
|
10
|
+
def initialize(name, code, abi, client = Ethereum::Singleton.instance)
|
11
11
|
@name = name
|
12
12
|
@code = code
|
13
13
|
@abi = abi
|
14
|
-
@functions =
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
18
|
-
|
19
|
-
|
20
|
-
@abi.select {|x| x["type"] == "event" }.each do |abievt|
|
21
|
-
@events << Ethereum::ContractEvent.new(abievt)
|
22
|
-
end
|
14
|
+
@constructor_inputs, @functions, @events = Ethereum::Abi.parse_abi(abi)
|
15
|
+
@formatter = Ethereum::Formatter.new
|
16
|
+
@client = client
|
17
|
+
@sender = client.default_account
|
18
|
+
@encoder = Encoder.new
|
19
|
+
@decoder = Decoder.new
|
23
20
|
end
|
24
21
|
|
25
|
-
def self.
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
22
|
+
def self.create(file: nil, client: Ethereum::Singleton.instance, code: nil, abi: nil, address: nil, name: nil)
|
23
|
+
contract = nil
|
24
|
+
if file.present?
|
25
|
+
contracts = Ethereum::Initializer.new(file, client).build_all
|
26
|
+
raise "No contracts complied" if contracts.empty?
|
27
|
+
contract = contracts.first.class_object.new
|
28
|
+
else
|
29
|
+
abi = JSON.parse(abi) if abi.is_a? String
|
30
|
+
contract = Ethereum::Contract.new(name, code, abi, client)
|
31
|
+
contract.build
|
32
|
+
contract = contract.class_object.new
|
33
|
+
end
|
34
|
+
contract.address = address
|
35
|
+
contract
|
38
36
|
end
|
39
37
|
|
40
|
-
def build(connection)
|
41
|
-
class_name = @name.camelize
|
42
|
-
functions = @functions
|
43
|
-
constructor_inputs = @constructor_inputs
|
44
|
-
binary = @code
|
45
|
-
events = @events
|
46
|
-
abi = @abi
|
47
|
-
|
48
|
-
class_methods = Class.new do
|
49
|
-
|
50
|
-
define_method "connection".to_sym do
|
51
|
-
connection
|
52
|
-
end
|
53
|
-
|
54
|
-
define_method :deploy do |*params|
|
55
|
-
formatter = Ethereum::Formatter.new
|
56
|
-
deploy_code = binary
|
57
|
-
deploy_arguments = ""
|
58
|
-
if constructor_inputs.present?
|
59
|
-
raise "Missing constructor parameter" and return if params.length != constructor_inputs.length
|
60
|
-
constructor_inputs.each_index do |i|
|
61
|
-
args = [constructor_inputs[i]["type"], params[i]]
|
62
|
-
deploy_arguments << formatter.to_payload(args)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
deploy_payload = deploy_code + deploy_arguments
|
66
|
-
deploytx = connection.eth_send_transaction({from: self.sender, data: "0x" + deploy_payload})["result"]
|
67
|
-
raise "Failed to deploy, did you unlock #{self.sender} account? Transaction hash: #{deploytx}" if deploytx.nil? || deploytx == "0x0000000000000000000000000000000000000000000000000000000000000000"
|
68
|
-
instance_variable_set("@deployment", Ethereum::Deployment.new(deploytx, connection))
|
69
|
-
end
|
70
|
-
|
71
|
-
define_method :estimate do |*params|
|
72
|
-
formatter = Ethereum::Formatter.new
|
73
|
-
deploy_code = binary
|
74
|
-
deploy_arguments = ""
|
75
|
-
if constructor_inputs.present?
|
76
|
-
raise "Missing constructor parameter" and return if params.length != constructor_inputs.length
|
77
|
-
constructor_inputs.each_index do |i|
|
78
|
-
args = [constructor_inputs[i]["type"], params[i]]
|
79
|
-
deploy_arguments << formatter.to_payload(args)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
deploy_payload = deploy_code + deploy_arguments
|
83
|
-
deploytx = connection.eth_estimate_gas({from: self.sender, data: "0x" + deploy_payload})["result"]
|
84
|
-
end
|
85
|
-
|
86
|
-
define_method :events do
|
87
|
-
return events
|
88
|
-
end
|
89
|
-
|
90
|
-
define_method :abi do
|
91
|
-
return abi
|
92
|
-
end
|
93
|
-
|
94
|
-
define_method :deployment do
|
95
|
-
instance_variable_get("@deployment")
|
96
|
-
end
|
97
|
-
|
98
|
-
define_method :deploy_and_wait do |time = 200.seconds, *params, **args, &block|
|
99
|
-
self.deploy(*params)
|
100
|
-
self.deployment.wait_for_deployment(time, **args, &block)
|
101
|
-
instance_variable_set("@address", self.deployment.contract_address)
|
102
|
-
self.events.each do |event|
|
103
|
-
event.set_address(self.deployment.contract_address)
|
104
|
-
event.set_client(connection)
|
105
|
-
end
|
106
|
-
self.deployment.contract_address
|
107
|
-
end
|
108
|
-
|
109
|
-
define_method :at do |addr|
|
110
|
-
instance_variable_set("@address", addr)
|
111
|
-
self.events.each do |event|
|
112
|
-
event.set_address(addr)
|
113
|
-
event.set_client(connection)
|
114
|
-
end
|
115
|
-
end
|
116
38
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
define_method :sender do
|
126
|
-
instance_variable_get("@sender") || connection.default_account
|
127
|
-
end
|
128
|
-
|
129
|
-
define_method :set_gas_price do |gp|
|
130
|
-
instance_variable_set("@gas_price", gp)
|
131
|
-
end
|
39
|
+
def address=(addr)
|
40
|
+
@address = addr
|
41
|
+
@events.each do |event|
|
42
|
+
event.set_address(addr)
|
43
|
+
event.set_client(@client)
|
44
|
+
end
|
45
|
+
end
|
132
46
|
|
133
|
-
|
134
|
-
|
135
|
-
|
47
|
+
def deploy(*params)
|
48
|
+
if @constructor_inputs.present?
|
49
|
+
raise "Missing constructor parameter" and return if params.length != @constructor_inputs.length
|
50
|
+
end
|
51
|
+
deploy_arguments = @formatter.construtor_params_to_payload(@constructor_inputs, params)
|
52
|
+
payload = "0x" + @code + deploy_arguments
|
53
|
+
tx = @client.eth_send_transaction({from: sender, data: payload})["result"]
|
54
|
+
raise "Failed to deploy, did you unlock #{sender} account? Transaction hash: #{deploytx}" if tx.nil? || tx == "0x0000000000000000000000000000000000000000000000000000000000000000"
|
55
|
+
@deployment = Ethereum::Deployment.new(tx, @client)
|
56
|
+
end
|
136
57
|
|
137
|
-
|
138
|
-
|
139
|
-
|
58
|
+
def deploy_and_wait(*params, **args, &block)
|
59
|
+
deploy(*params)
|
60
|
+
@deployment.wait_for_deployment(**args, &block)
|
61
|
+
self.events.each do |event|
|
62
|
+
event.set_address(@address)
|
63
|
+
event.set_client(@client)
|
64
|
+
end
|
65
|
+
@address = @deployment.contract_address
|
66
|
+
end
|
140
67
|
|
141
|
-
|
142
|
-
|
68
|
+
def estimate(*params)
|
69
|
+
deploy_arguments = ""
|
70
|
+
if @constructor_inputs.present?
|
71
|
+
raise "Wrong number of arguments in a constructor" and return if params.length != @constructor_inputs.length
|
72
|
+
@constructor_inputs.each_index do |i|
|
73
|
+
args = [@constructor_inputs[i]["type"], params[i]]
|
74
|
+
deploy_arguments << @formatter.to_payload(args)
|
143
75
|
end
|
76
|
+
end
|
77
|
+
deploy_payload = @code + deploy_arguments
|
78
|
+
result = @client.eth_estimate_gas({from: @sender, data: "0x" + deploy_payload})
|
79
|
+
@decoder.decode_int(result["result"])
|
80
|
+
end
|
144
81
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
formatter = Ethereum::Formatter.new
|
158
|
-
logs = connection.get_filter_logs(filter_id)
|
159
|
-
collection = []
|
160
|
-
logs["result"].each do |result|
|
161
|
-
inputs = evt.input_types
|
162
|
-
outputs = inputs.zip(result["topics"][1..-1])
|
163
|
-
data = {blockNumber: result["blockNumber"].hex, transactionHash: result["transactionHash"], blockHash: result["blockHash"], transactionIndex: result["transactionIndex"].hex, topics: []}
|
164
|
-
outputs.each do |output|
|
165
|
-
data[:topics] << formatter.from_payload(output)
|
166
|
-
end
|
167
|
-
collection << data
|
168
|
-
end
|
169
|
-
return collection
|
170
|
-
end
|
171
|
-
|
172
|
-
define_method "gfc_#{evt.name.underscore}".to_sym do |filter_id|
|
173
|
-
formatter = Ethereum::Formatter.new
|
174
|
-
logs = connection.get_filter_changes(filter_id)
|
175
|
-
collection = []
|
176
|
-
logs["result"].each do |result|
|
177
|
-
inputs = evt.input_types
|
178
|
-
outputs = inputs.zip(result["topics"][1..-1])
|
179
|
-
data = {blockNumber: result["blockNumber"].hex, transactionHash: result["transactionHash"], blockHash: result["blockHash"], transactionIndex: result["transactionIndex"].hex, topics: []}
|
180
|
-
outputs.each do |output|
|
181
|
-
data[:topics] << formatter.from_payload(output)
|
182
|
-
end
|
183
|
-
collection << data
|
184
|
-
end
|
185
|
-
return collection
|
186
|
-
end
|
82
|
+
def call_raw(fun, *args)
|
83
|
+
arg_types = fun.inputs.collect(&:type)
|
84
|
+
return {result: :error, message: "missing parameters for #{fun.function_string}" } if arg_types.length != args.length
|
85
|
+
payload = [fun.signature]
|
86
|
+
arg_types.zip(args).each do |arg|
|
87
|
+
payload << @formatter.to_payload(arg)
|
88
|
+
end
|
89
|
+
raw_result = @client.eth_call({to: @address, from: @sender, data: "0x" + payload.join()})
|
90
|
+
raw_result = raw_result["result"]
|
91
|
+
output = @decoder.decode_arguments(fun.outputs, raw_result)
|
92
|
+
return {data: "0x" + payload.join(), raw: raw_result, formatted: output}
|
93
|
+
end
|
187
94
|
|
188
|
-
|
95
|
+
def call(fun, *args)
|
96
|
+
output = call_raw(fun, *args)[:formatted]
|
97
|
+
if output.length == 1
|
98
|
+
return output[0]
|
99
|
+
else
|
100
|
+
return output
|
101
|
+
end
|
102
|
+
end
|
189
103
|
|
190
|
-
|
104
|
+
def transact(fun, *args)
|
105
|
+
arg_types = fun.inputs.collect(&:type)
|
106
|
+
return {result: :error, message: "missing parameters for #{fun.function_string}" } if arg_types.length != args.length
|
107
|
+
payload = []
|
108
|
+
payload << fun.signature
|
109
|
+
arg_types.zip(args).each do |arg|
|
110
|
+
payload << @formatter.to_payload(arg)
|
111
|
+
end
|
112
|
+
txid = @client.eth_send_transaction({to: @address, from: @sender, data: "0x" + payload.join()})["result"]
|
113
|
+
return Ethereum::Transaction.new(txid, @client, payload.join(), args)
|
114
|
+
end
|
191
115
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
call_raw_function_name_alias = "cr_#{derived_function_name}".to_sym
|
198
|
-
transact_function_name = "transact_#{derived_function_name}".to_sym
|
199
|
-
transact_function_name_alias = "t_#{derived_function_name}".to_sym
|
200
|
-
transact_and_wait_function_name = "transact_and_wait_#{derived_function_name}".to_sym
|
201
|
-
transact_and_wait_function_name_alias = "tw_#{derived_function_name}".to_sym
|
116
|
+
def transact_and_wait(fun, *args)
|
117
|
+
tx = transact(fun, *args)
|
118
|
+
tx.wait_for_miner
|
119
|
+
return tx
|
120
|
+
end
|
202
121
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
end
|
213
|
-
raw_result = connection.eth_call({to: self.address, from: self.sender, data: "0x" + payload.join()})
|
214
|
-
raw_result = raw_result["result"]
|
215
|
-
formatted_result = fun.outputs.collect {|x| x.type }.zip(raw_result.gsub(/^0x/,'').scan(/.{64}/))
|
216
|
-
output = formatted_result.collect {|x| formatter.from_payload(x) }
|
217
|
-
return {data: "0x" + payload.join(), raw: raw_result, formatted: output}
|
218
|
-
end
|
122
|
+
def create_filter(evt, **params)
|
123
|
+
params[:to_block] ||= "latest"
|
124
|
+
params[:from_block] ||= "0x0"
|
125
|
+
params[:address] ||= @address
|
126
|
+
params[:topics] = @encoder.ensure_prefix(evt.signature)
|
127
|
+
payload = {topics: [params[:topics]], fromBlock: params[:from_block], toBlock: params[:to_block], address: @encoder.ensure_prefix(params[:address])}
|
128
|
+
filter_id = @client.eth_new_filter(payload)
|
129
|
+
return @decoder.decode_int(filter_id["result"])
|
130
|
+
end
|
219
131
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
132
|
+
def parse_filter_data(evt, logs)
|
133
|
+
formatter = Ethereum::Formatter.new
|
134
|
+
collection = []
|
135
|
+
logs["result"].each do |result|
|
136
|
+
inputs = evt.input_types
|
137
|
+
outputs = inputs.zip(result["topics"][1..-1])
|
138
|
+
data = {blockNumber: result["blockNumber"].hex, transactionHash: result["transactionHash"], blockHash: result["blockHash"], transactionIndex: result["transactionIndex"].hex, topics: []}
|
139
|
+
outputs.each do |output|
|
140
|
+
data[:topics] << formatter.from_payload(output)
|
141
|
+
end
|
142
|
+
collection << data
|
143
|
+
end
|
144
|
+
return collection
|
145
|
+
end
|
229
146
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
connection = self.connection
|
234
|
-
return {result: :error, message: "missing parameters for #{fun.function_string}" } if arg_types.length != args.length
|
235
|
-
payload = []
|
236
|
-
payload << fun.signature
|
237
|
-
arg_types.zip(args).each do |arg|
|
238
|
-
payload << formatter.to_payload(arg)
|
239
|
-
end
|
240
|
-
txid = connection.eth_send_transaction({to: self.address, from: self.sender, data: "0x" + payload.join()})["result"]
|
241
|
-
return Ethereum::Transaction.new(txid, self.connection, payload.join(), args)
|
242
|
-
end
|
147
|
+
def get_filter_logs(evt, filter_id)
|
148
|
+
parse_filter_data evt, @client.eth_get_filter_logs(filter_id)
|
149
|
+
end
|
243
150
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
tx.wait_for_miner
|
248
|
-
return tx
|
249
|
-
end
|
151
|
+
def get_filter_changes(evt, filter_id)
|
152
|
+
parse_filter_data evt, @client.eth_get_filter_changes(filter_id)
|
153
|
+
end
|
250
154
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
155
|
+
def function_name(fun)
|
156
|
+
count = functions.select {|x| x.name == fun.name }.count
|
157
|
+
name = (count == 1) ? "#{fun.name.underscore}" : "#{fun.name.underscore}__#{fun.inputs.collect {|x| x.type}.join("__")}"
|
158
|
+
name.to_sym
|
159
|
+
end
|
255
160
|
|
161
|
+
def build
|
162
|
+
class_name = @name.camelize
|
163
|
+
parent = self
|
164
|
+
create_function_proxies
|
165
|
+
create_event_proxies
|
166
|
+
class_methods = Class.new do
|
167
|
+
extend Forwardable
|
168
|
+
def_delegators :parent, :abi, :deployment, :events
|
169
|
+
def_delegators :parent, :estimate, :deploy, :deploy_and_wait
|
170
|
+
def_delegators :parent, :address, :address=, :sender, :sender=
|
171
|
+
def_delegator :parent, :call_raw_proxy, :call_raw
|
172
|
+
def_delegator :parent, :call_proxy, :call
|
173
|
+
def_delegator :parent, :transact_proxy, :transact
|
174
|
+
def_delegator :parent, :transact_and_wait_proxy, :transact_and_wait
|
175
|
+
def_delegator :parent, :new_filter_proxy, :new_filter
|
176
|
+
def_delegator :parent, :get_filter_logs_proxy, :get_filter_logs
|
177
|
+
def_delegator :parent, :get_filter_change_proxy, :get_filter_changes
|
178
|
+
define_method :parent do
|
179
|
+
parent
|
256
180
|
end
|
257
181
|
end
|
258
|
-
if Object.const_defined?(class_name)
|
259
|
-
Object.send(:remove_const, class_name)
|
260
|
-
end
|
182
|
+
Object.send(:remove_const, class_name) if Object.const_defined?(class_name)
|
261
183
|
Object.const_set(class_name, class_methods)
|
262
184
|
@class_object = class_methods
|
263
185
|
end
|
264
186
|
|
187
|
+
private
|
188
|
+
def create_function_proxies
|
189
|
+
parent = self
|
190
|
+
call_raw_proxy, call_proxy, transact_proxy, transact_and_wait_proxy = Class.new, Class.new, Class.new, Class.new
|
191
|
+
@functions.each do |fun|
|
192
|
+
call_raw_proxy.send(:define_method, parent.function_name(fun)) { |*args| parent.call_raw(fun, *args) }
|
193
|
+
call_proxy.send(:define_method, parent.function_name(fun)) { |*args| parent.call(fun, *args) }
|
194
|
+
transact_proxy.send(:define_method, parent.function_name(fun)) { |*args| parent.transact(fun, *args) }
|
195
|
+
transact_and_wait_proxy.send(:define_method, parent.function_name(fun)) { |*args| parent.transact_and_wait(fun, *args) }
|
196
|
+
end
|
197
|
+
@call_raw_proxy, @call_proxy, @transact_proxy, @transact_and_wait_proxy = call_raw_proxy.new, call_proxy.new, transact_proxy.new, transact_and_wait_proxy.new
|
198
|
+
end
|
199
|
+
|
200
|
+
def create_event_proxies
|
201
|
+
parent = self
|
202
|
+
new_filter_proxy, get_filter_logs_proxy, get_filter_change_proxy = Class.new, Class.new, Class.new
|
203
|
+
events.each do |evt|
|
204
|
+
new_filter_proxy.send(:define_method, evt.name.underscore) { |*args| parent.create_filter(evt, *args) }
|
205
|
+
get_filter_logs_proxy.send(:define_method, evt.name.underscore) { |*args| parent.get_filter_logs(evt, *args) }
|
206
|
+
get_filter_change_proxy.send(:define_method, evt.name.underscore) { |*args| parent.get_filter_changes(evt, *args) }
|
207
|
+
end
|
208
|
+
@new_filter_proxy, @get_filter_logs_proxy, @get_filter_change_proxy = new_filter_proxy.new, get_filter_logs_proxy.new, get_filter_change_proxy.new
|
209
|
+
end
|
265
210
|
end
|
266
211
|
end
|
@@ -8,8 +8,7 @@ module Ethereum
|
|
8
8
|
@input_types = data["inputs"].collect {|x| x["type"]}
|
9
9
|
@inputs = data["inputs"].collect {|x| x["name"]}
|
10
10
|
@event_string = "#{@name}(#{@input_types.join(",")})"
|
11
|
-
@signature =
|
12
|
-
# @signature = Digest::SHA3.hexdigest @event_string, 256
|
11
|
+
@signature = Digest::SHA3.hexdigest(@event_string, 256)
|
13
12
|
end
|
14
13
|
|
15
14
|
def set_address(address)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Ethereum
|
2
|
+
class Decoder
|
3
|
+
|
4
|
+
def decode(type, value, start = 0)
|
5
|
+
value = value.gsub(/^0x/,'')
|
6
|
+
core, subtype = Abi::parse_type(type)
|
7
|
+
method_name = "decode_#{core}".to_sym
|
8
|
+
if "bytes" == core and subtype.nil?
|
9
|
+
decode_dynamic_bytes(value, start)
|
10
|
+
elsif "string" == core
|
11
|
+
self.send(method_name, value, start)
|
12
|
+
else
|
13
|
+
self.send(method_name, value[start..start+63])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def decode_uint(value)
|
18
|
+
value.hex
|
19
|
+
end
|
20
|
+
|
21
|
+
def decode_int(value)
|
22
|
+
raise ArgumentError if value.nil?
|
23
|
+
(value[0..1] == "ff") ? (value.hex - (2 ** 256)) : value.hex
|
24
|
+
end
|
25
|
+
|
26
|
+
def decode_bool(value)
|
27
|
+
return true if value == "0000000000000000000000000000000000000000000000000000000000000001"
|
28
|
+
return false if value == "0000000000000000000000000000000000000000000000000000000000000000"
|
29
|
+
raise ArgumentError
|
30
|
+
end
|
31
|
+
|
32
|
+
def decode_address(value)
|
33
|
+
raise ArgumentError if value.size != 40
|
34
|
+
value
|
35
|
+
end
|
36
|
+
|
37
|
+
def decode_bytes(value)
|
38
|
+
value.scan(/.{2}/).collect {|x| x.hex}.pack('C*').strip
|
39
|
+
end
|
40
|
+
|
41
|
+
def decode_dynamic_bytes(value, start = 0)
|
42
|
+
location = decode_uint(value[start..(start+63)]) * 2
|
43
|
+
size = decode_uint(value[location..location+63]) * 2
|
44
|
+
value[location+64..location+63+size].scan(/.{2}/).collect {|x| x.hex}.pack('C*')
|
45
|
+
end
|
46
|
+
|
47
|
+
def decode_string(value, start = 0)
|
48
|
+
decode_dynamic_bytes(value, start).force_encoding('utf-8')
|
49
|
+
end
|
50
|
+
|
51
|
+
def decode_arguments(arguments, data)
|
52
|
+
data = data.gsub(/^0x/,'')
|
53
|
+
types = arguments.map { |o| o.type }
|
54
|
+
types.each.with_index.map { |t , i| decode(t, data, i*64) }
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/lib/ethereum/deployment.rb
CHANGED
@@ -35,9 +35,9 @@ module Ethereum
|
|
35
35
|
@valid_deployment ||= check_deployed
|
36
36
|
end
|
37
37
|
|
38
|
-
def wait_for_deployment(timeout
|
38
|
+
def wait_for_deployment(timeout: DEFAULT_TIMEOUT, step: DEFAULT_STEP)
|
39
39
|
start_time = Time.now
|
40
|
-
|
40
|
+
loop do
|
41
41
|
raise "Transaction #{@id} timed out." if ((Time.now - start_time) > timeout)
|
42
42
|
sleep step
|
43
43
|
yield if block_given?
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Ethereum
|
2
|
+
|
3
|
+
class Encoder
|
4
|
+
|
5
|
+
def encode(type, value)
|
6
|
+
core, subtype = Abi::parse_type(type)
|
7
|
+
if core == "bytes" and subtype.nil?
|
8
|
+
encode_dynamic_bytes(value)
|
9
|
+
else
|
10
|
+
method_name = "encode_#{core}".to_sym
|
11
|
+
self.send(method_name, value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def encode_int(value)
|
16
|
+
to_twos_complement(value).to_s(16).rjust(64, '0')
|
17
|
+
end
|
18
|
+
|
19
|
+
def encode_uint(value)
|
20
|
+
raise ArgumentError if value < 0
|
21
|
+
encode_int(value)
|
22
|
+
end
|
23
|
+
|
24
|
+
def encode_bool(value)
|
25
|
+
(value ? "1" : "0").rjust(64, '0')
|
26
|
+
end
|
27
|
+
|
28
|
+
def encode_fixed(_value)
|
29
|
+
raise NotImplementedError
|
30
|
+
end
|
31
|
+
|
32
|
+
def encode_ufixed(_value)
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def encode_bytes(value)
|
37
|
+
value.each_char.map {|x| x.ord.to_s(16)}.join("").ljust(64, '0')
|
38
|
+
end
|
39
|
+
|
40
|
+
def encode_dynamic_bytes(value)
|
41
|
+
location = encode_uint(32)
|
42
|
+
size = encode_uint(value.size)
|
43
|
+
content = encode_bytes(value)
|
44
|
+
location + size + content
|
45
|
+
end
|
46
|
+
|
47
|
+
def encode_string(value)
|
48
|
+
location = encode_uint(32)
|
49
|
+
size = encode_uint(value.bytes.size)
|
50
|
+
content = value.bytes.map {|x| x.to_s(16)}.join("").ljust(64, '0')
|
51
|
+
location + size + content
|
52
|
+
end
|
53
|
+
|
54
|
+
def encode_address(value)
|
55
|
+
value = value.gsub(/^0x/,'')
|
56
|
+
raise ArgumentError if value.size != 40
|
57
|
+
value
|
58
|
+
end
|
59
|
+
|
60
|
+
def ensure_prefix(value)
|
61
|
+
value.start_with?("0x") ? value : ("0x" + value)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def to_twos_complement(number)
|
66
|
+
(number & ((1 << 256) - 1))
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/lib/ethereum/formatter.rb
CHANGED
@@ -103,6 +103,10 @@ module Ethereum
|
|
103
103
|
(hexstring.gsub(/^0x/,'')[0..1] == "ff") ? (hexstring.hex - (2 ** 256)) : hexstring.hex
|
104
104
|
end
|
105
105
|
|
106
|
+
def bool_to_payload(bool)
|
107
|
+
int_to_payload(bool ? 1 : 0)
|
108
|
+
end
|
109
|
+
|
106
110
|
def address_to_payload(address)
|
107
111
|
from_address(address)
|
108
112
|
end
|
@@ -112,7 +116,7 @@ module Ethereum
|
|
112
116
|
end
|
113
117
|
|
114
118
|
def int_to_payload(int)
|
115
|
-
self.to_twos_complement(
|
119
|
+
self.to_twos_complement(int).rjust(64, '0')
|
116
120
|
end
|
117
121
|
|
118
122
|
def bytes_to_payload(bytes)
|
@@ -123,6 +127,10 @@ module Ethereum
|
|
123
127
|
self.bytes_to_payload(bytes)
|
124
128
|
end
|
125
129
|
|
130
|
+
def construtor_params_to_payload(abi, params)
|
131
|
+
abi.map.with_index { |var, i| to_payload([var["type"], params[i]]) }.join
|
132
|
+
end
|
133
|
+
|
126
134
|
def get_base_type(typename)
|
127
135
|
typename.gsub(/\d+/,'')
|
128
136
|
end
|
data/lib/ethereum/function.rb
CHANGED
@@ -12,8 +12,24 @@ module Ethereum
|
|
12
12
|
@outputs = data["outputs"].collect do |output|
|
13
13
|
Ethereum::FunctionOutput.new(output)
|
14
14
|
end
|
15
|
-
@function_string =
|
16
|
-
@signature =
|
15
|
+
@function_string = self.class.calc_signature(@name, @inputs)
|
16
|
+
@signature = self.class.calc_id(@function_string)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.to_canonical_type(type)
|
20
|
+
type
|
21
|
+
.gsub(/(int)(\z|\D)/, '\1256\2')
|
22
|
+
.gsub(/(uint)(\z|\D)/, '\1256\2')
|
23
|
+
.gsub(/(fixed)(\z|\D)/, '\1128x128\2')
|
24
|
+
.gsub(/(ufixed)(\z|\D)/, '\1128x128\2')
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.calc_signature(name, inputs)
|
28
|
+
"#{name}(#{inputs.collect {|x| self.to_canonical_type(x.type) }.join(",")})"
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.calc_id(signature)
|
32
|
+
Digest::SHA3.hexdigest(signature, 256)[0..7]
|
17
33
|
end
|
18
34
|
|
19
35
|
end
|
data/lib/ethereum/http_client.rb
CHANGED
data/lib/ethereum/initializer.rb
CHANGED
@@ -13,13 +13,13 @@ module Ethereum
|
|
13
13
|
abi = JSON.parse(sol_output[contract]["abi"] )
|
14
14
|
name = contract
|
15
15
|
code = sol_output[contract]["bin"]
|
16
|
-
@contracts << Contract.new(name, code, abi)
|
16
|
+
@contracts << Contract.new(name, code, abi, @client)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
def build_all
|
21
21
|
@contracts.each do |contract|
|
22
|
-
contract.build
|
22
|
+
contract.build
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
data/lib/ethereum/ipc_client.rb
CHANGED
data/lib/ethereum/singleton.rb
CHANGED
data/lib/ethereum/solidity.rb
CHANGED
@@ -39,7 +39,7 @@ module Ethereum
|
|
39
39
|
|
40
40
|
def execute_solc(dir, filename)
|
41
41
|
cmd = "#{@bin_path} #{@args} '#{dir}' '#{filename}'"
|
42
|
-
|
42
|
+
_, stderr, status = Open3.capture3(cmd)
|
43
43
|
raise SystemCallError, "Unanable to run solc compliers" if status.exitstatus == 127
|
44
44
|
raise CompilationError, stderr unless status.exitstatus == 0
|
45
45
|
end
|
data/lib/ethereum/version.rb
CHANGED
@@ -4,20 +4,20 @@ namespace :ethereum do
|
|
4
4
|
namespace :contract do
|
5
5
|
|
6
6
|
desc "Compile a contract"
|
7
|
-
task :compile, [:path] do |
|
7
|
+
task :compile, [:path] do |_, args|
|
8
8
|
contract = Ethereum::Solidity.new.compile(args[:path])
|
9
9
|
puts "Contract abi:"
|
10
|
-
puts contract["
|
10
|
+
puts contract.map { |k, v| "#{k}: #{v["abi"]}" }.join("\n\n")
|
11
11
|
puts
|
12
12
|
puts "Contract binary code:"
|
13
|
-
puts contract["
|
13
|
+
puts contract.map { |k, v| "#{k}: #{v["bin"]}" }.join("\n\n")
|
14
14
|
puts
|
15
15
|
end
|
16
16
|
|
17
17
|
desc "Compile and deploy contract"
|
18
|
-
task :deploy, [:path] do |
|
18
|
+
task :deploy, [:path] do |_, args|
|
19
19
|
puts "Deploing contract #{args[:path]}"
|
20
|
-
@works = Ethereum::Contract.
|
20
|
+
@works = Ethereum::Contract.create(file: args[:path])
|
21
21
|
@works.deploy_and_wait { puts "." }
|
22
22
|
address = @works.deployment.contract_address
|
23
23
|
puts "Contract deployed under address: #{address}"
|
@@ -5,8 +5,8 @@ namespace :ethereum do
|
|
5
5
|
|
6
6
|
desc "Run testnet node "
|
7
7
|
task :test do
|
8
|
-
|
9
|
-
account =
|
8
|
+
_, out, _ = Open3.capture3("parity --chain ~/.parity/ropsten.json account list")
|
9
|
+
account = out.split(/[\[,\]]/)[1]
|
10
10
|
cmd = "parity --chain ~/.parity/ropsten.json --password ~/.parity/pass --unlock #{account} --author #{account}"
|
11
11
|
puts cmd
|
12
12
|
system cmd
|
@@ -14,8 +14,8 @@ namespace :ethereum do
|
|
14
14
|
|
15
15
|
desc "Run morden (production) node"
|
16
16
|
task :run do
|
17
|
-
|
18
|
-
account =
|
17
|
+
_, out, _ = Open3.capture3("parity account list")
|
18
|
+
account = out.split(/[\[,\]]/)[1]
|
19
19
|
system "parity --password ~/.parity/pass --unlock #{account} --author #{account} --no-jsonrpc"
|
20
20
|
end
|
21
21
|
|
@@ -30,7 +30,7 @@ namespace :ethereum do
|
|
30
30
|
task :waitforsync do
|
31
31
|
formatter = Ethereum::Formatter.new
|
32
32
|
begin
|
33
|
-
|
33
|
+
loop do
|
34
34
|
result = Ethereum::Singleton.instance.eth_syncing["result"]
|
35
35
|
unless result
|
36
36
|
puts "Synced"
|
@@ -5,13 +5,13 @@ namespace :ethereum do
|
|
5
5
|
namespace :transaction do
|
6
6
|
|
7
7
|
desc "Get info about transaction"
|
8
|
-
task :byhash, [:id] do |
|
8
|
+
task :byhash, [:id] do |_, args|
|
9
9
|
@client = Ethereum::Singleton.instance
|
10
10
|
pp @client.eth_get_transaction_by_hash(args[:id])
|
11
11
|
end
|
12
12
|
|
13
13
|
desc "Send"
|
14
|
-
task :send, [:address, :amount] do |
|
14
|
+
task :send, [:address, :amount] do |_, args|
|
15
15
|
@client = Ethereum::Singleton.instance
|
16
16
|
@formatter = Ethereum::Formatter.new
|
17
17
|
address = @formatter.to_address(args[:address])
|
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:
|
4
|
+
version: 2.0.0
|
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-01-
|
11
|
+
date: 2017-01-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -147,11 +147,14 @@ files:
|
|
147
147
|
- contracts/classic/QueueSample.sol
|
148
148
|
- ethereum.gemspec
|
149
149
|
- lib/ethereum.rb
|
150
|
+
- lib/ethereum/abi.rb
|
150
151
|
- lib/ethereum/client.rb
|
151
152
|
- lib/ethereum/contract.rb
|
152
153
|
- lib/ethereum/contract_event.rb
|
153
154
|
- lib/ethereum/contract_initializer.rb
|
155
|
+
- lib/ethereum/decoder.rb
|
154
156
|
- lib/ethereum/deployment.rb
|
157
|
+
- lib/ethereum/encoder.rb
|
155
158
|
- lib/ethereum/formatter.rb
|
156
159
|
- lib/ethereum/function.rb
|
157
160
|
- lib/ethereum/function_input.rb
|
@@ -191,7 +194,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
191
194
|
version: '0'
|
192
195
|
requirements: []
|
193
196
|
rubyforge_project:
|
194
|
-
rubygems_version: 2.5.
|
197
|
+
rubygems_version: 2.5.1
|
195
198
|
signing_key:
|
196
199
|
specification_version: 4
|
197
200
|
summary: Ruby Ethereum client using the JSON-RPC interface
|