ethereum-abi 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5929d9e3f2d9f92e4ee09812a83be6ed5ec8f483
4
+ data.tar.gz: 9113211fd4457ca8b4d1aaef4f35a2ed02936ce4
5
+ SHA512:
6
+ metadata.gz: 73b55734bf2fbe1a1cc37ff68904869eb54a6c85c574a5062a259e59b6125fb43732cb25dc15925e59468c8b0acf4f6474a45210aa3d2e243547c169130ef360
7
+ data.tar.gz: 5931f31438e902fc35bc2b2d8c5959c95465698ef6214356da4621df4d65b0a18f7b7f698d82ba70ff8aa83dae029efb3a286a0d5d005d7e8cae2ab2e4c054c1
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 zhangyaning
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ A Ruby implementation of [Ethereum-Contract-ABI](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI).
2
+
3
+ Extracted from [ruby-ethereum](https://github.com/janx/ruby-ethereum)
@@ -0,0 +1,333 @@
1
+ require 'ethereum/abi/contract_translator'
2
+ require 'ethereum/abi/type'
3
+ require 'ethereum/abi/constant'
4
+ require 'ethereum/exceptions'
5
+ require 'block_logger'
6
+ require 'rlp'
7
+
8
+ module Ethereum
9
+ ##
10
+ # Contract ABI encoding and decoding.
11
+ #
12
+ # @see https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
13
+ #
14
+ module ABI
15
+
16
+ extend self
17
+
18
+ include Constant
19
+
20
+ class EncodingError < StandardError; end
21
+ class DecodingError < StandardError; end
22
+ class ValueOutOfBounds < StandardError; end
23
+
24
+ ##
25
+ # Encodes multiple arguments using the head/tail mechanism.
26
+ #
27
+ def encode_abi(types, args)
28
+ parsed_types = types.map {|t| Type.parse(t) }
29
+
30
+ head_size = (0...args.size)
31
+ .map {|i| parsed_types[i].size || 32 }
32
+ .reduce(0, &:+)
33
+
34
+ head, tail = '', ''
35
+ args.each_with_index do |arg, i|
36
+ if parsed_types[i].dynamic?
37
+ head += encode_type(Type.size_type, head_size + tail.size)
38
+ tail += encode_type(parsed_types[i], arg)
39
+ else
40
+ head += encode_type(parsed_types[i], arg)
41
+ end
42
+ end
43
+
44
+ "#{head}#{tail}"
45
+ end
46
+ alias :encode :encode_abi
47
+
48
+ ##
49
+ # Encodes a single value (static or dynamic).
50
+ #
51
+ # @param type [Ethereum::ABI::Type] value type
52
+ # @param arg [Object] value
53
+ #
54
+ # @return [String] encoded bytes
55
+ #
56
+ def encode_type(type, arg)
57
+ if %w(string bytes).include?(type.base) && type.sub.empty?
58
+ raise ArgumentError, "arg must be a string" unless arg.instance_of?(String)
59
+
60
+ size = encode_type Type.size_type, arg.size
61
+ padding = BYTE_ZERO * (Utils.ceil32(arg.size) - arg.size)
62
+
63
+ "#{size}#{arg}#{padding}"
64
+ elsif type.dynamic?
65
+ raise ArgumentError, "arg must be an array" unless arg.instance_of?(Array)
66
+
67
+ head, tail = '', ''
68
+ if type.dims.last == 0
69
+ head += encode_type(Type.size_type, arg.size)
70
+ else
71
+ raise ArgumentError, "Wrong array size: found #{arg.size}, expecting #{type.dims.last}" unless arg.size == type.dims.last
72
+ end
73
+
74
+ sub_type = type.subtype
75
+ sub_size = type.subtype.size
76
+ arg.size.times do |i|
77
+ if sub_size.nil?
78
+ head += encode_type(Type.size_type, 32*arg.size + tail.size)
79
+ tail += encode_type(sub_type, arg[i])
80
+ else
81
+ head += encode_type(sub_type, arg[i])
82
+ end
83
+ end
84
+
85
+ "#{head}#{tail}"
86
+ else # static type
87
+ if type.dims.empty?
88
+ encode_primitive_type type, arg
89
+ else
90
+ arg.map {|x| encode_type(type.subtype, x) }.join
91
+ end
92
+ end
93
+ end
94
+
95
+ def encode_primitive_type(type, arg)
96
+ case type.base
97
+ when 'uint'
98
+ real_size = type.sub.to_i
99
+ i = get_uint arg
100
+
101
+ raise ValueOutOfBounds, arg unless i >= 0 && i < 2**real_size
102
+ Utils.zpad_int i
103
+ when 'bool'
104
+ raise ArgumentError, "arg is not bool: #{arg}" unless arg.instance_of?(TrueClass) || arg.instance_of?(FalseClass)
105
+ Utils.zpad_int(arg ? 1: 0)
106
+ when 'int'
107
+ real_size = type.sub.to_i
108
+ i = get_int arg
109
+
110
+ raise ValueOutOfBounds, arg unless i >= -2**(real_size-1) && i < 2**(real_size-1)
111
+ Utils.zpad_int(i % 2**type.sub.to_i)
112
+ when 'ureal', 'ufixed'
113
+ high, low = type.sub.split('x').map(&:to_i)
114
+
115
+ raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**high
116
+ Utils.zpad_int((arg * 2**low).to_i)
117
+ when 'real', 'fixed'
118
+ high, low = type.sub.split('x').map(&:to_i)
119
+
120
+ raise ValueOutOfBounds, arg unless arg >= -2**(high - 1) && arg < 2**(high - 1)
121
+
122
+ i = (arg * 2**low).to_i
123
+ Utils.zpad_int(i % 2**(high+low))
124
+ when 'string', 'bytes'
125
+ raise EncodingError, "Expecting string: #{arg}" unless arg.instance_of?(String)
126
+
127
+ if type.sub.empty? # variable length type
128
+ size = Utils.zpad_int arg.size
129
+ padding = BYTE_ZERO * (Utils.ceil32(arg.size) - arg.size)
130
+ "#{size}#{arg}#{padding}"
131
+ else # fixed length type
132
+ raise ValueOutOfBounds, arg unless arg.size <= type.sub.to_i
133
+
134
+ padding = BYTE_ZERO * (32 - arg.size)
135
+ "#{arg}#{padding}"
136
+ end
137
+ when 'hash'
138
+ size = type.sub.to_i
139
+ raise EncodingError, "too long: #{arg}" unless size > 0 && size <= 32
140
+
141
+ if arg.is_a?(Integer)
142
+ Utils.zpad_int(arg)
143
+ elsif arg.size == size
144
+ Utils.zpad arg, 32
145
+ elsif arg.size == size * 2
146
+ Utils.zpad_hex arg
147
+ else
148
+ raise EncodingError, "Could not parse hash: #{arg}"
149
+ end
150
+ when 'address'
151
+ if arg.is_a?(Integer)
152
+ Utils.zpad_int arg
153
+ elsif arg.size == 20
154
+ Utils.zpad arg, 32
155
+ elsif arg.size == 40
156
+ Utils.zpad_hex arg
157
+ elsif arg.size == 42 && arg[0,2] == '0x'
158
+ Utils.zpad_hex arg[2..-1]
159
+ else
160
+ raise EncodingError, "Could not parse address: #{arg}"
161
+ end
162
+ else
163
+ raise EncodingError, "Unhandled type: #{type.base} #{type.sub}"
164
+ end
165
+ end
166
+
167
+ ##
168
+ # Decodes multiple arguments using the head/tail mechanism.
169
+ #
170
+ def decode_abi(types, data)
171
+ parsed_types = types.map {|t| Type.parse(t) }
172
+
173
+ outputs = [nil] * types.size
174
+ start_positions = [nil] * types.size + [data.size]
175
+
176
+ # TODO: refactor, a reverse iteration will be better
177
+ pos = 0
178
+ parsed_types.each_with_index do |t, i|
179
+ # If a type is static, grab the data directly, otherwise record its
180
+ # start position
181
+ if t.dynamic?
182
+ start_positions[i] = Utils.big_endian_to_int(data[pos, 32])
183
+
184
+ j = i - 1
185
+ while j >= 0 && start_positions[j].nil?
186
+ start_positions[j] = start_positions[i]
187
+ j -= 1
188
+ end
189
+
190
+ pos += 32
191
+ else
192
+ outputs[i] = data[pos, t.size]
193
+ pos += t.size
194
+ end
195
+ end
196
+
197
+ # We add a start position equal to the length of the entire data for
198
+ # convenience.
199
+ j = types.size - 1
200
+ while j >= 0 && start_positions[j].nil?
201
+ start_positions[j] = start_positions[types.size]
202
+ j -= 1
203
+ end
204
+
205
+ raise DecodingError, "Not enough data for head" unless pos <= data.size
206
+
207
+ parsed_types.each_with_index do |t, i|
208
+ if t.dynamic?
209
+ offset, next_offset = start_positions[i, 2]
210
+ outputs[i] = data[offset...next_offset]
211
+ end
212
+ end
213
+
214
+ parsed_types.zip(outputs).map {|(type, out)| decode_type(type, out) }
215
+ end
216
+ alias :decode :decode_abi
217
+
218
+ def decode_type(type, arg)
219
+ if %w(string bytes).include?(type.base) && type.sub.empty?
220
+ l = Utils.big_endian_to_int arg[0,32]
221
+ data = arg[32..-1]
222
+
223
+ raise DecodingError, "Wrong data size for string/bytes object" unless data.size == Utils.ceil32(l)
224
+
225
+ data[0, l]
226
+ elsif type.dynamic?
227
+ l = Utils.big_endian_to_int arg[0,32]
228
+ subtype = type.subtype
229
+
230
+ if subtype.dynamic?
231
+ raise DecodingError, "Not enough data for head" unless arg.size >= 32 + 32*l
232
+
233
+ start_positions = (1..l).map {|i| Utils.big_endian_to_int arg[32*i, 32] }
234
+ start_positions.push arg.size
235
+
236
+ outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] }
237
+
238
+ outputs.map {|out| decode_type(subtype, out) }
239
+ else
240
+ (0...l).map {|i| decode_type(subtype, arg[32 + subtype.size*i, subtype.size]) }
241
+ end
242
+ elsif !type.dims.empty? # static-sized arrays
243
+ l = type.dims.last[0]
244
+ subtype = type.subtype
245
+
246
+ (0...l).map {|i| decode_type(subtype, arg[subtype.size*i, subtype.size]) }
247
+ else
248
+ decode_primitive_type type, arg
249
+ end
250
+ end
251
+
252
+ def decode_primitive_type(type, data)
253
+ case type.base
254
+ when 'address'
255
+ Utils.encode_hex data[12..-1]
256
+ when 'string', 'bytes'
257
+ if type.sub.empty? # dynamic
258
+ size = Utils.big_endian_to_int data[0,32]
259
+ data[32..-1][0,size]
260
+ else # fixed
261
+ data[0, type.sub.to_i]
262
+ end
263
+ when 'hash'
264
+ data[(32 - type.sub.to_i), type.sub.to_i]
265
+ when 'uint'
266
+ Utils.big_endian_to_int data
267
+ when 'int'
268
+ u = Utils.big_endian_to_int data
269
+ u >= 2**(type.sub.to_i-1) ? (u - 2**type.sub.to_i) : u
270
+ when 'ureal', 'ufixed'
271
+ high, low = type.sub.split('x').map(&:to_i)
272
+ Utils.big_endian_to_int(data) * 1.0 / 2**low
273
+ when 'real', 'fixed'
274
+ high, low = type.sub.split('x').map(&:to_i)
275
+ u = Utils.big_endian_to_int data
276
+ i = u >= 2**(high+low-1) ? (u - 2**(high+low)) : u
277
+ i * 1.0 / 2**low
278
+ when 'bool'
279
+ data[-1] == BYTE_ONE
280
+ else
281
+ raise DecodingError, "Unknown primitive type: #{type.base}"
282
+ end
283
+ end
284
+
285
+ private
286
+
287
+ def get_uint(n)
288
+ case n
289
+ when Integer
290
+ raise EncodingError, "Number out of range: #{n}" if n > UINT_MAX || n < UINT_MIN
291
+ n
292
+ when String
293
+ if n.size == 40
294
+ Utils.big_endian_to_int Utils.decode_hex(n)
295
+ elsif n.size <= 32
296
+ Utils.big_endian_to_int n
297
+ else
298
+ raise EncodingError, "String too long: #{n}"
299
+ end
300
+ when true
301
+ 1
302
+ when false, nil
303
+ 0
304
+ else
305
+ raise EncodingError, "Cannot decode uint: #{n}"
306
+ end
307
+ end
308
+
309
+ def get_int(n)
310
+ case n
311
+ when Integer
312
+ raise EncodingError, "Number out of range: #{n}" if n > INT_MAX || n < INT_MIN
313
+ n
314
+ when String
315
+ if n.size == 40
316
+ i = Utils.big_endian_to_int Utils.decode_hex(n)
317
+ elsif n.size <= 32
318
+ i = Utils.big_endian_to_int n
319
+ else
320
+ raise EncodingError, "String too long: #{n}"
321
+ end
322
+ i > INT_MAX ? (i-TT256) : i
323
+ when true
324
+ 1
325
+ when false, nil
326
+ 0
327
+ else
328
+ raise EncodingError, "Cannot decode int: #{n}"
329
+ end
330
+ end
331
+
332
+ end
333
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ module ABI
5
+ module Constant
6
+ BYTE_EMPTY = "".freeze
7
+ BYTE_ZERO = "\x00".freeze
8
+ BYTE_ONE = "\x01".freeze
9
+
10
+ TT32 = 2**32
11
+ TT256 = 2**256
12
+ TT64M1 = 2**64 - 1
13
+
14
+ UINT_MAX = 2**256 - 1
15
+ UINT_MIN = 0
16
+ INT_MAX = 2**255 - 1
17
+ INT_MIN = -2**255
18
+
19
+ HASH_ZERO = ("\x00"*32).freeze
20
+
21
+ PUBKEY_ZERO = ("\x00"*32).freeze
22
+ PRIVKEY_ZERO = ("\x00"*32).freeze
23
+ PRIVKEY_ZERO_HEX = ('0'*64).freeze
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,184 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ require 'json'
4
+
5
+ module Ethereum
6
+ module ABI
7
+ class ContractTranslator
8
+
9
+ def initialize(contract_interface)
10
+ if contract_interface.instance_of?(String)
11
+ contract_interface = JSON.parse contract_interface
12
+ end
13
+
14
+ @contract = {
15
+ constructor_data: nil,
16
+ function_data: {},
17
+ event_data: {}
18
+ }
19
+
20
+ contract_interface.each do |desc|
21
+ encode_types = desc['inputs'].map {|e| e['type'] }
22
+ signature = desc['inputs'].map {|e| [e['type'], e['name']] }
23
+
24
+ # type can be omitted, defaulting to function
25
+ type = desc['type'] || 'function'
26
+ case type
27
+ when 'function'
28
+ name = basename desc['name']
29
+ decode_types = desc['outputs'].map {|e| e['type'] }
30
+ @contract[:function_data][name] = {
31
+ prefix: method_id(name, encode_types),
32
+ encode_types: encode_types,
33
+ decode_types: decode_types,
34
+ is_constant: desc.fetch('constant', false),
35
+ signature: signature
36
+ }
37
+ when 'event'
38
+ name = basename desc['name']
39
+ indexed = desc['inputs'].map {|e| e['indexed'] }
40
+ names = desc['inputs'].map {|e| e['name'] }
41
+ @contract[:event_data][event_id(name, encode_types)] = {
42
+ types: encode_types,
43
+ name: name,
44
+ names: names,
45
+ indexed: indexed,
46
+ anonymous: desc.fetch('anonymous', false)
47
+ }
48
+ when 'constructor'
49
+ raise ValueError, "Only one constructor is supported." if @contract[:constructor_data]
50
+ @contract[:constructor_data] = {
51
+ encode_types: encode_types,
52
+ signature: signature
53
+ }
54
+ else
55
+ raise ValueError, "Unknown interface type: #{type}"
56
+ end
57
+ end
58
+ end
59
+
60
+ ##
61
+ # Return the encoded function call.
62
+ #
63
+ # @param name [String] One of the existing functions described in the
64
+ # contract interface.
65
+ # @param args [Array[Object]] The function arguments that will be encoded
66
+ # and used in the contract execution in the vm.
67
+ #
68
+ # @return [String] The encoded function name and arguments so that it can
69
+ # be used with the evm to execute a function call, the binary string
70
+ # follows the Ethereum Contract ABI.
71
+ #
72
+ def encode(name, args)
73
+ raise ValueError, "Unknown function #{name}" unless function_data.include?(name)
74
+
75
+ desc = function_data[name]
76
+ func_id = Utils.zpad(Utils.encode_int(desc[:prefix]), 4)
77
+ calldata = ABI.encode_abi desc[:encode_types], args
78
+
79
+ "#{func_id}#{calldata}"
80
+ end
81
+
82
+ ##
83
+ # Return the encoded constructor call.
84
+ #
85
+ def encode_constructor_arguments(args)
86
+ raise ValueError, "The contract interface didn't have a constructor" unless constructor_data
87
+
88
+ ABI.encode_abi constructor_data[:encode_types], args
89
+ end
90
+
91
+ def decode(name, data)
92
+ desc = function_data[name]
93
+ ABI.decode_abi desc[:decode_types], data
94
+ end
95
+
96
+ def constructor_data
97
+ @contract[:constructor_data]
98
+ end
99
+
100
+ def function_data
101
+ @contract[:function_data]
102
+ end
103
+
104
+ def event_data
105
+ @contract[:event_data]
106
+ end
107
+
108
+ def function(name)
109
+ function_data[name]
110
+ end
111
+
112
+ def event(name, encode_types)
113
+ event_data[event_id(name, encode_types)]
114
+ end
115
+
116
+ def listen(log, noprint: false)
117
+ return if log.topics.size == 0 || !event_data.has_key?(log.topics[0])
118
+
119
+ data = event_data[log.topics[0]]
120
+ types = data[:types]
121
+ name = data[:name]
122
+ names = data[:names]
123
+ indexed = data[:indexed]
124
+ indexed_types = types.zip(indexed).select {|(t, i)| i.true? }.map(&:first)
125
+ unindexed_types = types.zip(indexed).select {|(t, i)| i.false? }.map(&:first)
126
+
127
+ deserialized_args = ABI.decode_abi unindexed_types, log.data
128
+
129
+ o = {}
130
+ c1, c2 = 0, 0
131
+ names.each_with_index do |n, i|
132
+ if indexed[i].true?
133
+ topic_bytes = Utils.zpad_int log.topics[c1+1]
134
+ o[n] = ABI.decode_primitive_type ABI::Type.parse(indexed_types[c1]), topic_bytes
135
+ c1 += 1
136
+ else
137
+ o[n] = deserialized_args[c2]
138
+ c2 += 1
139
+ end
140
+ end
141
+
142
+ o['_event_type'] = name
143
+ p o unless noprint
144
+
145
+ o
146
+ end
147
+
148
+ def method_id(name, encode_types)
149
+ Utils.big_endian_to_int Utils.keccak256(get_sig(name, encode_types))[0,4]
150
+ end
151
+
152
+ def event_id(name, encode_types)
153
+ Utils.big_endian_to_int Utils.keccak256(get_sig(name, encode_types))
154
+ end
155
+
156
+ private
157
+
158
+ def logger
159
+ @logger ||= Logger.new 'eth.abi.contract_translator'
160
+ end
161
+
162
+ def get_sig(name, encode_types)
163
+ "#{name}(#{encode_types.map {|x| canonical_name(x) }.join(',')})"
164
+ end
165
+
166
+ def canonical_name(x)
167
+ case x
168
+ when /\A(uint|int)(\[.*\])?\z/
169
+ "#{$1}256#{$2}"
170
+ when /\A(real|ureal|fixed|ufixed)(\[.*\])?\z/
171
+ "#{$1}128x128#{$2}"
172
+ else
173
+ x
174
+ end
175
+ end
176
+
177
+ def basename(n)
178
+ i = n.index '('
179
+ i ? n[0,i] : n
180
+ end
181
+
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,117 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ module ABI
5
+ class Type
6
+
7
+ class ParseError < StandardError; end
8
+
9
+ class <<self
10
+ ##
11
+ # Crazy regexp to seperate out base type component (eg. uint), size (eg.
12
+ # 256, 128x128, nil), array component (eg. [], [45], nil)
13
+ #
14
+ def parse(type)
15
+ _, base, sub, dimension = /([a-z]*)([0-9]*x?[0-9]*)((\[[0-9]*\])*)/.match(type).to_a
16
+
17
+ dims = dimension.scan(/\[[0-9]*\]/)
18
+ raise ParseError, "Unknown characters found in array declaration" if dims.join != dimension
19
+
20
+ case base
21
+ when 'string'
22
+ raise ParseError, "String type must have no suffix or numerical suffix" unless sub.empty?
23
+ when 'bytes'
24
+ raise ParseError, "Maximum 32 bytes for fixed-length string or bytes" unless sub.empty? || sub.to_i <= 32
25
+ when 'uint', 'int'
26
+ raise ParseError, "Integer type must have numerical suffix" unless sub =~ /\A[0-9]+\z/
27
+
28
+ size = sub.to_i
29
+ raise ParseError, "Integer size out of bounds" unless size >= 8 && size <= 256
30
+ raise ParseError, "Integer size must be multiple of 8" unless size % 8 == 0
31
+ when 'ureal', 'real', 'fixed', 'ufixed'
32
+ raise ParseError, "Real type must have suffix of form <high>x<low>, e.g. 128x128" unless sub =~ /\A[0-9]+x[0-9]+\z/
33
+
34
+ high, low = sub.split('x').map(&:to_i)
35
+ total = high + low
36
+
37
+ raise ParseError, "Real size out of bounds (max 32 bytes)" unless total >= 8 && total <= 256
38
+ raise ParseError, "Real high/low sizes must be multiples of 8" unless high % 8 == 0 && low % 8 == 0
39
+ when 'hash'
40
+ raise ParseError, "Hash type must have numerical suffix" unless sub =~ /\A[0-9]+\z/
41
+ when 'address'
42
+ raise ParseError, "Address cannot have suffix" unless sub.empty?
43
+ when 'bool'
44
+ raise ParseError, "Bool cannot have suffix" unless sub.empty?
45
+ else
46
+ raise ParseError, "Unrecognized type base: #{base}"
47
+ end
48
+
49
+ new(base, sub, dims.map {|x| x[1...-1].to_i })
50
+ end
51
+
52
+ def size_type
53
+ @size_type ||= new('uint', 256, [])
54
+ end
55
+ end
56
+
57
+ attr :base, :sub, :dims
58
+
59
+ ##
60
+ # @param base [String] base name of type, e.g. uint for uint256[4]
61
+ # @param sub [String] subscript of type, e.g. 256 for uint256[4]
62
+ # @param dims [Array[Integer]] dimensions of array type, e.g. [1,2,0]
63
+ # for uint256[1][2][], [] for non-array type
64
+ #
65
+ def initialize(base, sub, dims)
66
+ @base = base
67
+ @sub = sub
68
+ @dims = dims
69
+ end
70
+
71
+ def ==(another_type)
72
+ base == another_type.base &&
73
+ sub == another_type.sub &&
74
+ dims == another_type.dims
75
+ end
76
+
77
+ ##
78
+ # Get the static size of a type, or nil if dynamic.
79
+ #
80
+ # @return [Integer, NilClass] size of static type, or nil for dynamic
81
+ # type
82
+ #
83
+ def size
84
+ @size ||= if dims.empty?
85
+ if %w(string bytes).include?(base) && sub.empty?
86
+ nil
87
+ else
88
+ 32
89
+ end
90
+ else
91
+ if dims.last == 0 # 0 for dynamic array []
92
+ nil
93
+ else
94
+ subtype.dynamic? ? nil : dims.last * subtype.size
95
+ end
96
+ end
97
+ end
98
+
99
+ def dynamic?
100
+ size.nil?
101
+ end
102
+
103
+ ##
104
+ # Type with one dimension lesser.
105
+ #
106
+ # @example
107
+ # Type.parse("uint256[2][]").subtype # => Type.new('uint', 256, [2])
108
+ #
109
+ # @return [Ethereum::ABI::Type]
110
+ #
111
+ def subtype
112
+ @subtype ||= self.class.new(base, sub, dims[0...-1])
113
+ end
114
+
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,5 @@
1
+ module Ethereum
2
+ module ABI
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ethereum-abi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Zhang.Ya.Ning
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ethereum-base
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: block_logger
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: rlp
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.7.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.7.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 5.8.3
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 5.8.3
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.8.7.6
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.8.7.6
97
+ description: Ethereum ABI implementation in ruby.
98
+ email:
99
+ - zhangyaning1985@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - LICENSE
105
+ - README.md
106
+ - lib/ethereum/abi.rb
107
+ - lib/ethereum/abi/constant.rb
108
+ - lib/ethereum/abi/contract_translator.rb
109
+ - lib/ethereum/abi/type.rb
110
+ - lib/ethereum/abi/version.rb
111
+ homepage: https://github.com/u2/ethereum-abi
112
+ licenses:
113
+ - MIT
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.4.5
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: Ethereum ABI, ruby version.
135
+ test_files: []
136
+ has_rdoc: