web3-eth 0.1.0 → 0.2.0
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/.idea/workspace.xml +402 -168
- data/README.md +115 -3
- data/lib/web3/eth/abi/abi_coder.rb +370 -0
- data/lib/web3/eth/abi/constant.rb +30 -0
- data/lib/web3/eth/abi/exceptions.rb +28 -0
- data/lib/web3/eth/abi/type.rb +116 -0
- data/lib/web3/eth/abi/utils.rb +224 -0
- data/lib/web3/eth/block.rb +1 -3
- data/lib/web3/eth/call_trace.rb +42 -0
- data/lib/web3/eth/contract.rb +134 -0
- data/lib/web3/eth/{ethereum.rb → eth_module.rb} +10 -1
- data/lib/web3/eth/etherscan.rb +71 -0
- data/lib/web3/eth/log.rb +30 -0
- data/lib/web3/eth/rpc.rb +4 -5
- data/lib/web3/eth/trace_module.rb +26 -0
- data/lib/web3/eth/transaction.rb +18 -0
- data/lib/web3/eth/transaction_receipt.rb +3 -1
- data/lib/web3/eth/utility.rb +3 -0
- data/lib/web3/eth/version.rb +1 -1
- data/lib/web3/eth.rb +8 -2
- data/web3-eth.gemspec +2 -0
- metadata +41 -3
@@ -0,0 +1,224 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
require 'digest/sha3'
|
5
|
+
require 'openssl'
|
6
|
+
require 'rlp'
|
7
|
+
|
8
|
+
module Web3::Eth::Abi
|
9
|
+
module Utils
|
10
|
+
|
11
|
+
extend self
|
12
|
+
|
13
|
+
include Constant
|
14
|
+
|
15
|
+
##
|
16
|
+
# Not the keccak in sha3, although it's underlying lib named SHA3
|
17
|
+
#
|
18
|
+
def keccak256(x)
|
19
|
+
Digest::SHA3.new(256).digest(x)
|
20
|
+
end
|
21
|
+
|
22
|
+
def keccak512(x)
|
23
|
+
Digest::SHA3.new(512).digest(x)
|
24
|
+
end
|
25
|
+
|
26
|
+
def keccak256_rlp(x)
|
27
|
+
keccak256 RLP.encode(x)
|
28
|
+
end
|
29
|
+
|
30
|
+
def sha256(x)
|
31
|
+
Digest::SHA256.digest x
|
32
|
+
end
|
33
|
+
|
34
|
+
def double_sha256(x)
|
35
|
+
sha256 sha256(x)
|
36
|
+
end
|
37
|
+
|
38
|
+
def ripemd160(x)
|
39
|
+
Digest::RMD160.digest x
|
40
|
+
end
|
41
|
+
|
42
|
+
def hash160(x)
|
43
|
+
ripemd160 sha256(x)
|
44
|
+
end
|
45
|
+
|
46
|
+
def hash160_hex(x)
|
47
|
+
encode_hex hash160(x)
|
48
|
+
end
|
49
|
+
|
50
|
+
def mod_exp(x, y, n)
|
51
|
+
x.to_bn.mod_exp(y, n).to_i
|
52
|
+
end
|
53
|
+
|
54
|
+
def mod_mul(x, y, n)
|
55
|
+
x.to_bn.mod_mul(y, n).to_i
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_signed(i)
|
59
|
+
i > Constant::INT_MAX ? (i-Constant::TT256) : i
|
60
|
+
end
|
61
|
+
|
62
|
+
def base58_check_to_bytes(s)
|
63
|
+
leadingzbytes = s.match(/\A1*/)[0]
|
64
|
+
data = Constant::BYTE_ZERO * leadingzbytes.size + BaseConvert.convert(s, 58, 256)
|
65
|
+
|
66
|
+
raise ChecksumError, "double sha256 checksum doesn't match" unless double_sha256(data[0...-4])[0,4] == data[-4..-1]
|
67
|
+
data[1...-4]
|
68
|
+
end
|
69
|
+
|
70
|
+
def bytes_to_base58_check(bytes, magicbyte=0)
|
71
|
+
bs = "#{magicbyte.chr}#{bytes}"
|
72
|
+
leadingzbytes = bs.match(/\A#{Constant::BYTE_ZERO}*/)[0]
|
73
|
+
checksum = double_sha256(bs)[0,4]
|
74
|
+
'1'*leadingzbytes.size + BaseConvert.convert("#{bs}#{checksum}", 256, 58)
|
75
|
+
end
|
76
|
+
|
77
|
+
def ceil32(x)
|
78
|
+
x % 32 == 0 ? x : (x + 32 - x%32)
|
79
|
+
end
|
80
|
+
|
81
|
+
def encode_hex(b)
|
82
|
+
RLP::Utils.encode_hex b
|
83
|
+
end
|
84
|
+
|
85
|
+
def decode_hex(s)
|
86
|
+
RLP::Utils.decode_hex s
|
87
|
+
end
|
88
|
+
|
89
|
+
def big_endian_to_int(s)
|
90
|
+
RLP::Sedes.big_endian_int.deserialize s.sub(/\A(\x00)+/, '')
|
91
|
+
end
|
92
|
+
|
93
|
+
def int_to_big_endian(n)
|
94
|
+
RLP::Sedes.big_endian_int.serialize n
|
95
|
+
end
|
96
|
+
|
97
|
+
def lpad(x, symbol, l)
|
98
|
+
return x if x.size >= l
|
99
|
+
symbol * (l - x.size) + x
|
100
|
+
end
|
101
|
+
|
102
|
+
def rpad(x, symbol, l)
|
103
|
+
return x if x.size >= l
|
104
|
+
x + symbol * (l - x.size)
|
105
|
+
end
|
106
|
+
|
107
|
+
def zpad(x, l)
|
108
|
+
lpad x, BYTE_ZERO, l
|
109
|
+
end
|
110
|
+
|
111
|
+
def zunpad(x)
|
112
|
+
x.sub /\A\x00+/, ''
|
113
|
+
end
|
114
|
+
|
115
|
+
def zpad_int(n, l=32)
|
116
|
+
zpad encode_int(n), l
|
117
|
+
end
|
118
|
+
|
119
|
+
def zpad_hex(s, l=32)
|
120
|
+
zpad decode_hex(s), l
|
121
|
+
end
|
122
|
+
|
123
|
+
def int_to_addr(x)
|
124
|
+
zpad_int x, 20
|
125
|
+
end
|
126
|
+
|
127
|
+
def encode_int(n)
|
128
|
+
raise ArgumentError, "Integer invalid or out of range: #{n}" unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
|
129
|
+
int_to_big_endian n
|
130
|
+
end
|
131
|
+
|
132
|
+
def decode_int(v)
|
133
|
+
raise ArgumentError, "No leading zero bytes allowed for integers" if v.size > 0 && (v[0] == Constant::BYTE_ZERO || v[0] == 0)
|
134
|
+
big_endian_to_int v
|
135
|
+
end
|
136
|
+
|
137
|
+
def bytearray_to_int(arr)
|
138
|
+
o = 0
|
139
|
+
arr.each {|x| o = (o << 8) + x }
|
140
|
+
o
|
141
|
+
end
|
142
|
+
|
143
|
+
def int_array_to_bytes(arr)
|
144
|
+
arr.pack('C*')
|
145
|
+
end
|
146
|
+
|
147
|
+
def bytes_to_int_array(bytes)
|
148
|
+
bytes.unpack('C*')
|
149
|
+
end
|
150
|
+
|
151
|
+
def coerce_to_int(x)
|
152
|
+
if x.is_a?(Numeric)
|
153
|
+
x
|
154
|
+
elsif x.size == 40
|
155
|
+
big_endian_to_int decode_hex(x)
|
156
|
+
else
|
157
|
+
big_endian_to_int x
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def coerce_to_bytes(x)
|
162
|
+
if x.is_a?(Numeric)
|
163
|
+
int_to_big_endian x
|
164
|
+
elsif x.size == 40
|
165
|
+
decode_hex(x)
|
166
|
+
else
|
167
|
+
x
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def coerce_addr_to_hex(x)
|
172
|
+
if x.is_a?(Numeric)
|
173
|
+
encode_hex zpad(int_to_big_endian(x), 20)
|
174
|
+
elsif x.size == 40 || x.size == 0
|
175
|
+
x
|
176
|
+
else
|
177
|
+
encode_hex zpad(x, 20)[-20..-1]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def normalize_address(x, allow_blank: false)
|
182
|
+
address = Address.new(x)
|
183
|
+
raise ValueError, "address is blank" if !allow_blank && address.blank?
|
184
|
+
address.to_bytes
|
185
|
+
end
|
186
|
+
|
187
|
+
def mk_contract_address(sender, nonce)
|
188
|
+
keccak256_rlp([normalize_address(sender), nonce])[12..-1]
|
189
|
+
end
|
190
|
+
|
191
|
+
def mk_metropolis_contract_address(sender, initcode)
|
192
|
+
keccak256(normalize_address(sender) + initcode)[12..-1]
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
def parse_int_or_hex(s)
|
197
|
+
if s.is_a?(Numeric)
|
198
|
+
s
|
199
|
+
elsif s[0,2] == '0x'
|
200
|
+
big_endian_to_int decode_hex(normalize_hex_without_prefix(s))
|
201
|
+
else
|
202
|
+
s.to_i
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def normalize_hex_without_prefix(s)
|
207
|
+
if s[0,2] == '0x'
|
208
|
+
(s.size % 2 == 1 ? '0' : '') + s[2..-1]
|
209
|
+
else
|
210
|
+
s
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def function_signature method_name, arg_types
|
215
|
+
"#{method_name}(#{arg_types.join(',')})"
|
216
|
+
end
|
217
|
+
|
218
|
+
def signature_hash signature, length=64
|
219
|
+
encode_hex(keccak256(signature))[0...length]
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
end
|
224
|
+
end
|
data/lib/web3/eth/block.rb
CHANGED
@@ -15,9 +15,7 @@ module Web3
|
|
15
15
|
self.class.send(:define_method, k, proc {self.instance_variable_get("@#{k}")})
|
16
16
|
end
|
17
17
|
|
18
|
-
@transactions =
|
19
|
-
Transaction.new t
|
20
|
-
}
|
18
|
+
@transactions = @transactions.collect {|t| Web3::Eth::Transaction.new t }
|
21
19
|
|
22
20
|
end
|
23
21
|
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Web3
|
2
|
+
module Eth
|
3
|
+
|
4
|
+
class CallTrace
|
5
|
+
|
6
|
+
include Web3::Eth::Utility
|
7
|
+
|
8
|
+
attr_reader :raw_data
|
9
|
+
|
10
|
+
def initialize trace_data
|
11
|
+
@raw_data = trace_data
|
12
|
+
|
13
|
+
trace_data.each do |k, v|
|
14
|
+
self.instance_variable_set("@#{k}", v)
|
15
|
+
self.class.send(:define_method, k, proc {self.instance_variable_get("@#{k}")})
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def value_eth
|
21
|
+
wei_to_ether from_hex action['value']
|
22
|
+
end
|
23
|
+
|
24
|
+
def from
|
25
|
+
action['from']
|
26
|
+
end
|
27
|
+
|
28
|
+
def to
|
29
|
+
action['to']
|
30
|
+
end
|
31
|
+
|
32
|
+
def input
|
33
|
+
action['input']
|
34
|
+
end
|
35
|
+
|
36
|
+
def output
|
37
|
+
result['output']
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Web3
|
2
|
+
module Eth
|
3
|
+
class Contract
|
4
|
+
|
5
|
+
include Abi::AbiCoder
|
6
|
+
include Abi::Utils
|
7
|
+
include Utility
|
8
|
+
|
9
|
+
|
10
|
+
class ContractInstance
|
11
|
+
|
12
|
+
def initialize contract, address
|
13
|
+
@contract = contract
|
14
|
+
@address = address
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing m, *args
|
18
|
+
@contract.call_contract @address, m.to_s, args
|
19
|
+
end
|
20
|
+
|
21
|
+
def __contract__
|
22
|
+
@contract
|
23
|
+
end
|
24
|
+
|
25
|
+
def __address__
|
26
|
+
@address
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class ContractMethod
|
32
|
+
attr_reader :abi, :signature, :name, :signature_hash, :input_types, :output_types, :constant
|
33
|
+
|
34
|
+
def initialize abi
|
35
|
+
@abi = abi
|
36
|
+
@name = abi['name']
|
37
|
+
@constant = !!abi['constant']
|
38
|
+
@input_types = abi['inputs'].map{|a| a['type']}
|
39
|
+
@output_types = abi['outputs'].map{|a| a['type']} if abi['outputs']
|
40
|
+
@signature = Abi::Utils.function_signature @name, @input_types
|
41
|
+
@signature_hash = Abi::Utils.signature_hash @signature, (abi['type']=='event' ? 64 : 8)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :web3_rpc, :abi, :functions, :events, :constructor
|
47
|
+
|
48
|
+
def initialize abi, web_rpc = nil
|
49
|
+
@web3_rpc = web_rpc
|
50
|
+
@abi = abi.kind_of?(String) ? JSON.parse(abi) : abi
|
51
|
+
parse_abi @abi
|
52
|
+
end
|
53
|
+
|
54
|
+
def at address
|
55
|
+
ContractInstance.new self, address
|
56
|
+
end
|
57
|
+
|
58
|
+
def call_contract contract_address, method_name, args
|
59
|
+
|
60
|
+
function = functions[method_name]
|
61
|
+
raise "No method found in ABI: #{method_name}" unless function
|
62
|
+
raise "Function #{method_name} is not constant: #{method_name}, requires to sign transaction" unless function.constant
|
63
|
+
|
64
|
+
data = '0x' + function.signature_hash + encode_hex(encode_abi(function.input_types, args) )
|
65
|
+
|
66
|
+
response = web3_rpc.request "eth_call", [{ to: contract_address, data: data}]
|
67
|
+
|
68
|
+
string_data = [remove_0x_head(response)].pack('H*')
|
69
|
+
return nil if string_data.empty?
|
70
|
+
|
71
|
+
result = decode_abi function.output_types, string_data
|
72
|
+
result.length==1 ? result.first : result
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
def find_event_by_hash method_hash
|
77
|
+
events.values.detect{|e| e.signature_hash==method_hash}
|
78
|
+
end
|
79
|
+
|
80
|
+
def find_function_by_hash method_hash
|
81
|
+
functions.values.detect{|e| e.signature_hash==method_hash}
|
82
|
+
end
|
83
|
+
|
84
|
+
def parse_log_args log
|
85
|
+
|
86
|
+
event = find_event_by_hash log.method_hash
|
87
|
+
raise "No event found by hash #{log.method_hash}, probably ABI is not related to log event" unless event
|
88
|
+
|
89
|
+
not_indexed_types = event.abi['inputs'].select{|a| !a['indexed']}.collect{|a| a['type']}
|
90
|
+
not_indexed_values = not_indexed_types.empty? ? [] :
|
91
|
+
decode_abi(not_indexed_types, [remove_0x_head(log.raw_data['data'])].pack('H*') )
|
92
|
+
|
93
|
+
indexed_types = event.abi['inputs'].select{|a| a['indexed']}.collect{|a| a['type']}
|
94
|
+
indexed_values = [indexed_types, log.indexed_args].transpose.collect{|arg|
|
95
|
+
decode_abi([arg.first], [arg.second].pack('H*') ).first
|
96
|
+
}
|
97
|
+
|
98
|
+
i = j = 0
|
99
|
+
|
100
|
+
args = event.abi['inputs'].collect{|input|
|
101
|
+
input['indexed'] ? (i+=1; indexed_values[i-1]) : (j+=1;not_indexed_values[j-1])
|
102
|
+
}
|
103
|
+
|
104
|
+
{event: event, args: args}
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
def parse_call_args transaction
|
109
|
+
function = find_function_by_hash transaction.method_hash
|
110
|
+
raise "No function found by hash #{transaction.method_hash}, probably ABI is not related to call" unless function
|
111
|
+
[function.input_types, transaction.method_arguments].transpose.collect{|arg|
|
112
|
+
decode_abi([arg.first], [arg.second].pack('H*') ).first
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
def parse_constructor_args transaction
|
118
|
+
# suffix # 0xa1 0x65 'b' 'z' 'z' 'r' '0' 0x58 0x20 <32 bytes swarm hash> 0x00 0x29
|
119
|
+
# look http://solidity.readthedocs.io/en/latest/metadata.html for details
|
120
|
+
args = transaction.input[/a165627a7a72305820\w{64}0029(\w*)/,1]
|
121
|
+
args ? decode_abi(constructor.input_types, [args].pack('H*') ) : []
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def parse_abi abi
|
127
|
+
@functions = Hash[abi.select{|a| a['type']=='function'}.map{|a| [a['name'], ContractMethod.new(a)]}]
|
128
|
+
@events = Hash[abi.select{|a| a['type']=='event'}.map{|a| [a['name'], ContractMethod.new(a)]}]
|
129
|
+
@constructor = ContractMethod.new abi.detect{|a| a['type']=='constructor'}
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Web3
|
2
2
|
module Eth
|
3
3
|
|
4
|
-
class
|
4
|
+
class EthModule
|
5
5
|
|
6
6
|
include Web3::Eth::Utility
|
7
7
|
|
@@ -33,6 +33,15 @@ module Web3
|
|
33
33
|
TransactionReceipt.new @web3_rpc.request("#{PREFIX}#{__method__}", [tx_hash])
|
34
34
|
end
|
35
35
|
|
36
|
+
def contract abi
|
37
|
+
Web3::Eth::Contract.new abi, @web3_rpc
|
38
|
+
end
|
39
|
+
|
40
|
+
def load_contract etherscan_api, contract_address
|
41
|
+
contract(etherscan_api.contract_getabi address: contract_address).at contract_address
|
42
|
+
end
|
43
|
+
|
44
|
+
|
36
45
|
def method_missing m, *args
|
37
46
|
@web3_rpc.request "#{PREFIX}#{m}", args[0]
|
38
47
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Web3
|
2
|
+
module Eth
|
3
|
+
class Etherscan
|
4
|
+
|
5
|
+
|
6
|
+
DEFAULT_CONNECT_OPTIONS = {
|
7
|
+
open_timeout: 10,
|
8
|
+
read_timeout: 70,
|
9
|
+
parse_result: true,
|
10
|
+
url: 'https://api.etherscan.io/api'
|
11
|
+
}
|
12
|
+
|
13
|
+
attr_reader :api_key, :connect_options
|
14
|
+
|
15
|
+
def initialize api_key, connect_options: DEFAULT_CONNECT_OPTIONS
|
16
|
+
@api_key = api_key
|
17
|
+
@connect_options = connect_options
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing m, *args
|
21
|
+
api_module, action = m.to_s.split '_', 2
|
22
|
+
raise "Calling method must be in form <module>_<action>" unless action
|
23
|
+
|
24
|
+
arguments = args[0].kind_of?(String) ? { address: args[0] } : args[0]
|
25
|
+
result = request api_module, action, arguments
|
26
|
+
|
27
|
+
if connect_options[:parse_result]
|
28
|
+
begin
|
29
|
+
JSON.parse result
|
30
|
+
rescue
|
31
|
+
result
|
32
|
+
end
|
33
|
+
else
|
34
|
+
result
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def request api_module, action, args = {}
|
43
|
+
|
44
|
+
uri = URI connect_options[:url]
|
45
|
+
uri.query = URI.encode_www_form({
|
46
|
+
module: api_module,
|
47
|
+
action: action,
|
48
|
+
apikey: api_key
|
49
|
+
}.merge(args))
|
50
|
+
|
51
|
+
Net::HTTP.start(uri.host, uri.port,
|
52
|
+
connect_options.merge(use_ssl: uri.scheme=='https' )) do |http|
|
53
|
+
|
54
|
+
request = Net::HTTP::Get.new uri
|
55
|
+
response = http.request request
|
56
|
+
|
57
|
+
raise "Error code #{response.code} on request #{uri.to_s} #{request.body}" unless response.kind_of? Net::HTTPOK
|
58
|
+
|
59
|
+
json = JSON.parse(response.body)
|
60
|
+
|
61
|
+
raise "Response #{json['message']} on request #{uri.to_s}" unless json['status']=='1'
|
62
|
+
|
63
|
+
json['result']
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/web3/eth/log.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Web3
|
2
|
+
module Eth
|
3
|
+
|
4
|
+
class Log
|
5
|
+
|
6
|
+
attr_reader :raw_data
|
7
|
+
|
8
|
+
def initialize log
|
9
|
+
@raw_data = log
|
10
|
+
|
11
|
+
log.each do |k, v|
|
12
|
+
self.instance_variable_set("@#{k}", v)
|
13
|
+
self.class.send(:define_method, k, proc {self.instance_variable_get("@#{k}")})
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_hash
|
19
|
+
topics.first[2..65]
|
20
|
+
end
|
21
|
+
|
22
|
+
def indexed_args
|
23
|
+
topics[1...topics.size].collect{|x| x[2..65]}
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/lib/web3/eth/rpc.rb
CHANGED
@@ -17,6 +17,8 @@ module Web3
|
|
17
17
|
DEFAULT_HOST = 'localhost'
|
18
18
|
DEFAULT_PORT = 8545
|
19
19
|
|
20
|
+
attr_reader :eth, :trace
|
21
|
+
|
20
22
|
def initialize host: DEFAULT_HOST, port: DEFAULT_PORT, connect_options: DEFAULT_CONNECT_OPTIONS
|
21
23
|
|
22
24
|
@client_id = Random.rand 10000000
|
@@ -24,12 +26,9 @@ module Web3
|
|
24
26
|
@uri = URI((connect_options[:use_ssl] ? 'https' : 'http')+ "://#{host}:#{port}")
|
25
27
|
@connect_options = connect_options
|
26
28
|
|
27
|
-
@eth =
|
28
|
-
|
29
|
-
|
29
|
+
@eth = EthModule.new self
|
30
|
+
@trace = TraceModule.new self
|
30
31
|
|
31
|
-
def eth
|
32
|
-
@eth
|
33
32
|
end
|
34
33
|
|
35
34
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Web3
|
2
|
+
module Eth
|
3
|
+
|
4
|
+
class TraceModule
|
5
|
+
|
6
|
+
include Web3::Eth::Utility
|
7
|
+
|
8
|
+
PREFIX = 'trace_'
|
9
|
+
|
10
|
+
def initialize web3_rpc
|
11
|
+
@web3_rpc = web3_rpc
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing m, *args
|
15
|
+
@web3_rpc.request "#{PREFIX}#{m}", args[0]
|
16
|
+
end
|
17
|
+
|
18
|
+
def internalCallsByHash tx_hash
|
19
|
+
@web3_rpc.request("#{PREFIX}transaction", [tx_hash]).select{|t| t['traceAddress']!=[]}.collect{|t|
|
20
|
+
CallTrace.new t
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/web3/eth/transaction.rb
CHANGED
@@ -18,6 +18,24 @@ module Web3
|
|
18
18
|
|
19
19
|
end
|
20
20
|
|
21
|
+
def method_hash
|
22
|
+
if input && input.length>=10
|
23
|
+
input[2...10]
|
24
|
+
else
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_arguments
|
30
|
+
if input && input.length>10
|
31
|
+
(0...(input.length-10)/ 64).to_a.collect{|index|
|
32
|
+
input[index*64+10..index*64+73]
|
33
|
+
}
|
34
|
+
else
|
35
|
+
[]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
21
39
|
def block_number
|
22
40
|
from_hex blockNumber
|
23
41
|
end
|
@@ -15,6 +15,8 @@ module Web3
|
|
15
15
|
self.class.send(:define_method, k, proc {self.instance_variable_get("@#{k}")})
|
16
16
|
end
|
17
17
|
|
18
|
+
@logs = @logs.collect {|log| Web3::Eth::Log.new log }
|
19
|
+
|
18
20
|
end
|
19
21
|
|
20
22
|
def block_number
|
@@ -22,7 +24,7 @@ module Web3
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def success?
|
25
|
-
status==1 || status.nil?
|
27
|
+
status==1 || status=='0x1' || status.nil?
|
26
28
|
end
|
27
29
|
|
28
30
|
def gas_used_eth
|
data/lib/web3/eth/utility.rb
CHANGED
data/lib/web3/eth/version.rb
CHANGED
data/lib/web3/eth.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
require "web3/eth/version"
|
2
|
+
require "web3/eth/abi/abi_coder"
|
2
3
|
require "web3/eth/utility"
|
3
4
|
require "web3/eth/block"
|
4
5
|
require "web3/eth/transaction"
|
6
|
+
require "web3/eth/contract"
|
7
|
+
require "web3/eth/call_trace"
|
8
|
+
require "web3/eth/log"
|
5
9
|
require "web3/eth/transaction_receipt"
|
6
|
-
require "web3/eth/
|
7
|
-
require "web3/eth/
|
10
|
+
require "web3/eth/eth_module"
|
11
|
+
require "web3/eth/trace_module"
|
12
|
+
require "web3/eth/etherscan"
|
13
|
+
require "web3/eth/rpc"
|
data/web3-eth.gemspec
CHANGED
@@ -23,6 +23,8 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
24
|
spec.require_paths = ["lib"]
|
25
25
|
|
26
|
+
spec.add_dependency('rlp', '~> 0.7.3')
|
27
|
+
spec.add_dependency('digest-sha3', '~> 1.1.0')
|
26
28
|
spec.add_development_dependency "bundler", "~> 1.14"
|
27
29
|
spec.add_development_dependency "rake", "~> 10.0"
|
28
30
|
spec.add_development_dependency "minitest", "~> 5.0"
|